[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/3] save/restore PCI configuration space in pciback.
This patch enables saving/restoring PCI configuration space in pciback. The timing of saving/restoring configuration space is like below. When pciback is bound to devices. - Pciback saves configuration space. When pciback is unbound to devices. - Pciback restores configuration space. When guest OS boots or a device is hotadded. - Pciback restores configuration space. - Pciback changes state of backend device to Initialised/Reconfigured. - Xend waits for the transition to Initialised/Reconfigured. When guest OS shutdowns or a device is hotremoved. - Pciback restores configuration space. - Xend resets devices. * If D-state of the device is not D0, the state is changed to D0 before resetting the device. - Xend deassigns devices. * Essentially, devices should be reset before configuration space is restored. But it needs big modifications. Applying these patches, configuration space is restored when guest OS boots, a device is hotadded or pciback is unbound. So it has no matter. The following registers are saved/restored by pciback. Configuration Space - Base Address Register set - Cache-line size Register - Latency timer Register - Enable SERR Bit / Enable PERR Bit in Control Register - Device Control Register (PCI Express Capability) - Link Control Register (PCI Express Capability) - Device Control 2 Register (PCI Express Capability) - Link Control 2 Register (PCI Express Capability) AER - Uncorrectable Error Mask Register - Uncorrectable Error Severity Register - Correctable Error Mask Register - Advanced Error Capabilities and Control Register Thanks, -- Yuji Shimada Signed-off-by: Yuji Shimada <shimada-yxb@xxxxxxxxxxxxxxx> diff -r 366c31f3ab4b drivers/xen/pciback/Makefile --- a/drivers/xen/pciback/Makefile Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/Makefile Tue Apr 21 16:48:25 2009 +0900 @@ -5,6 +5,7 @@ conf_space_capability.o \ conf_space_capability_vpd.o \ conf_space_capability_pm.o \ + conf_space_capability_exp.o \ conf_space_quirks.o pciback-$(CONFIG_PCI_MSI) += conf_space_capability_msi.o pciback-$(CONFIG_XEN_PCIDEV_BACKEND_VPCI) += vpci.o diff -r 366c31f3ab4b drivers/xen/pciback/conf_space.c --- a/drivers/xen/pciback/conf_space.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/conf_space.c Tue Apr 21 16:48:25 2009 +0900 @@ -375,6 +375,18 @@ cfg_entry->field = field; cfg_entry->base_offset = base_offset; + /* When the size of the registers is different, by a version of the PCI, + * "is_need" function can prevent addition to the list of an unnecessary + * register. + */ + if (field->is_need) { + err = field->is_need(dev, base_offset); + if (!err) { + kfree(cfg_entry); + goto out; + } + } + /* silently ignore duplicate fields */ err = pciback_field_is_dup(dev,OFFSET(cfg_entry)); if (err) @@ -433,3 +445,69 @@ { return pciback_config_capability_init(); } + +/* save AER one register */ +static int pciback_aer_save_one_register(struct pci_dev *dev, int offset) +{ + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + return pci_read_config_dword(dev, (dev_data->config.aer_base + offset), + (u32*)&dev_data->config.config_aer[offset]); +} + +/* save AER registers */ +int pciback_aer_reg_save(struct pci_dev *dev) +{ + int err = 0; + + /* after reset, following register values should be restored. + * So, save them. + */ + err = pciback_aer_save_one_register(dev, PCI_ERR_UNCOR_MASK); + if (err) + goto out; + err = pciback_aer_save_one_register(dev, PCI_ERR_UNCOR_SEVER); + if (err) + goto out; + err = pciback_aer_save_one_register(dev, PCI_ERR_COR_MASK); + if (err) + goto out; + err = pciback_aer_save_one_register(dev, PCI_ERR_CAP); + + out: + return err; +} + +/* restore AER one register */ +static int pciback_aer_restore_one_register(struct pci_dev *dev, int offset) +{ + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + return pci_write_config_dword(dev, + (dev_data->config.aer_base + offset), + *((u32*)&dev_data->config.config[offset])); +} + +/* restore AER registers */ +int pciback_aer_reg_restore(struct pci_dev *dev) +{ + int err = 0; + + /* the following registers should be reconfigured to correct values + * after reset. restore them. + * other registers should not be reconfigured after reset + * if there is no reason + */ + err = pciback_aer_restore_one_register(dev, PCI_ERR_UNCOR_MASK); + if (err) + goto out; + err = pciback_aer_restore_one_register(dev, PCI_ERR_UNCOR_SEVER); + if (err) + goto out; + err = pciback_aer_restore_one_register(dev, PCI_ERR_COR_MASK); + if (err) + goto out; + err = pciback_aer_restore_one_register(dev, PCI_ERR_CAP); + + out: + return err; +} + diff -r 366c31f3ab4b drivers/xen/pciback/conf_space.h --- a/drivers/xen/pciback/conf_space.h Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/conf_space.h Tue Apr 21 16:48:25 2009 +0900 @@ -27,6 +27,10 @@ void *data); typedef int (*conf_byte_read) (struct pci_dev * dev, int offset, u8 * value, void *data); +typedef int (*conf_dword_restore)(struct pci_dev *dev, int offset, u32 data); +typedef int (*conf_word_restore)(struct pci_dev *dev, int offset, u16 data); +typedef int (*conf_byte_restore)(struct pci_dev *dev, int offset, u8 data); +typedef int (*conf_field_is_need)(struct pci_dev *dev, int offset); /* These are the fields within the configuration space which we * are interested in intercepting reads/writes to and changing their @@ -44,16 +48,20 @@ struct { conf_dword_write write; conf_dword_read read; + conf_dword_restore restore; } dw; struct { conf_word_write write; conf_word_read read; + conf_word_restore restore; } w; struct { conf_byte_write write; conf_byte_read read; + conf_byte_restore restore; } b; } u; + conf_field_is_need is_need; struct list_head list; }; @@ -123,4 +131,22 @@ int pciback_config_header_add_fields(struct pci_dev *dev); int pciback_config_capability_add_fields(struct pci_dev *dev); +static inline int pciback_restore_config_dword(struct pci_dev *dev, int offset, + u32 data) +{ + return pci_write_config_dword(dev, offset, data); +} + +static inline int pciback_restore_config_word(struct pci_dev *dev, int offset, + u16 data) +{ + return pci_write_config_word(dev, offset, data); +} + +static inline int pciback_restore_config_byte(struct pci_dev *dev, int offset, + u8 data) +{ + return pci_write_config_byte(dev, offset, data); +} + #endif /* __XEN_PCIBACK_CONF_SPACE_H__ */ diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability.c --- a/drivers/xen/pciback/conf_space_capability.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/conf_space_capability.c Tue Apr 21 16:48:25 2009 +0900 @@ -59,11 +59,13 @@ extern struct pciback_config_capability pciback_config_capability_vpd; extern struct pciback_config_capability pciback_config_capability_pm; +extern struct pciback_config_capability pciback_config_capability_exp; int pciback_config_capability_init(void) { register_capability(&pciback_config_capability_vpd); register_capability(&pciback_config_capability_pm); + register_capability(&pciback_config_capability_exp); return 0; } diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability_exp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/drivers/xen/pciback/conf_space_capability_exp.c Tue Apr 21 16:48:25 2009 +0900 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2009, NEC Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307 USA. + */ +/* + * PCI Backend -- Configuration overlay for PCI Express capability + */ +#include <linux/pci.h> +#include "conf_space.h" +#include "conf_space_capability.h" + +/* PCI express version 1.0 does not have the registers over offset 24H */ +static int pciback_exp_is_need_config2(struct pci_dev *dev, int base_offset) +{ + u16 cap; + pci_read_config_word(dev, base_offset + PCI_EXP_FLAGS, &cap); + if ((cap & PCI_EXP_FLAGS_VERS) == 1) + return 0; + else + return 1; +} + +static const struct config_field caplist_exp[] = { + { + .offset = PCI_EXP_DEVCTL, + .size = 2, + .u.w.restore = pciback_restore_config_word, + }, + { + .offset = PCI_EXP_LNKCTL, + .size = 2, + .u.w.restore = pciback_restore_config_word, + }, + { + .offset = PCI_EXP_DEVCTL2, + .size = 2, + .u.w.restore = pciback_restore_config_word, + .is_need = pciback_exp_is_need_config2, + }, + { + .offset = PCI_EXP_LNKCTL2, + .size = 2, + .u.w.restore = pciback_restore_config_word, + .is_need = pciback_exp_is_need_config2, + }, + {} +}; + +struct pciback_config_capability pciback_config_capability_exp = { + .capability = PCI_CAP_ID_EXP, + .fields = caplist_exp, +}; + diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_capability_pm.c --- a/drivers/xen/pciback/conf_space_capability_pm.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/conf_space_capability_pm.c Tue Apr 21 16:48:25 2009 +0900 @@ -94,6 +94,27 @@ return ERR_PTR(err); } +/* restore Power Management Control/Status register */ +static int pm_ctrl_restore(struct pci_dev *dev, int offset, u16 data) +{ + int err; + u16 value; + + err = pci_read_config_word(dev, offset, &value); + if (err) + goto out; + + /* No need to restore, just clear PME Enable and PME Status bit + * Note: register type of PME Status bit is RW1C, so clear by writing 1b + */ + value = (value & ~PCI_PM_CTRL_PME_ENABLE) | PCI_PM_CTRL_PME_STATUS; + + err = pci_write_config_word(dev, offset, value); + + out: + return err; +} + static const struct config_field caplist_pm[] = { { .offset = PCI_PM_PMC, @@ -106,6 +127,7 @@ .init = pm_ctrl_init, .u.w.read = pciback_read_config_word, .u.w.write = pm_ctrl_write, + .u.w.restore = pm_ctrl_restore, }, { .offset = PCI_PM_PPB_EXTENSIONS, diff -r 366c31f3ab4b drivers/xen/pciback/conf_space_header.c --- a/drivers/xen/pciback/conf_space_header.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/conf_space_header.c Tue Apr 21 16:48:25 2009 +0900 @@ -210,12 +210,34 @@ return err; } +#define CONF_COMMAND_MASK (PCI_COMMAND_PARITY | PCI_COMMAND_SERR) + +static int command_restore(struct pci_dev *dev, int offset, u16 data) +{ + int err; + u16 tmp_val; + + err = pci_read_config_word(dev, offset, &tmp_val); + if (err) + goto out; + + tmp_val &= ~CONF_COMMAND_MASK; + tmp_val |= (data & CONF_COMMAND_MASK); + + err = pci_write_config_word(dev, offset, tmp_val); + + out: + return err; +} + + static const struct config_field header_common[] = { { .offset = PCI_COMMAND, .size = 2, .u.w.read = pciback_read_config_word, .u.w.write = command_write, + .u.w.restore = command_restore, }, { .offset = PCI_INTERRUPT_LINE, @@ -233,11 +255,13 @@ .size = 1, .u.b.read = pciback_read_config_byte, .u.b.write = pciback_write_config_byte, + .u.b.restore = pciback_restore_config_byte, }, { .offset = PCI_LATENCY_TIMER, .size = 1, .u.b.read = pciback_read_config_byte, + .u.b.restore = pciback_restore_config_byte, }, { .offset = PCI_BIST, @@ -257,6 +281,7 @@ .release = bar_release, \ .u.dw.read = bar_read, \ .u.dw.write = bar_write, \ + .u.dw.restore = pciback_restore_config_dword, \ } #define CFG_FIELD_ROM(reg_offset) \ @@ -268,6 +293,7 @@ .release = bar_release, \ .u.dw.read = bar_read, \ .u.dw.write = rom_write, \ + .u.dw.restore = pciback_restore_config_dword, \ } static const struct config_field header_0[] = { diff -r 366c31f3ab4b drivers/xen/pciback/pci_stub.c --- a/drivers/xen/pciback/pci_stub.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/pci_stub.c Tue Apr 21 16:48:25 2009 +0900 @@ -135,6 +135,91 @@ return psdev; } +int pcistub_save_config_space(struct pci_dev *dev) +{ + int err = 0; + int offset; + u8 value; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + + /* save configuration space */ + for (offset = 0; offset < 256; offset++) { + err = pci_read_config_byte(dev, offset, &value); + if (err) { + dev_err(&dev->dev, + "(%s) Read error in config space[%x]!\n", + __func__, offset); + goto out; + } + dev_data->config.config[offset] = value; + } + dev_dbg(&dev->dev, "Save real configuration space \n"); + + dev_data->config.aer_base = + (u32)pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!dev_data->config.aer_base) + goto out; + + /* save advanced error reporting registers */ + err = pciback_aer_reg_save(dev); + dev_dbg(&dev->dev, "Save advanced error reporting[%d]\n", err); + + out: + return err; +} + +int pcistub_restore_config_space(struct pci_dev *dev) +{ + int err = 0; + struct pciback_dev_data *dev_data = pci_get_drvdata(dev); + struct config_field_entry *cfg_entry; + const struct config_field *field; + int offset; + + /* Restore configuration space */ + list_for_each_entry(cfg_entry, &dev_data->config_fields, list) { + field = cfg_entry->field; + offset = OFFSET(cfg_entry); + switch (field->size) { + case 1: + if (!field->u.b.restore) + continue; + err = field->u.b.restore(dev, + offset, dev_data->config.config[offset]); + break; + case 2: + if (!field->u.w.restore) + continue; + err = field->u.w.restore(dev, offset, + *((u16 *)&dev_data->config.config[offset])); + break; + case 4: + if (!field->u.dw.restore) + continue; + err = field->u.dw.restore(dev, offset, + *((u32 *)&dev_data->config.config[offset])); + break; + } + if (err) { + dev_err(&dev->dev, + "(%s) Restore error<%d> in config space" + "at offset 0x%x!\n", __func__, err, offset); + goto out; + } + } + dev_dbg(&dev->dev, "Restore base configuration space \n"); + + if (!dev_data->config.aer_base) + goto out; + + /* restore advanced error reporting registers */ + err = pciback_aer_reg_restore(dev); + dev_dbg(&dev->dev, "Restore advanced error reporting\n"); + + out: + return err; +} + static struct pci_dev *pcistub_device_get_pci_dev(struct pciback_device *pdev, struct pcistub_device *psdev) { @@ -292,6 +377,10 @@ } pci_set_drvdata(dev, dev_data); + /* Save configuration space */ + dev_dbg(&dev->dev, "Save configuration space\n"); + pcistub_save_config_space(dev); + dev_dbg(&dev->dev, "initializing config\n"); init_waitqueue_head(&aer_wait_queue); diff -r 366c31f3ab4b drivers/xen/pciback/pciback.h --- a/drivers/xen/pciback/pciback.h Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/pciback.h Tue Apr 21 16:48:25 2009 +0900 @@ -44,10 +44,17 @@ struct work_struct op_work; }; +struct pciback_config { + u8 config[256]; + u32 aer_base; + u8 config_aer[72]; +}; + struct pciback_dev_data { struct list_head config_fields; int permissive; int warned_on_write; + struct pciback_config config; }; /* Get/Put PCI Devices that are hidden from the PCI Backend Domain */ @@ -122,5 +129,12 @@ extern int verbose_request; void test_and_schedule_op(struct pciback_device *pdev); + +extern int pciback_aer_reg_save(struct pci_dev *dev); +extern int pciback_aer_reg_restore(struct pci_dev *dev); + +extern int pcistub_save_config_space(struct pci_dev *dev); +extern int pcistub_restore_config_space(struct pci_dev *dev); + #endif diff -r 366c31f3ab4b drivers/xen/pciback/pciback_ops.c --- a/drivers/xen/pciback/pciback_ops.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/pciback_ops.c Tue Apr 21 16:48:25 2009 +0900 @@ -20,6 +20,9 @@ { u16 cmd; + /* restore configuration space */ + pcistub_restore_config_space(dev); + /* Disable devices (but not bridges) */ if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL) { pci_disable_device(dev); diff -r 366c31f3ab4b drivers/xen/pciback/xenbus.c --- a/drivers/xen/pciback/xenbus.c Tue Apr 14 11:17:47 2009 +0100 +++ b/drivers/xen/pciback/xenbus.c Tue Apr 21 16:48:25 2009 +0900 @@ -43,6 +43,30 @@ return pdev; } +static int pciback_reinit_device(struct pciback_device *pdev, + int domain, int bus, int slot, int func) +{ + int err = 0; + struct pci_dev *dev; + + dev_dbg(&pdev->xdev->dev, "Reinitializing dom %x bus %x slot %x" + " func %x\n", domain, bus, slot, func); + + dev = pciback_get_pci_dev(pdev, domain, bus, PCI_DEVFN(slot, func)); + if (!dev) { + err = -EINVAL; + dev_dbg(&pdev->xdev->dev, "Couldn't locate PCI device " + "(%04x:%02x:%02x.%01x)! not owned by this domain\n", + domain, bus, slot, func); + goto out; + } + + pciback_reset_device(dev); + + out: + return err; +} + static void pciback_disconnect(struct pciback_device *pdev) { spin_lock(&pdev->dev_lock); @@ -394,6 +418,15 @@ if (err) goto out; + err = pciback_reinit_device(pdev, domain, bus, slot, + func); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error reinitialize pci device" + "configuration"); + goto out; + } + /* Publish pci roots. */ err = pciback_publish_pci_roots(pdev, pciback_publish_pci_root); if (err) { @@ -575,6 +608,14 @@ if (err) goto out; + err = pciback_reinit_device(pdev, domain, bus, slot, func); + if (err) { + xenbus_dev_fatal(pdev->xdev, err, + "Error reinitialize pci device " + "configuration"); + goto out; + } + /* Switch substate of this device. */ l = snprintf(state_str, sizeof(state_str), "state-%d", i); if (unlikely(l >= (sizeof(state_str) - 1))) { @@ -627,6 +668,10 @@ pciback_setup_backend(pdev); break; + case XenbusStateReconfiguring: + pciback_reconfigure(pdev); + break; + default: break; } _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |