18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PCI Backend - Functions for creating a virtual configuration space for 48c2ecf20Sopenharmony_ci * exported PCI Devices. 58c2ecf20Sopenharmony_ci * It's dangerous to allow PCI Driver Domains to change their 68c2ecf20Sopenharmony_ci * device's resources (memory, i/o ports, interrupts). We need to 78c2ecf20Sopenharmony_ci * restrict changes to certain PCI Configuration registers: 88c2ecf20Sopenharmony_ci * BARs, INTERRUPT_PIN, most registers in the header... 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define dev_fmt(fmt) DRV_NAME ": " fmt 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include "pciback.h" 198c2ecf20Sopenharmony_ci#include "conf_space.h" 208c2ecf20Sopenharmony_ci#include "conf_space_quirks.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cibool xen_pcibk_permissive; 238c2ecf20Sopenharmony_cimodule_param_named(permissive, xen_pcibk_permissive, bool, 0644); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* This is where xen_pcibk_read_config_byte, xen_pcibk_read_config_word, 268c2ecf20Sopenharmony_ci * xen_pcibk_write_config_word, and xen_pcibk_write_config_byte are created. */ 278c2ecf20Sopenharmony_ci#define DEFINE_PCI_CONFIG(op, size, type) \ 288c2ecf20Sopenharmony_ciint xen_pcibk_##op##_config_##size \ 298c2ecf20Sopenharmony_ci(struct pci_dev *dev, int offset, type value, void *data) \ 308c2ecf20Sopenharmony_ci{ \ 318c2ecf20Sopenharmony_ci return pci_##op##_config_##size(dev, offset, value); \ 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(read, byte, u8 *) 358c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(read, word, u16 *) 368c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(read, dword, u32 *) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(write, byte, u8) 398c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(write, word, u16) 408c2ecf20Sopenharmony_ciDEFINE_PCI_CONFIG(write, dword, u32) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int conf_space_read(struct pci_dev *dev, 438c2ecf20Sopenharmony_ci const struct config_field_entry *entry, 448c2ecf20Sopenharmony_ci int offset, u32 *value) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci int ret = 0; 478c2ecf20Sopenharmony_ci const struct config_field *field = entry->field; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci *value = 0; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci switch (field->size) { 528c2ecf20Sopenharmony_ci case 1: 538c2ecf20Sopenharmony_ci if (field->u.b.read) 548c2ecf20Sopenharmony_ci ret = field->u.b.read(dev, offset, (u8 *) value, 558c2ecf20Sopenharmony_ci entry->data); 568c2ecf20Sopenharmony_ci break; 578c2ecf20Sopenharmony_ci case 2: 588c2ecf20Sopenharmony_ci if (field->u.w.read) 598c2ecf20Sopenharmony_ci ret = field->u.w.read(dev, offset, (u16 *) value, 608c2ecf20Sopenharmony_ci entry->data); 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci case 4: 638c2ecf20Sopenharmony_ci if (field->u.dw.read) 648c2ecf20Sopenharmony_ci ret = field->u.dw.read(dev, offset, value, entry->data); 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci return ret; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int conf_space_write(struct pci_dev *dev, 718c2ecf20Sopenharmony_ci const struct config_field_entry *entry, 728c2ecf20Sopenharmony_ci int offset, u32 value) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci int ret = 0; 758c2ecf20Sopenharmony_ci const struct config_field *field = entry->field; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci switch (field->size) { 788c2ecf20Sopenharmony_ci case 1: 798c2ecf20Sopenharmony_ci if (field->u.b.write) 808c2ecf20Sopenharmony_ci ret = field->u.b.write(dev, offset, (u8) value, 818c2ecf20Sopenharmony_ci entry->data); 828c2ecf20Sopenharmony_ci break; 838c2ecf20Sopenharmony_ci case 2: 848c2ecf20Sopenharmony_ci if (field->u.w.write) 858c2ecf20Sopenharmony_ci ret = field->u.w.write(dev, offset, (u16) value, 868c2ecf20Sopenharmony_ci entry->data); 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci case 4: 898c2ecf20Sopenharmony_ci if (field->u.dw.write) 908c2ecf20Sopenharmony_ci ret = field->u.dw.write(dev, offset, value, 918c2ecf20Sopenharmony_ci entry->data); 928c2ecf20Sopenharmony_ci break; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci return ret; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic inline u32 get_mask(int size) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (size == 1) 1008c2ecf20Sopenharmony_ci return 0xff; 1018c2ecf20Sopenharmony_ci else if (size == 2) 1028c2ecf20Sopenharmony_ci return 0xffff; 1038c2ecf20Sopenharmony_ci else 1048c2ecf20Sopenharmony_ci return 0xffffffff; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic inline int valid_request(int offset, int size) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci /* Validate request (no un-aligned requests) */ 1108c2ecf20Sopenharmony_ci if ((size == 1 || size == 2 || size == 4) && (offset % size) == 0) 1118c2ecf20Sopenharmony_ci return 1; 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic inline u32 merge_value(u32 val, u32 new_val, u32 new_val_mask, 1168c2ecf20Sopenharmony_ci int offset) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci if (offset >= 0) { 1198c2ecf20Sopenharmony_ci new_val_mask <<= (offset * 8); 1208c2ecf20Sopenharmony_ci new_val <<= (offset * 8); 1218c2ecf20Sopenharmony_ci } else { 1228c2ecf20Sopenharmony_ci new_val_mask >>= (offset * -8); 1238c2ecf20Sopenharmony_ci new_val >>= (offset * -8); 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci val = (val & ~new_val_mask) | (new_val & new_val_mask); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return val; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int xen_pcibios_err_to_errno(int err) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci switch (err) { 1338c2ecf20Sopenharmony_ci case PCIBIOS_SUCCESSFUL: 1348c2ecf20Sopenharmony_ci return XEN_PCI_ERR_success; 1358c2ecf20Sopenharmony_ci case PCIBIOS_DEVICE_NOT_FOUND: 1368c2ecf20Sopenharmony_ci return XEN_PCI_ERR_dev_not_found; 1378c2ecf20Sopenharmony_ci case PCIBIOS_BAD_REGISTER_NUMBER: 1388c2ecf20Sopenharmony_ci return XEN_PCI_ERR_invalid_offset; 1398c2ecf20Sopenharmony_ci case PCIBIOS_FUNC_NOT_SUPPORTED: 1408c2ecf20Sopenharmony_ci return XEN_PCI_ERR_not_implemented; 1418c2ecf20Sopenharmony_ci case PCIBIOS_SET_FAILED: 1428c2ecf20Sopenharmony_ci return XEN_PCI_ERR_access_denied; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci return err; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciint xen_pcibk_config_read(struct pci_dev *dev, int offset, int size, 1488c2ecf20Sopenharmony_ci u32 *ret_val) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int err = 0; 1518c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 1528c2ecf20Sopenharmony_ci const struct config_field_entry *cfg_entry; 1538c2ecf20Sopenharmony_ci const struct config_field *field; 1548c2ecf20Sopenharmony_ci int field_start, field_end; 1558c2ecf20Sopenharmony_ci /* if read fails for any reason, return 0 1568c2ecf20Sopenharmony_ci * (as if device didn't respond) */ 1578c2ecf20Sopenharmony_ci u32 value = 0, tmp_val; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "read %d bytes at 0x%x\n", size, offset); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci if (!valid_request(offset, size)) { 1628c2ecf20Sopenharmony_ci err = XEN_PCI_ERR_invalid_offset; 1638c2ecf20Sopenharmony_ci goto out; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci /* Get the real value first, then modify as appropriate */ 1678c2ecf20Sopenharmony_ci switch (size) { 1688c2ecf20Sopenharmony_ci case 1: 1698c2ecf20Sopenharmony_ci err = pci_read_config_byte(dev, offset, (u8 *) &value); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case 2: 1728c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, offset, (u16 *) &value); 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case 4: 1758c2ecf20Sopenharmony_ci err = pci_read_config_dword(dev, offset, &value); 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { 1808c2ecf20Sopenharmony_ci field = cfg_entry->field; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci field_start = OFFSET(cfg_entry); 1838c2ecf20Sopenharmony_ci field_end = OFFSET(cfg_entry) + field->size; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (offset + size > field_start && field_end > offset) { 1868c2ecf20Sopenharmony_ci err = conf_space_read(dev, cfg_entry, field_start, 1878c2ecf20Sopenharmony_ci &tmp_val); 1888c2ecf20Sopenharmony_ci if (err) 1898c2ecf20Sopenharmony_ci goto out; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci value = merge_value(value, tmp_val, 1928c2ecf20Sopenharmony_ci get_mask(field->size), 1938c2ecf20Sopenharmony_ci field_start - offset); 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciout: 1988c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "read %d bytes at 0x%x = %x\n", size, offset, value); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci *ret_val = value; 2018c2ecf20Sopenharmony_ci return xen_pcibios_err_to_errno(err); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciint xen_pcibk_config_write(struct pci_dev *dev, int offset, int size, u32 value) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int err = 0, handled = 0; 2078c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 2088c2ecf20Sopenharmony_ci const struct config_field_entry *cfg_entry; 2098c2ecf20Sopenharmony_ci const struct config_field *field; 2108c2ecf20Sopenharmony_ci u32 tmp_val; 2118c2ecf20Sopenharmony_ci int field_start, field_end; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "write request %d bytes at 0x%x = %x\n", 2148c2ecf20Sopenharmony_ci size, offset, value); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!valid_request(offset, size)) 2178c2ecf20Sopenharmony_ci return XEN_PCI_ERR_invalid_offset; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { 2208c2ecf20Sopenharmony_ci field = cfg_entry->field; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci field_start = OFFSET(cfg_entry); 2238c2ecf20Sopenharmony_ci field_end = OFFSET(cfg_entry) + field->size; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (offset + size > field_start && field_end > offset) { 2268c2ecf20Sopenharmony_ci err = conf_space_read(dev, cfg_entry, field_start, 2278c2ecf20Sopenharmony_ci &tmp_val); 2288c2ecf20Sopenharmony_ci if (err) 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci tmp_val = merge_value(tmp_val, value, get_mask(size), 2328c2ecf20Sopenharmony_ci offset - field_start); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci err = conf_space_write(dev, cfg_entry, field_start, 2358c2ecf20Sopenharmony_ci tmp_val); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* handled is set true here, but not every byte 2388c2ecf20Sopenharmony_ci * may have been written! Properly detecting if 2398c2ecf20Sopenharmony_ci * every byte is handled is unnecessary as the 2408c2ecf20Sopenharmony_ci * flag is used to detect devices that need 2418c2ecf20Sopenharmony_ci * special helpers to work correctly. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci handled = 1; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (!handled && !err) { 2488c2ecf20Sopenharmony_ci /* By default, anything not specificially handled above is 2498c2ecf20Sopenharmony_ci * read-only. The permissive flag changes this behavior so 2508c2ecf20Sopenharmony_ci * that anything not specifically handled above is writable. 2518c2ecf20Sopenharmony_ci * This means that some fields may still be read-only because 2528c2ecf20Sopenharmony_ci * they have entries in the config_field list that intercept 2538c2ecf20Sopenharmony_ci * the write and do nothing. */ 2548c2ecf20Sopenharmony_ci if (dev_data->permissive || xen_pcibk_permissive) { 2558c2ecf20Sopenharmony_ci switch (size) { 2568c2ecf20Sopenharmony_ci case 1: 2578c2ecf20Sopenharmony_ci err = pci_write_config_byte(dev, offset, 2588c2ecf20Sopenharmony_ci (u8) value); 2598c2ecf20Sopenharmony_ci break; 2608c2ecf20Sopenharmony_ci case 2: 2618c2ecf20Sopenharmony_ci err = pci_write_config_word(dev, offset, 2628c2ecf20Sopenharmony_ci (u16) value); 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case 4: 2658c2ecf20Sopenharmony_ci err = pci_write_config_dword(dev, offset, 2668c2ecf20Sopenharmony_ci (u32) value); 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } else if (!dev_data->warned_on_write) { 2708c2ecf20Sopenharmony_ci dev_data->warned_on_write = 1; 2718c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "Driver tried to write to a " 2728c2ecf20Sopenharmony_ci "read-only configuration space field at offset" 2738c2ecf20Sopenharmony_ci " 0x%x, size %d. This may be harmless, but if " 2748c2ecf20Sopenharmony_ci "you have problems with your device:\n" 2758c2ecf20Sopenharmony_ci "1) see permissive attribute in sysfs\n" 2768c2ecf20Sopenharmony_ci "2) report problems to the xen-devel " 2778c2ecf20Sopenharmony_ci "mailing list along with details of your " 2788c2ecf20Sopenharmony_ci "device obtained from lspci.\n", offset, size); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return xen_pcibios_err_to_errno(err); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ciint xen_pcibk_get_interrupt_type(struct pci_dev *dev) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci int err; 2888c2ecf20Sopenharmony_ci u16 val; 2898c2ecf20Sopenharmony_ci int ret = 0; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* 2928c2ecf20Sopenharmony_ci * Do not trust dev->msi(x)_enabled here, as enabling could be done 2938c2ecf20Sopenharmony_ci * bypassing the pci_*msi* functions, by the qemu. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (dev->msi_cap) { 2968c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, 2978c2ecf20Sopenharmony_ci dev->msi_cap + PCI_MSI_FLAGS, 2988c2ecf20Sopenharmony_ci &val); 2998c2ecf20Sopenharmony_ci if (err) 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci if (val & PCI_MSI_FLAGS_ENABLE) 3028c2ecf20Sopenharmony_ci ret |= INTERRUPT_TYPE_MSI; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci if (dev->msix_cap) { 3058c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, 3068c2ecf20Sopenharmony_ci dev->msix_cap + PCI_MSIX_FLAGS, 3078c2ecf20Sopenharmony_ci &val); 3088c2ecf20Sopenharmony_ci if (err) 3098c2ecf20Sopenharmony_ci return err; 3108c2ecf20Sopenharmony_ci if (val & PCI_MSIX_FLAGS_ENABLE) 3118c2ecf20Sopenharmony_ci ret |= INTERRUPT_TYPE_MSIX; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * PCIe spec says device cannot use INTx if MSI/MSI-X is enabled, 3168c2ecf20Sopenharmony_ci * so check for INTx only when both are disabled. 3178c2ecf20Sopenharmony_ci */ 3188c2ecf20Sopenharmony_ci if (!ret) { 3198c2ecf20Sopenharmony_ci err = pci_read_config_word(dev, PCI_COMMAND, &val); 3208c2ecf20Sopenharmony_ci if (err) 3218c2ecf20Sopenharmony_ci return err; 3228c2ecf20Sopenharmony_ci if (!(val & PCI_COMMAND_INTX_DISABLE)) 3238c2ecf20Sopenharmony_ci ret |= INTERRUPT_TYPE_INTX; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return ret ?: INTERRUPT_TYPE_NONE; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_civoid xen_pcibk_config_free_dyn_fields(struct pci_dev *dev) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 3328c2ecf20Sopenharmony_ci struct config_field_entry *cfg_entry, *t; 3338c2ecf20Sopenharmony_ci const struct config_field *field; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "free-ing dynamically allocated virtual " 3368c2ecf20Sopenharmony_ci "configuration space fields\n"); 3378c2ecf20Sopenharmony_ci if (!dev_data) 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) { 3418c2ecf20Sopenharmony_ci field = cfg_entry->field; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (field->clean) { 3448c2ecf20Sopenharmony_ci field->clean((struct config_field *)field); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci kfree(cfg_entry->data); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci list_del(&cfg_entry->list); 3498c2ecf20Sopenharmony_ci kfree(cfg_entry); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_civoid xen_pcibk_config_reset_dev(struct pci_dev *dev) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 3588c2ecf20Sopenharmony_ci const struct config_field_entry *cfg_entry; 3598c2ecf20Sopenharmony_ci const struct config_field *field; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "resetting virtual configuration space\n"); 3628c2ecf20Sopenharmony_ci if (!dev_data) 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { 3668c2ecf20Sopenharmony_ci field = cfg_entry->field; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (field->reset) 3698c2ecf20Sopenharmony_ci field->reset(dev, OFFSET(cfg_entry), cfg_entry->data); 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_civoid xen_pcibk_config_free_dev(struct pci_dev *dev) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 3768c2ecf20Sopenharmony_ci struct config_field_entry *cfg_entry, *t; 3778c2ecf20Sopenharmony_ci const struct config_field *field; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "free-ing virtual configuration space fields\n"); 3808c2ecf20Sopenharmony_ci if (!dev_data) 3818c2ecf20Sopenharmony_ci return; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci list_for_each_entry_safe(cfg_entry, t, &dev_data->config_fields, list) { 3848c2ecf20Sopenharmony_ci list_del(&cfg_entry->list); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci field = cfg_entry->field; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (field->release) 3898c2ecf20Sopenharmony_ci field->release(dev, OFFSET(cfg_entry), cfg_entry->data); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci kfree(cfg_entry); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ciint xen_pcibk_config_add_field_offset(struct pci_dev *dev, 3968c2ecf20Sopenharmony_ci const struct config_field *field, 3978c2ecf20Sopenharmony_ci unsigned int base_offset) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci int err = 0; 4008c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 4018c2ecf20Sopenharmony_ci struct config_field_entry *cfg_entry; 4028c2ecf20Sopenharmony_ci void *tmp; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci cfg_entry = kmalloc(sizeof(*cfg_entry), GFP_KERNEL); 4058c2ecf20Sopenharmony_ci if (!cfg_entry) { 4068c2ecf20Sopenharmony_ci err = -ENOMEM; 4078c2ecf20Sopenharmony_ci goto out; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci cfg_entry->data = NULL; 4118c2ecf20Sopenharmony_ci cfg_entry->field = field; 4128c2ecf20Sopenharmony_ci cfg_entry->base_offset = base_offset; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* silently ignore duplicate fields */ 4158c2ecf20Sopenharmony_ci err = xen_pcibk_field_is_dup(dev, OFFSET(cfg_entry)); 4168c2ecf20Sopenharmony_ci if (err) 4178c2ecf20Sopenharmony_ci goto out; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (field->init) { 4208c2ecf20Sopenharmony_ci tmp = field->init(dev, OFFSET(cfg_entry)); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (IS_ERR(tmp)) { 4238c2ecf20Sopenharmony_ci err = PTR_ERR(tmp); 4248c2ecf20Sopenharmony_ci goto out; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci cfg_entry->data = tmp; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "added config field at offset 0x%02x\n", 4318c2ecf20Sopenharmony_ci OFFSET(cfg_entry)); 4328c2ecf20Sopenharmony_ci list_add_tail(&cfg_entry->list, &dev_data->config_fields); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ciout: 4358c2ecf20Sopenharmony_ci if (err) 4368c2ecf20Sopenharmony_ci kfree(cfg_entry); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return err; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci/* This sets up the device's virtual configuration space to keep track of 4428c2ecf20Sopenharmony_ci * certain registers (like the base address registers (BARs) so that we can 4438c2ecf20Sopenharmony_ci * keep the client from manipulating them directly. 4448c2ecf20Sopenharmony_ci */ 4458c2ecf20Sopenharmony_ciint xen_pcibk_config_init_dev(struct pci_dev *dev) 4468c2ecf20Sopenharmony_ci{ 4478c2ecf20Sopenharmony_ci int err = 0; 4488c2ecf20Sopenharmony_ci struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "initializing virtual configuration space\n"); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dev_data->config_fields); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci err = xen_pcibk_config_header_add_fields(dev); 4558c2ecf20Sopenharmony_ci if (err) 4568c2ecf20Sopenharmony_ci goto out; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci err = xen_pcibk_config_capability_add_fields(dev); 4598c2ecf20Sopenharmony_ci if (err) 4608c2ecf20Sopenharmony_ci goto out; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci err = xen_pcibk_config_quirks_init(dev); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ciout: 4658c2ecf20Sopenharmony_ci return err; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ciint xen_pcibk_config_init(void) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci return xen_pcibk_config_capability_init(); 4718c2ecf20Sopenharmony_ci} 472