brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · cf568e8 Raw
306 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * PCI Backend - Handles the virtual fields found on the capability lists4 *               in the configuration space.5 *6 * Author: Ryan Wilson <hap9@epoch.ncsc.mil>7 */8 9#include <linux/kernel.h>10#include <linux/pci.h>11#include "pciback.h"12#include "conf_space.h"13 14static LIST_HEAD(capabilities);15struct xen_pcibk_config_capability {16	struct list_head cap_list;17 18	int capability;19 20	/* If the device has the capability found above, add these fields */21	const struct config_field *fields;22};23 24static const struct config_field caplist_header[] = {25	{26	 .offset    = PCI_CAP_LIST_ID,27	 .size      = 2, /* encompass PCI_CAP_LIST_ID & PCI_CAP_LIST_NEXT */28	 .u.w.read  = xen_pcibk_read_config_word,29	 .u.w.write = NULL,30	},31	{}32};33 34static inline void register_capability(struct xen_pcibk_config_capability *cap)35{36	list_add_tail(&cap->cap_list, &capabilities);37}38 39int xen_pcibk_config_capability_add_fields(struct pci_dev *dev)40{41	int err = 0;42	struct xen_pcibk_config_capability *cap;43	int cap_offset;44 45	list_for_each_entry(cap, &capabilities, cap_list) {46		cap_offset = pci_find_capability(dev, cap->capability);47		if (cap_offset) {48			dev_dbg(&dev->dev, "Found capability 0x%x at 0x%x\n",49				cap->capability, cap_offset);50 51			err = xen_pcibk_config_add_fields_offset(dev,52							       caplist_header,53							       cap_offset);54			if (err)55				goto out;56			err = xen_pcibk_config_add_fields_offset(dev,57							       cap->fields,58							       cap_offset);59			if (err)60				goto out;61		}62	}63 64out:65	return err;66}67 68static int vpd_address_write(struct pci_dev *dev, int offset, u16 value,69			     void *data)70{71	/* Disallow writes to the vital product data */72	if (value & PCI_VPD_ADDR_F)73		return PCIBIOS_SET_FAILED;74	else75		return pci_write_config_word(dev, offset, value);76}77 78static const struct config_field caplist_vpd[] = {79	{80	 .offset    = PCI_VPD_ADDR,81	 .size      = 2,82	 .u.w.read  = xen_pcibk_read_config_word,83	 .u.w.write = vpd_address_write,84	 },85	{86	 .offset     = PCI_VPD_DATA,87	 .size       = 4,88	 .u.dw.read  = xen_pcibk_read_config_dword,89	 .u.dw.write = NULL,90	 },91	{}92};93 94static int pm_caps_read(struct pci_dev *dev, int offset, u16 *value,95			void *data)96{97	int err;98	u16 real_value;99 100	err = pci_read_config_word(dev, offset, &real_value);101	if (err)102		goto out;103 104	*value = real_value & ~PCI_PM_CAP_PME_MASK;105 106out:107	return err;108}109 110/* PM_OK_BITS specifies the bits that the driver domain is allowed to change.111 * Can't allow driver domain to enable PMEs - they're shared */112#define PM_OK_BITS (PCI_PM_CTRL_PME_STATUS|PCI_PM_CTRL_DATA_SEL_MASK)113 114static int pm_ctrl_write(struct pci_dev *dev, int offset, u16 new_value,115			 void *data)116{117	int err;118	u16 old_value;119	pci_power_t new_state;120 121	err = pci_read_config_word(dev, offset, &old_value);122	if (err)123		goto out;124 125	new_state = (__force pci_power_t)(new_value & PCI_PM_CTRL_STATE_MASK);126 127	new_value &= PM_OK_BITS;128	if ((old_value & PM_OK_BITS) != new_value) {129		new_value = (old_value & ~PM_OK_BITS) | new_value;130		err = pci_write_config_word(dev, offset, new_value);131		if (err)132			goto out;133	}134 135	/* Let pci core handle the power management change */136	dev_dbg(&dev->dev, "set power state to %x\n", new_state);137	err = pci_set_power_state(dev, new_state);138	if (err) {139		err = PCIBIOS_SET_FAILED;140		goto out;141	}142 143 out:144	return err;145}146 147/* Ensure PMEs are disabled */148static void *pm_ctrl_init(struct pci_dev *dev, int offset)149{150	int err;151	u16 value;152 153	err = pci_read_config_word(dev, offset, &value);154	if (err)155		goto out;156 157	if (value & PCI_PM_CTRL_PME_ENABLE) {158		value &= ~PCI_PM_CTRL_PME_ENABLE;159		err = pci_write_config_word(dev, offset, value);160	}161 162out:163	return err ? ERR_PTR(err) : NULL;164}165 166static const struct config_field caplist_pm[] = {167	{168		.offset     = PCI_PM_PMC,169		.size       = 2,170		.u.w.read   = pm_caps_read,171	},172	{173		.offset     = PCI_PM_CTRL,174		.size       = 2,175		.init       = pm_ctrl_init,176		.u.w.read   = xen_pcibk_read_config_word,177		.u.w.write  = pm_ctrl_write,178	},179	{180		.offset     = PCI_PM_PPB_EXTENSIONS,181		.size       = 1,182		.u.b.read   = xen_pcibk_read_config_byte,183	},184	{185		.offset     = PCI_PM_DATA_REGISTER,186		.size       = 1,187		.u.b.read   = xen_pcibk_read_config_byte,188	},189	{}190};191 192static struct msi_msix_field_config {193	u16          enable_bit;   /* bit for enabling MSI/MSI-X */194	u16          allowed_bits; /* bits allowed to be changed */195	unsigned int int_type;     /* interrupt type for exclusiveness check */196} msi_field_config = {197	.enable_bit	= PCI_MSI_FLAGS_ENABLE,198	.allowed_bits	= PCI_MSI_FLAGS_ENABLE,199	.int_type	= INTERRUPT_TYPE_MSI,200}, msix_field_config = {201	.enable_bit	= PCI_MSIX_FLAGS_ENABLE,202	.allowed_bits	= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL,203	.int_type	= INTERRUPT_TYPE_MSIX,204};205 206static void *msi_field_init(struct pci_dev *dev, int offset)207{208	return &msi_field_config;209}210 211static void *msix_field_init(struct pci_dev *dev, int offset)212{213	return &msix_field_config;214}215 216static int msi_msix_flags_write(struct pci_dev *dev, int offset, u16 new_value,217				void *data)218{219	int err;220	u16 old_value;221	const struct msi_msix_field_config *field_config = data;222	const struct xen_pcibk_dev_data *dev_data = pci_get_drvdata(dev);223 224	if (xen_pcibk_permissive || dev_data->permissive)225		goto write;226 227	err = pci_read_config_word(dev, offset, &old_value);228	if (err)229		return err;230 231	if (new_value == old_value)232		return 0;233 234	if (!dev_data->allow_interrupt_control ||235	    (new_value ^ old_value) & ~field_config->allowed_bits)236		return PCIBIOS_SET_FAILED;237 238	if (new_value & field_config->enable_bit) {239		/*240		 * Don't allow enabling together with other interrupt type, but do241		 * allow enabling MSI(-X) while INTx is still active to please Linuxes242		 * MSI(-X) startup sequence. It is safe to do, as according to PCI243		 * spec, device with enabled MSI(-X) shouldn't use INTx.244		 */245		int int_type = xen_pcibk_get_interrupt_type(dev);246 247		if (int_type == INTERRUPT_TYPE_NONE ||248		    int_type == INTERRUPT_TYPE_INTX ||249		    int_type == field_config->int_type)250			goto write;251		return PCIBIOS_SET_FAILED;252	}253 254write:255	return pci_write_config_word(dev, offset, new_value);256}257 258static const struct config_field caplist_msix[] = {259	{260		.offset    = PCI_MSIX_FLAGS,261		.size      = 2,262		.init      = msix_field_init,263		.u.w.read  = xen_pcibk_read_config_word,264		.u.w.write = msi_msix_flags_write,265	},266	{}267};268 269static const struct config_field caplist_msi[] = {270	{271		.offset    = PCI_MSI_FLAGS,272		.size      = 2,273		.init      = msi_field_init,274		.u.w.read  = xen_pcibk_read_config_word,275		.u.w.write = msi_msix_flags_write,276	},277	{}278};279 280static struct xen_pcibk_config_capability xen_pcibk_config_capability_pm = {281	.capability = PCI_CAP_ID_PM,282	.fields = caplist_pm,283};284static struct xen_pcibk_config_capability xen_pcibk_config_capability_vpd = {285	.capability = PCI_CAP_ID_VPD,286	.fields = caplist_vpd,287};288static struct xen_pcibk_config_capability xen_pcibk_config_capability_msi = {289	.capability = PCI_CAP_ID_MSI,290	.fields = caplist_msi,291};292static struct xen_pcibk_config_capability xen_pcibk_config_capability_msix = {293	.capability = PCI_CAP_ID_MSIX,294	.fields = caplist_msix,295};296 297int xen_pcibk_config_capability_init(void)298{299	register_capability(&xen_pcibk_config_capability_vpd);300	register_capability(&xen_pcibk_config_capability_pm);301	register_capability(&xen_pcibk_config_capability_msi);302	register_capability(&xen_pcibk_config_capability_msix);303 304	return 0;305}306