diff options
| -rw-r--r-- | drivers/xen/xen-pciback/conf_space.c | 2 | ||||
| -rw-r--r-- | drivers/xen/xen-pciback/conf_space.h | 2 | ||||
| -rw-r--r-- | drivers/xen/xen-pciback/conf_space_header.c | 61 |
3 files changed, 51 insertions, 14 deletions
diff --git a/drivers/xen/xen-pciback/conf_space.c b/drivers/xen/xen-pciback/conf_space.c index 46ae0f9f02ad..75fe3d466515 100644 --- a/drivers/xen/xen-pciback/conf_space.c +++ b/drivers/xen/xen-pciback/conf_space.c | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include "conf_space.h" | 16 | #include "conf_space.h" |
| 17 | #include "conf_space_quirks.h" | 17 | #include "conf_space_quirks.h" |
| 18 | 18 | ||
| 19 | static bool permissive; | 19 | bool permissive; |
| 20 | module_param(permissive, bool, 0644); | 20 | module_param(permissive, bool, 0644); |
| 21 | 21 | ||
| 22 | /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, | 22 | /* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, |
diff --git a/drivers/xen/xen-pciback/conf_space.h b/drivers/xen/xen-pciback/conf_space.h index e56c934ad137..2e1d73d1d5d0 100644 --- a/drivers/xen/xen-pciback/conf_space.h +++ b/drivers/xen/xen-pciback/conf_space.h | |||
| @@ -64,6 +64,8 @@ struct config_field_entry { | |||
| 64 | void *data; | 64 | void *data; |
| 65 | }; | 65 | }; |
| 66 | 66 | ||
| 67 | extern bool permissive; | ||
| 68 | |||
| 67 | #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) | 69 | #define OFFSET(cfg_entry) ((cfg_entry)->base_offset+(cfg_entry)->field->offset) |
| 68 | 70 | ||
| 69 | /* Add fields to a device - the add_fields macro expects to get a pointer to | 71 | /* Add fields to a device - the add_fields macro expects to get a pointer to |
diff --git a/drivers/xen/xen-pciback/conf_space_header.c b/drivers/xen/xen-pciback/conf_space_header.c index c5ee82587e8c..2d7369391472 100644 --- a/drivers/xen/xen-pciback/conf_space_header.c +++ b/drivers/xen/xen-pciback/conf_space_header.c | |||
| @@ -11,6 +11,10 @@ | |||
| 11 | #include "pciback.h" | 11 | #include "pciback.h" |
| 12 | #include "conf_space.h" | 12 | #include "conf_space.h" |
| 13 | 13 | ||
| 14 | struct pci_cmd_info { | ||
| 15 | u16 val; | ||
| 16 | }; | ||
| 17 | |||
| 14 | struct pci_bar_info { | 18 | struct pci_bar_info { |
| 15 | u32 val; | 19 | u32 val; |
| 16 | u32 len_val; | 20 | u32 len_val; |
| @@ -20,22 +24,36 @@ struct pci_bar_info { | |||
| 20 | #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) | 24 | #define is_enable_cmd(value) ((value)&(PCI_COMMAND_MEMORY|PCI_COMMAND_IO)) |
| 21 | #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) | 25 | #define is_master_cmd(value) ((value)&PCI_COMMAND_MASTER) |
| 22 | 26 | ||
| 23 | static int command_read(struct pci_dev *dev, int offset, u16 *value, void *data) | 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) | ||
| 24 | { | 33 | { |
| 25 | int i; | 34 | struct pci_cmd_info *cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); |
| 26 | int ret; | 35 | int err; |
| 27 | 36 | ||
| 28 | ret = xen_pcibk_read_config_word(dev, offset, value, data); | 37 | if (!cmd) |
| 29 | if (!pci_is_enabled(dev)) | 38 | return ERR_PTR(-ENOMEM); |
| 30 | return ret; | 39 | |
| 31 | 40 | err = pci_read_config_word(dev, PCI_COMMAND, &cmd->val); | |
| 32 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 41 | if (err) { |
| 33 | if (dev->resource[i].flags & IORESOURCE_IO) | 42 | kfree(cmd); |
| 34 | *value |= PCI_COMMAND_IO; | 43 | return ERR_PTR(err); |
| 35 | if (dev->resource[i].flags & IORESOURCE_MEM) | ||
| 36 | *value |= PCI_COMMAND_MEMORY; | ||
| 37 | } | 44 | } |
| 38 | 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 | |||
| 39 | return ret; | 57 | return ret; |
| 40 | } | 58 | } |
| 41 | 59 | ||
| @@ -43,6 +61,8 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) | |||
| 43 | { | 61 | { |
| 44 | struct xen_pcibk_dev_data *dev_data; | 62 | struct xen_pcibk_dev_data *dev_data; |
| 45 | int err; | 63 | int err; |
| 64 | u16 val; | ||
| 65 | struct pci_cmd_info *cmd = data; | ||
| 46 | 66 | ||
| 47 | dev_data = pci_get_drvdata(dev); | 67 | dev_data = pci_get_drvdata(dev); |
| 48 | if (!pci_is_enabled(dev) && is_enable_cmd(value)) { | 68 | if (!pci_is_enabled(dev) && is_enable_cmd(value)) { |
| @@ -83,6 +103,19 @@ static int command_write(struct pci_dev *dev, int offset, u16 value, void *data) | |||
| 83 | } | 103 | } |
| 84 | } | 104 | } |
| 85 | 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 | |||
| 86 | return pci_write_config_word(dev, offset, value); | 119 | return pci_write_config_word(dev, offset, value); |
| 87 | } | 120 | } |
| 88 | 121 | ||
| @@ -282,6 +315,8 @@ static const struct config_field header_common[] = { | |||
| 282 | { | 315 | { |
| 283 | .offset = PCI_COMMAND, | 316 | .offset = PCI_COMMAND, |
| 284 | .size = 2, | 317 | .size = 2, |
| 318 | .init = command_init, | ||
| 319 | .release = bar_release, | ||
| 285 | .u.w.read = command_read, | 320 | .u.w.read = command_read, |
| 286 | .u.w.write = command_write, | 321 | .u.w.write = command_write, |
| 287 | }, | 322 | }, |
