xen-pciback: limit guest control of command register
[cascardo/linux.git] / drivers / xen / xen-pciback / conf_space_header.c
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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
8
9 #include <linux/kernel.h>
10 #include <linux/pci.h>
11 #include "pciback.h"
12 #include "conf_space.h"
13
14 struct pci_cmd_info {
15         u16 val;
16 };
17
18 struct pci_bar_info {
19         u32 val;
20         u32 len_val;
21         int which;
22 };
23
24 #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO))
25 #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER)
26
27 /* Bits guests are allowed to control in permissive mode. */
28 #define PCI_COMMAND_GUEST (PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| \
29                            PCI_COMMAND_INVALIDATE|PCI_COMMAND_VGA_PALETTE| \
30                            PCI_COMMAND_WAIT|PCI_COMMAND_FAST_BACK)
31
32 static void *command_init(struct pci_dev *dev, int offset)
33 {
34         struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL);
35         int err;
36
37         if (!cmd)
38                 return ERR_PTR(-ENOMEM);
39
40         err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val);
41         if (err) {
42                 kfree(cmd);
43                 return ERR_PTR(err);
44         }
45
46         return cmd;
47 }
48
49 static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data)
50 {
51         int ret = pci_read_config_word(dev, offset, value);
52         const struct pci_cmd_info *cmd = data;
53
54         *value &= PCI_COMMAND_GUEST;
55         *value |= cmd->val & ~PCI_COMMAND_GUEST;
56
57         return ret;
58 }
59
60 static int command_write(struct pci_dev *dev, int offset, u16 value, void *data)
61 {
62         struct xen_pcibk_dev_data *dev_data;
63         int err;
64         u16 val;
65         struct pci_cmd_info *cmd = data;
66
67         dev_data = pci_get_drvdata(dev);
68         if (!pci_is_enabled(dev) && is_enable_cmd(value)) {
69                 if (unlikely(verbose_request))
70                         printk(KERN_DEBUG DRV_NAME ": %s: enable\n",
71                                pci_name(dev));
72                 err = pci_enable_device(dev);
73                 if (err)
74                         return err;
75                 if (dev_data)
76                         dev_data->enable_intx = 1;
77         } else if (pci_is_enabled(dev) && !is_enable_cmd(value)) {
78                 if (unlikely(verbose_request))
79                         printk(KERN_DEBUG DRV_NAME ": %s: disable\n",
80                                pci_name(dev));
81                 pci_disable_device(dev);
82                 if (dev_data)
83                         dev_data->enable_intx = 0;
84         }
85
86         if (!dev->is_busmaster && is_master_cmd(value)) {
87                 if (unlikely(verbose_request))
88                         printk(KERN_DEBUG DRV_NAME ": %s: set bus master\n",
89                                pci_name(dev));
90                 pci_set_master(dev);
91         }
92
93         if (value & PCI_COMMAND_INVALIDATE) {
94                 if (unlikely(verbose_request))
95                         printk(KERN_DEBUG
96                                DRV_NAME ": %s: enable memory-write-invalidate\n",
97                                pci_name(dev));
98                 err = pci_set_mwi(dev);
99                 if (err) {
100                         pr_warn("%s: cannot enable memory-write-invalidate (%d)\n",
101                                 pci_name(dev), err);
102                         value &= ~PCI_COMMAND_INVALIDATE;
103                 }
104         }
105
106         cmd->val = value;
107
108         if (!permissive && (!dev_data || !dev_data->permissive))
109                 return 0;
110
111         /* Only allow the guest to control certain bits. */
112         err = pci_read_config_word(dev, offset, &val);
113         if (err || val == value)
114                 return err;
115
116         value &= PCI_COMMAND_GUEST;
117         value |= val & ~PCI_COMMAND_GUEST;
118
119         return pci_write_config_word(dev, offset, value);
120 }
121
122 static int rom_write(struct pci_dev *dev, int offset, u32 value, void *data)
123 {
124         struct pci_bar_info *bar = data;
125
126         if (unlikely(!bar)) {
127                 pr_warn(DRV_NAME ": driver data not found for %s\n",
128                        pci_name(dev));
129                 return XEN_PCI_ERR_op_failed;
130         }
131
132         /* A write to obtain the length must happen as a 32-bit write.
133          * This does not (yet) support writing individual bytes
134          */
135         if (value == ~PCI_ROM_ADDRESS_ENABLE)
136                 bar->which = 1;
137         else {
138                 u32 tmpval;
139                 pci_read_config_dword(dev, offset, &tmpval);
140                 if (tmpval != bar->val && value == bar->val) {
141                         /* Allow restoration of bar value. */
142                         pci_write_config_dword(dev, offset, bar->val);
143                 }
144                 bar->which = 0;
145         }
146
147         /* Do we need to support enabling/disabling the rom address here? */
148
149         return 0;
150 }
151
152 /* For the BARs, only allow writes which write ~0 or
153  * the correct resource information
154  * (Needed for when the driver probes the resource usage)
155  */
156 static int bar_write(struct pci_dev *dev, int offset, u32 value, void *data)
157 {
158         struct pci_bar_info *bar = data;
159
160         if (unlikely(!bar)) {
161                 pr_warn(DRV_NAME ": driver data not found for %s\n",
162                        pci_name(dev));
163                 return XEN_PCI_ERR_op_failed;
164         }
165
166         /* A write to obtain the length must happen as a 32-bit write.
167          * This does not (yet) support writing individual bytes
168          */
169         if (value == ~0)
170                 bar->which = 1;
171         else {
172                 u32 tmpval;
173                 pci_read_config_dword(dev, offset, &tmpval);
174                 if (tmpval != bar->val && value == bar->val) {
175                         /* Allow restoration of bar value. */
176                         pci_write_config_dword(dev, offset, bar->val);
177                 }
178                 bar->which = 0;
179         }
180
181         return 0;
182 }
183
184 static int bar_read(struct pci_dev *dev, int offset, u32 * value, void *data)
185 {
186         struct pci_bar_info *bar = data;
187
188         if (unlikely(!bar)) {
189                 pr_warn(DRV_NAME ": driver data not found for %s\n",
190                        pci_name(dev));
191                 return XEN_PCI_ERR_op_failed;
192         }
193
194         *value = bar->which ? bar->len_val : bar->val;
195
196         return 0;
197 }
198
199 static inline void read_dev_bar(struct pci_dev *dev,
200                                 struct pci_bar_info *bar_info, int offset,
201                                 u32 len_mask)
202 {
203         int     pos;
204         struct resource *res = dev->resource;
205
206         if (offset == PCI_ROM_ADDRESS || offset == PCI_ROM_ADDRESS1)
207                 pos = PCI_ROM_RESOURCE;
208         else {
209                 pos = (offset - PCI_BASE_ADDRESS_0) / 4;
210                 if (pos && ((res[pos - 1].flags & (PCI_BASE_ADDRESS_SPACE |
211                                 PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
212                            (PCI_BASE_ADDRESS_SPACE_MEMORY |
213                                 PCI_BASE_ADDRESS_MEM_TYPE_64))) {
214                         bar_info->val = res[pos - 1].start >> 32;
215                         bar_info->len_val = res[pos - 1].end >> 32;
216                         return;
217                 }
218         }
219
220         bar_info->val = res[pos].start |
221                         (res[pos].flags & PCI_REGION_FLAG_MASK);
222         bar_info->len_val = resource_size(&res[pos]);
223 }
224
225 static void *bar_init(struct pci_dev *dev, int offset)
226 {
227         struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
228
229         if (!bar)
230                 return ERR_PTR(-ENOMEM);
231
232         read_dev_bar(dev, bar, offset, ~0);
233         bar->which = 0;
234
235         return bar;
236 }
237
238 static void *rom_init(struct pci_dev *dev, int offset)
239 {
240         struct pci_bar_info *bar = kmalloc(sizeof(*bar), GFP_KERNEL);
241
242         if (!bar)
243                 return ERR_PTR(-ENOMEM);
244
245         read_dev_bar(dev, bar, offset, ~PCI_ROM_ADDRESS_ENABLE);
246         bar->which = 0;
247
248         return bar;
249 }
250
251 static void bar_reset(struct pci_dev *dev, int offset, void *data)
252 {
253         struct pci_bar_info *bar = data;
254
255         bar->which = 0;
256 }
257
258 static void bar_release(struct pci_dev *dev, int offset, void *data)
259 {
260         kfree(data);
261 }
262
263 static int xen_pcibk_read_vendor(struct pci_dev *dev, int offset,
264                                u16 *value, void *data)
265 {
266         *value = dev->vendor;
267
268         return 0;
269 }
270
271 static int xen_pcibk_read_device(struct pci_dev *dev, int offset,
272                                u16 *value, void *data)
273 {
274         *value = dev->device;
275
276         return 0;
277 }
278
279 static int interrupt_read(struct pci_dev *dev, int offset, u8 * value,
280                           void *data)
281 {
282         *value = (u8) dev->irq;
283
284         return 0;
285 }
286
287 static int bist_write(struct pci_dev *dev, int offset, u8 value, void *data)
288 {
289         u8 cur_value;
290         int err;
291
292         err = pci_read_config_byte(dev, offset, &cur_value);
293         if (err)
294                 goto out;
295
296         if ((cur_value & ~PCI_BIST_START) == (value & ~PCI_BIST_START)
297             || value == PCI_BIST_START)
298                 err = pci_write_config_byte(dev, offset, value);
299
300 out:
301         return err;
302 }
303
304 static const struct config_field header_common[] = {
305         {
306          .offset    = PCI_VENDOR_ID,
307          .size      = 2,
308          .u.w.read  = xen_pcibk_read_vendor,
309         },
310         {
311          .offset    = PCI_DEVICE_ID,
312          .size      = 2,
313          .u.w.read  = xen_pcibk_read_device,
314         },
315         {
316          .offset    = PCI_COMMAND,
317          .size      = 2,
318          .init      = command_init,
319          .release   = bar_release,
320          .u.w.read  = command_read,
321          .u.w.write = command_write,
322         },
323         {
324          .offset    = PCI_INTERRUPT_LINE,
325          .size      = 1,
326          .u.b.read  = interrupt_read,
327         },
328         {
329          .offset    = PCI_INTERRUPT_PIN,
330          .size      = 1,
331          .u.b.read  = xen_pcibk_read_config_byte,
332         },
333         {
334          /* Any side effects of letting driver domain control cache line? */
335          .offset    = PCI_CACHE_LINE_SIZE,
336          .size      = 1,
337          .u.b.read  = xen_pcibk_read_config_byte,
338          .u.b.write = xen_pcibk_write_config_byte,
339         },
340         {
341          .offset    = PCI_LATENCY_TIMER,
342          .size      = 1,
343          .u.b.read  = xen_pcibk_read_config_byte,
344         },
345         {
346          .offset    = PCI_BIST,
347          .size      = 1,
348          .u.b.read  = xen_pcibk_read_config_byte,
349          .u.b.write = bist_write,
350         },
351         {}
352 };
353
354 #define CFG_FIELD_BAR(reg_offset)                       \
355         {                                               \
356         .offset     = reg_offset,                       \
357         .size       = 4,                                \
358         .init       = bar_init,                         \
359         .reset      = bar_reset,                        \
360         .release    = bar_release,                      \
361         .u.dw.read  = bar_read,                         \
362         .u.dw.write = bar_write,                        \
363         }
364
365 #define CFG_FIELD_ROM(reg_offset)                       \
366         {                                               \
367         .offset     = reg_offset,                       \
368         .size       = 4,                                \
369         .init       = rom_init,                         \
370         .reset      = bar_reset,                        \
371         .release    = bar_release,                      \
372         .u.dw.read  = bar_read,                         \
373         .u.dw.write = rom_write,                        \
374         }
375
376 static const struct config_field header_0[] = {
377         CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
378         CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
379         CFG_FIELD_BAR(PCI_BASE_ADDRESS_2),
380         CFG_FIELD_BAR(PCI_BASE_ADDRESS_3),
381         CFG_FIELD_BAR(PCI_BASE_ADDRESS_4),
382         CFG_FIELD_BAR(PCI_BASE_ADDRESS_5),
383         CFG_FIELD_ROM(PCI_ROM_ADDRESS),
384         {}
385 };
386
387 static const struct config_field header_1[] = {
388         CFG_FIELD_BAR(PCI_BASE_ADDRESS_0),
389         CFG_FIELD_BAR(PCI_BASE_ADDRESS_1),
390         CFG_FIELD_ROM(PCI_ROM_ADDRESS1),
391         {}
392 };
393
394 int xen_pcibk_config_header_add_fields(struct pci_dev *dev)
395 {
396         int err;
397
398         err = xen_pcibk_config_add_fields(dev, header_common);
399         if (err)
400                 goto out;
401
402         switch (dev->hdr_type) {
403         case PCI_HEADER_TYPE_NORMAL:
404                 err = xen_pcibk_config_add_fields(dev, header_0);
405                 break;
406
407         case PCI_HEADER_TYPE_BRIDGE:
408                 err = xen_pcibk_config_add_fields(dev, header_1);
409                 break;
410
411         default:
412                 err = -EINVAL;
413                 pr_err("%s: Unsupported header type %d!\n",
414                        pci_name(dev), dev->hdr_type);
415                 break;
416         }
417
418 out:
419         return err;
420 }