aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/xen/xen-pciback/conf_space_header.c
diff options
context:
space:
mode:
authorKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2009-10-13 17:22:20 -0400
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-07-19 20:58:01 -0400
commit30edc14bf39afde24ef7db2de66c91805db80828 (patch)
tree1cf5b6f28a3ea4159a09bcef9d11be6d427e3558 /drivers/xen/xen-pciback/conf_space_header.c
parent56299378726d5f2ba8d3c8cbbd13cb280ba45e4f (diff)
xen/pciback: xen pci backend driver.
This is the host side counterpart to the frontend driver in drivers/pci/xen-pcifront.c. The PV protocol is also implemented by frontend drivers in other OSes too, such as the BSDs. The PV protocol is rather simple. There is page shared with the guest, which has the 'struct xen_pci_sharedinfo' embossed in it. The backend has a thread that is kicked every-time the structure is changed and based on the operation field it performs specific tasks: XEN_PCI_OP_conf_[read|write]: Read/Write 0xCF8/0xCFC filtered data. (conf_space*.c) Based on which field is probed, we either enable/disable the PCI device, change power state, read VPD, etc. The major goal of this call is to provide a Physical IRQ (PIRQ) to the guest. The PIRQ is Xen hypervisor global IRQ value irrespective of the IRQ is tied in to the IO-APIC, or is a vector. For GSI type interrupts, the PIRQ==GSI holds. For MSI/MSI-X the PIRQ value != Linux IRQ number (thought PIRQ==vector). Please note, that with Xen, all interrupts (except those level shared ones) are injected directly to the guest - there is no host interaction. XEN_PCI_OP_[enable|disable]_msi[|x] (pciback_ops.c) Enables/disables the MSI/MSI-X capability of the device. These operations setup the MSI/MSI-X vectors for the guest and pass them to the frontend. When the device is activated, the interrupts are directly injected in the guest without involving the host. XEN_PCI_OP_aer_[detected|resume|mmio|slotreset]: In case of failure, perform the appropriate AER commands on the guest. Right now that is a cop-out - we just kill the guest. Besides implementing those commands, it can also - hide a PCI device from the host. When booting up, the user can specify xen-pciback.hide=(1:0:0)(BDF..) so that host does not try to use the device. The driver was lifted from linux-2.6.18.hg tree and fixed up so that it could compile under v3.0. Per suggestion from Jesse Barnes moved the driver to drivers/xen/xen-pciback. Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Diffstat (limited to 'drivers/xen/xen-pciback/conf_space_header.c')
-rw-r--r--drivers/xen/xen-pciback/conf_space_header.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c
new file mode 100644
index 000000000000..3ae7da137f7e
--- /dev/null
+++ b/drivers/xen/xen-pciback/conf_space_header.c
@@ -0,0 +1,318 @@
1/*
2 * PCI Backend - Handles the virtual fields in the configuration space headers.
3 *
4 * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
5 */
6
7#include <linux/kernel.h>
8#include <linux/pci.h>
9#include "pciback.h"
10#include "conf_space.h"
11
12struct pci_bar_info {
13 u32 val;
14 u32 len_val;
15 int which;
16};
17
18#define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
19#define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
20
21static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
22{
23 int err;
24
25 if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
26 if (unlikely(verbose_request))
27 printk(KERN_DEBUG "pciback: %s: enable\n",
28 pci_name(dev));
29 err = pci_enable_device(dev);
30 if (err)
31 return err;
32 } else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
33 if (unlikely(verbose_request))
34 printk(KERN_DEBUG "pciback: %s: disable\n",
35 pci_name(dev));
36 pci_disable_device(dev);
37 }
38
39 if (!dev->is_busmaster && is_master_cmd(value)) {
40 if (unlikely(verbose_request))
41 printk(KERN_DEBUG "pciback: %s: set bus master\n",
42 pci_name(dev));
43 pci_set_master(dev);
44 }
45
46 if (value & PCI_COMMAND_INVALIDATE) {
47 if (unlikely(verbose_request))
48 printk(KERN_DEBUG
49 "pciback: %s: enable memory-write-invalidate\n",
50 pci_name(dev));
51 err = pci_set_mwi(dev);
52 if (err) {
53 printk(KERN_WARNING
54 "pciback: %s: cannot enable "
55 "memory-write-invalidate (%d)\n",
56 pci_name(dev), err);
57 value &= ~PCI_COMMAND_INVALIDATE;
58 }
59 }
60
61 return pci_write_config_word(dev, offset, value);
62}
63
64static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
65{
66 struct pci_bar_info *bar = data;
67
68 if (unlikely(!bar)) {
69 printk(KERN_WARNING "pciback: driver data not found for %s\n",
70 pci_name(dev));
71 return XEN_PCI_ERR_op_failed;
72 }
73
74 /* A write to obtain the length must happen as a 32-bit write.
75 * This does not (yet) support writing individual bytes
76 */
77 if (value == ~PCI_ROM_ADDRESS_ENABLE)
78 bar->which = 1;
79 else {
80 u32 tmpval;
81 pci_read_config_dword(dev, offset, &tmpval);
82 if (tmpval != bar->val && value == bar->val) {
83 /* Allow restoration of bar value. */
84 pci_write_config_dword(dev, offset, bar->val);
85 }
86 bar->which = 0;
87 }
88
89 /* Do we need to support enabling/disabling the rom address here? */
90
91 return 0;
92}
93
94/* For the BARs, only allow writes which write ~0 or
95 * the correct resource information
96 * (Needed for when the driver probes the resource usage)
97 */
98static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
99{
100 struct pci_bar_info *bar = data;
101
102 if (unlikely(!bar)) {
103 printk(KERN_WARNING "pciback: driver data not found for %s\n",
104 pci_name(dev));
105 return XEN_PCI_ERR_op_failed;
106 }
107
108 /* A write to obtain the length must happen as a 32-bit write.
109 * This does not (yet) support writing individual bytes
110 */
111 if (value == ~0)
112 bar->which = 1;
113 else {
114 u32 tmpval;
115 pci_read_config_dword(dev, offset, &tmpval);
116 if (tmpval != bar->val && value == bar->val) {
117 /* Allow restoration of bar value. */
118 pci_write_config_dword(dev, offset, bar->val);
119 }
120 bar->which = 0;
121 }
122
123 return 0;
124}
125
126static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
127{
128 struct pci_bar_info *bar = data;
129
130 if (unlikely(!bar)) {
131 printk(KERN_WARNING "pciback: driver data not found for %s\n",
132 pci_name(dev));
133 return XEN_PCI_ERR_op_failed;
134 }
135
136 *value = bar->which ? bar->len_val : bar->val;
137
138 return 0;
139}
140
141static inline void read_dev_bar(struct pci_dev *dev,
142 struct pci_bar_info *bar_info, int offset,
143 u32 len_mask)
144{
145 pci_read_config_dword(dev, offset, &bar_info->val);
146 pci_write_config_dword(dev, offset, len_mask);
147 pci_read_config_dword(dev, offset, &bar_info->len_val);
148 pci_write_config_dword(dev, offset, bar_info->val);
149}
150
151static void *bar_init(struct pci_dev *dev, int offset)
152{
153 struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
154
155 if (!bar)
156 return ERR_PTR(-ENOMEM);
157
158 read_dev_bar(dev, bar, offset, ~0);
159 bar->which = 0;
160
161 return bar;
162}
163
164static void *rom_init(struct pci_dev *dev, int offset)
165{
166 struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
167
168 if (!bar)
169 return ERR_PTR(-ENOMEM);
170
171 read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE);
172 bar->which = 0;
173
174 return bar;
175}
176
177static void bar_reset(struct pci_dev *dev, int offset, void *data)
178{
179 struct pci_bar_info *bar = data;
180
181 bar->which = 0;
182}
183
184static void bar_release(struct pci_dev *dev, int offset, void *data)
185{
186 kfree(data);
187}
188
189static int interrupt_read(struct pci_dev *dev, int offset, u8 * value,
190 void *data)
191{
192 *value = (u8) dev->irq;
193
194 return 0;
195}
196
197static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data)
198{
199 u8 cur_value;
200 int err;
201
202 err = pci_read_config_byte(dev, offset, &cur_value);
203 if (err)
204 goto out;
205
206 if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START)
207 || value == PCI_BIST_START)
208 err = pci_write_config_byte(dev, offset, value);
209
210out:
211 return err;
212}
213
214static const struct config_field header_common[] = {
215 {
216 .offset = PCI_COMMAND,
217 .size = 2,
218 .u.w.read = pciback_read_config_word,
219 .u.w.write = command_write,
220 },
221 {
222 .offset = PCI_INTERRUPT_LINE,
223 .size = 1,
224 .u.b.read = interrupt_read,
225 },
226 {
227 .offset = PCI_INTERRUPT_PIN,
228 .size = 1,
229 .u.b.read = pciback_read_config_byte,
230 },
231 {
232 /* Any side effects of letting driver domain control cache line? */
233 .offset = PCI_CACHE_LINE_SIZE,
234 .size = 1,
235 .u.b.read = pciback_read_config_byte,
236 .u.b.write = pciback_write_config_byte,
237 },
238 {
239 .offset = PCI_LATENCY_TIMER,
240 .size = 1,
241 .u.b.read = pciback_read_config_byte,
242 },
243 {
244 .offset = PCI_BIST,
245 .size = 1,
246 .u.b.read = pciback_read_config_byte,
247 .u.b.write = bist_write,
248 },
249 {}
250};
251
252#define CFG_FIELD_BAR(reg_offset) \
253 { \
254 .offset = reg_offset, \
255 .size = 4, \
256 .init = bar_init, \
257 .reset = bar_reset, \
258 .release = bar_release, \
259 .u.dw.read = bar_read, \
260 .u.dw.write = bar_write, \
261 }
262
263#define CFG_FIELD_ROM(reg_offset) \
264 { \
265 .offset = reg_offset, \
266 .size = 4, \
267 .init = rom_init, \
268 .reset = bar_reset, \
269 .release = bar_release, \
270 .u.dw.read = bar_read, \
271 .u.dw.write = rom_write, \
272 }
273
274static const struct config_field header_0[] = {
275 CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
276 CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
277 CFG_FIELD_BAR(PCI_BASE_ADDRESS_2),
278 CFG_FIELD_BAR(PCI_BASE_ADDRESS_3),
279 CFG_FIELD_BAR(PCI_BASE_ADDRESS_4),
280 CFG_FIELD_BAR(PCI_BASE_ADDRESS_5),
281 CFG_FIELD_ROM(PCI_ROM_ADDRESS),
282 {}
283};
284
285static const struct config_field header_1[] = {
286 CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
287 CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
288 CFG_FIELD_ROM(PCI_ROM_ADDRESS1),
289 {}
290};
291
292int pciback_config_header_add_fields(struct pci_dev *dev)
293{
294 int err;
295
296 err = pciback_config_add_fields(dev, header_common);
297 if (err)
298 goto out;
299
300 switch (dev->hdr_type) {
301 case PCI_HEADER_TYPE_NORMAL:
302 err = pciback_config_add_fields(dev, header_0);
303 break;
304
305 case PCI_HEADER_TYPE_BRIDGE:
306 err = pciback_config_add_fields(dev, header_1);
307 break;
308
309 default:
310 err = -EINVAL;
311 printk(KERN_ERR "pciback: %s: Unsupported header type %d!\n",
312 pci_name(dev), dev->hdr_type);
313 break;
314 }
315
316out:
317 return err;
318}