This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / xen / pciback / conf_space.c
1 /*
2  * PCI Backend - Functions for creating a virtual configuration space for
3  *               exported PCI Devices.
4  *               It's dangerous to allow PCI Driver Domains to change their
5  *               device's resources (memory, i/o ports, interrupts). We need to
6  *               restrict changes to certain PCI Configuration registers:
7  *               BARs, INTERRUPT_PIN, most registers in the header...
8  *
9  * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/pci.h>
14 #include "pciback.h"
15 #include "conf_space.h"
16 #include "conf_space_quirks.h"
17
18 #define DEFINE_PCI_CONFIG(op,size,type)                         \
19 int pciback_##op##_config_##size                                \
20 (struct pci_dev *dev, int offset, type value, void *data)       \
21 {                                                               \
22         return pci_##op##_config_##size (dev, offset, value);   \
23 }
24
25 DEFINE_PCI_CONFIG(read, byte, u8 *)
26 DEFINE_PCI_CONFIG(read, word, u16 *)
27 DEFINE_PCI_CONFIG(read, dword, u32 *)
28
29 DEFINE_PCI_CONFIG(write, byte, u8)
30 DEFINE_PCI_CONFIG(write, word, u16)
31 DEFINE_PCI_CONFIG(write, dword, u32)
32
33 static int conf_space_read(struct pci_dev *dev,
34                            struct config_field_entry *entry, int offset,
35                            u32 * value)
36 {
37         int ret = 0;
38         struct config_field *field = entry->field;
39
40         *value = 0;
41
42         switch (field->size) {
43         case 1:
44                 if (field->u.b.read)
45                         ret = field->u.b.read(dev, offset, (u8 *) value,
46                                               entry->data);
47                 break;
48         case 2:
49                 if (field->u.w.read)
50                         ret = field->u.w.read(dev, offset, (u16 *) value,
51                                               entry->data);
52                 break;
53         case 4:
54                 if (field->u.dw.read)
55                         ret = field->u.dw.read(dev, offset, value, entry->data);
56                 break;
57         }
58         return ret;
59 }
60
61 static int conf_space_write(struct pci_dev *dev,
62                             struct config_field_entry *entry, int offset,
63                             u32 value)
64 {
65         int ret = 0;
66         struct config_field *field = entry->field;
67
68         switch (field->size) {
69         case 1:
70                 if (field->u.b.write)
71                         ret = field->u.b.write(dev, offset, (u8) value,
72                                                entry->data);
73                 break;
74         case 2:
75                 if (field->u.w.write)
76                         ret = field->u.w.write(dev, offset, (u16) value,
77                                                entry->data);
78                 break;
79         case 4:
80                 if (field->u.dw.write)
81                         ret = field->u.dw.write(dev, offset, value,
82                                                 entry->data);
83                 break;
84         }
85         return ret;
86 }
87
88 static inline u32 get_mask(int size)
89 {
90         if (size == 1)
91                 return 0xff;
92         else if (size == 2)
93                 return 0xffff;
94         else
95                 return 0xffffffff;
96 }
97
98 static inline int valid_request(int offset, int size)
99 {
100         /* Validate request (no un-aligned requests) */
101         if ((size == 1 || size == 2 || size == 4) && (offset % size) == 0)
102                 return 1;
103         return 0;
104 }
105
106 static inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask,
107                               int offset)
108 {
109         if (offset >= 0) {
110                 new_val_mask <<= (offset * 8);
111                 new_val <<= (offset * 8);
112         } else {
113                 new_val_mask >>= (offset * -8);
114                 new_val >>= (offset * -8);
115         }
116         val = (val & ~new_val_mask) | (new_val & new_val_mask);
117
118         return val;
119 }
120
121 static int pcibios_err_to_errno(int err)
122 {
123         switch (err) {
124         case PCIBIOS_SUCCESSFUL:
125                 return XEN_PCI_ERR_success;
126         case PCIBIOS_DEVICE_NOT_FOUND:
127                 return XEN_PCI_ERR_dev_not_found;
128         case PCIBIOS_BAD_REGISTER_NUMBER:
129                 return XEN_PCI_ERR_invalid_offset;
130         case PCIBIOS_FUNC_NOT_SUPPORTED:
131                 return XEN_PCI_ERR_not_implemented;
132         case PCIBIOS_SET_FAILED:
133                 return XEN_PCI_ERR_access_denied;
134         }
135         return err;
136 }
137
138 int pciback_config_read(struct pci_dev *dev, int offset, int size,
139                         u32 * ret_val)
140 {
141         int err = 0;
142         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
143         struct config_field_entry *cfg_entry;
144         struct config_field *field;
145         int req_start, req_end, field_start, field_end;
146         /* if read fails for any reason, return 0 (as if device didn't respond) */
147         u32 value = 0, tmp_val;
148
149         if (unlikely(verbose_request))
150                 printk(KERN_DEBUG "pciback: %s: read %d bytes at 0x%x\n",
151                        pci_name(dev), size, offset);
152
153         if (!valid_request(offset, size)) {
154                 err = XEN_PCI_ERR_invalid_offset;
155                 goto out;
156         }
157
158         /* Get the real value first, then modify as appropriate */
159         switch (size) {
160         case 1:
161                 err = pci_read_config_byte(dev, offset, (u8 *) & value);
162                 break;
163         case 2:
164                 err = pci_read_config_word(dev, offset, (u16 *) & value);
165                 break;
166         case 4:
167                 err = pci_read_config_dword(dev, offset, &value);
168                 break;
169         }
170
171         list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
172                 field = cfg_entry->field;
173
174                 req_start = offset;
175                 req_end = offset + size;
176                 field_start = OFFSET(cfg_entry);
177                 field_end = OFFSET(cfg_entry) + field->size;
178
179                 if ((req_start >= field_start && req_start < field_end)
180                     || (req_end > field_start && req_end <= field_end)) {
181                         err = conf_space_read(dev, cfg_entry, field_start,
182                                               &tmp_val);
183                         if (err)
184                                 goto out;
185
186                         value = merge_value(value, tmp_val,
187                                             get_mask(field->size),
188                                             field_start - req_start);
189                 }
190         }
191
192       out:
193         if (unlikely(verbose_request))
194                 printk(KERN_DEBUG "pciback: %s: read %d bytes at 0x%x = %x\n",
195                        pci_name(dev), size, offset, value);
196
197         *ret_val = value;
198         return pcibios_err_to_errno(err);
199 }
200
201 int pciback_config_write(struct pci_dev *dev, int offset, int size, u32 value)
202 {
203         int err = 0, handled = 0;
204         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
205         struct config_field_entry *cfg_entry;
206         struct config_field *field;
207         u32 tmp_val;
208         int req_start, req_end, field_start, field_end;
209
210         if (unlikely(verbose_request))
211                 printk(KERN_DEBUG
212                        "pciback: %s: write request %d bytes at 0x%x = %x\n",
213                        pci_name(dev), size, offset, value);
214
215         if (!valid_request(offset, size))
216                 return XEN_PCI_ERR_invalid_offset;
217
218         list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
219                 field = cfg_entry->field;
220
221                 req_start = offset;
222                 req_end = offset + size;
223                 field_start = OFFSET(cfg_entry);
224                 field_end = OFFSET(cfg_entry) + field->size;
225
226                 if ((req_start >= field_start && req_start < field_end)
227                     || (req_end > field_start && req_end <= field_end)) {
228                         tmp_val = 0;
229
230                         err = pciback_config_read(dev, field_start,
231                                                   field->size, &tmp_val);
232                         if (err)
233                                 break;
234
235                         tmp_val = merge_value(tmp_val, value, get_mask(size),
236                                               req_start - field_start);
237
238                         err = conf_space_write(dev, cfg_entry, field_start,
239                                                tmp_val);
240
241                         /* handled is set true here, but not every byte
242                          * may have been written! Properly detecting if
243                          * every byte is handled is unnecessary as the
244                          * flag is used to detect devices that need
245                          * special helpers to work correctly.
246                          */
247                         handled = 1;
248                 }
249         }
250
251         if (!handled && !err) {
252                 /* By default, anything not specificially handled above is
253                  * read-only. The permissive flag changes this behavior so
254                  * that anything not specifically handled above is writable.
255                  * This means that some fields may still be read-only because
256                  * they have entries in the config_field list that intercept
257                  * the write and do nothing. */
258                 if (dev_data->permissive) {
259                         switch (size) {
260                         case 1:
261                                 err = pci_write_config_byte(dev, offset,
262                                                             (u8) value);
263                                 break;
264                         case 2:
265                                 err = pci_write_config_word(dev, offset,
266                                                             (u16) value);
267                                 break;
268                         case 4:
269                                 err = pci_write_config_dword(dev, offset,
270                                                              (u32) value);
271                                 break;
272                         }
273                 } else if (!dev_data->warned_on_write) {
274                         dev_data->warned_on_write = 1;
275                         dev_warn(&dev->dev, "Driver tried to write to a "
276                                  "read-only configuration space field at offset "
277                                  "0x%x, size %d. This may be harmless, but if "
278                                  "you have problems with your device:\n"
279                                  "1) see permissive attribute in sysfs\n"
280                                  "2) report problems to the xen-devel "
281                                  "mailing list along with details of your "
282                                  "device obtained from lspci.\n", offset, size);
283                 }
284         }
285
286         return pcibios_err_to_errno(err);
287 }
288
289 void pciback_config_free_dyn_fields(struct pci_dev *dev)
290 {
291         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
292         struct config_field_entry *cfg_entry, *t;
293         struct config_field *field;
294
295         dev_dbg(&dev->dev,
296                 "free-ing dynamically allocated virtual configuration space fields\n");
297
298         list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) {
299                 field = cfg_entry->field;
300
301                 if (field->clean) {
302                         field->clean(field);
303
304                         if (cfg_entry->data)
305                                 kfree(cfg_entry->data);
306
307                         list_del(&cfg_entry->list);
308                         kfree(cfg_entry);
309                 }
310
311         }
312 }
313
314 void pciback_config_reset_dev(struct pci_dev *dev)
315 {
316         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
317         struct config_field_entry *cfg_entry;
318         struct config_field *field;
319
320         dev_dbg(&dev->dev, "resetting virtual configuration space\n");
321
322         list_for_each_entry(cfg_entry, &dev_data->config_fields, list) {
323                 field = cfg_entry->field;
324
325                 if (field->reset)
326                         field->reset(dev, OFFSET(cfg_entry), cfg_entry->data);
327         }
328 }
329
330 void pciback_config_free_dev(struct pci_dev *dev)
331 {
332         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
333         struct config_field_entry *cfg_entry, *t;
334         struct config_field *field;
335
336         dev_dbg(&dev->dev, "free-ing virtual configuration space fields\n");
337
338         list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) {
339                 list_del(&cfg_entry->list);
340
341                 field = cfg_entry->field;
342
343                 if (field->release)
344                         field->release(dev, OFFSET(cfg_entry), cfg_entry->data);
345
346                 kfree(cfg_entry);
347         }
348 }
349
350 int pciback_config_add_field_offset(struct pci_dev *dev,
351                                     struct config_field *field,
352                                     unsigned int offset)
353 {
354         int err = 0;
355         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
356         struct config_field_entry *cfg_entry;
357         void *tmp;
358
359         /* silently ignore duplicate fields */
360         if (pciback_field_is_dup(dev, field->offset))
361                 goto out;
362
363         cfg_entry = kmalloc(sizeof(*cfg_entry), GFP_KERNEL);
364         if (!cfg_entry) {
365                 err = -ENOMEM;
366                 goto out;
367         }
368
369         cfg_entry->data = NULL;
370         cfg_entry->field = field;
371         cfg_entry->base_offset = offset;
372
373         if (field->init) {
374                 tmp = field->init(dev, OFFSET(cfg_entry));
375
376                 if (IS_ERR(tmp)) {
377                         err = PTR_ERR(tmp);
378                         goto out;
379                 }
380
381                 cfg_entry->data = tmp;
382         }
383
384         dev_dbg(&dev->dev, "added config field at offset 0x%02x\n",
385                 OFFSET(cfg_entry));
386         list_add_tail(&cfg_entry->list, &dev_data->config_fields);
387
388       out:
389         if (err)
390                 kfree(cfg_entry);
391
392         return err;
393 }
394
395 /* This sets up the device's virtual configuration space to keep track of 
396  * certain registers (like the base address registers (BARs) so that we can
397  * keep the client from manipulating them directly.
398  */
399 int pciback_config_init_dev(struct pci_dev *dev)
400 {
401         int err = 0;
402         struct pciback_dev_data *dev_data = pci_get_drvdata(dev);
403
404         dev_dbg(&dev->dev, "initializing virtual configuration space\n");
405
406         INIT_LIST_HEAD(&dev_data->config_fields);
407
408         err = pciback_config_header_add_fields(dev);
409         if (err)
410                 goto out;
411
412         err = pciback_config_capability_add_fields(dev);
413         if (err)
414                 goto out;
415
416         err = pciback_config_quirks_init(dev);
417
418       out:
419         return err;
420 }
421
422 int pciback_config_init(void)
423 {
424         return pciback_config_capability_init();
425 }