static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, const void *arg) { struct pci_vpd *vpd = dev->vpd; const u8 *buf = arg; loff_t end = pos + count; int ret = 0; if (pos < 0 || (pos & 3) || (count & 3)) return -EINVAL; if (!vpd->valid) { vpd->valid = 1; vpd->len = pci_vpd_size(dev, vpd->len); } if (vpd->len == 0) return -EIO; if (end > vpd->len) return -EINVAL; if (mutex_lock_killable(&vpd->lock)) return -EINTR; ret = pci_vpd_wait(dev); if (ret < 0) goto out; while (pos < end) { u32 val; val = *buf++; val |= *buf++ << 8; val |= *buf++ << 16; val |= *buf++ << 24; ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); if (ret < 0) break; ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, pos | PCI_VPD_ADDR_F); if (ret < 0) break; vpd->busy = 1; vpd->flag = 0; ret = pci_vpd_wait(dev); if (ret < 0) break; pos += sizeof(u32); } out: mutex_unlock(&vpd->lock); return ret ? ret : count; }
SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, unsigned long, off, unsigned long, len, void __user *, buf) { struct pci_dev *dev; u8 byte; u16 word; u32 dword; int err = 0; if (!capable(CAP_SYS_ADMIN) || (get_securelevel() > 0)) return -EPERM; dev = pci_get_bus_and_slot(bus, dfn); if (!dev) return -ENODEV; switch (len) { case 1: err = get_user(byte, (u8 __user *)buf); if (err) break; err = pci_user_write_config_byte(dev, off, byte); if (err != PCIBIOS_SUCCESSFUL) err = -EIO; break; case 2: err = get_user(word, (u16 __user *)buf); if (err) break; err = pci_user_write_config_word(dev, off, word); if (err != PCIBIOS_SUCCESSFUL) err = -EIO; break; case 4: err = get_user(dword, (u32 __user *)buf); if (err) break; err = pci_user_write_config_dword(dev, off, dword); if (err != PCIBIOS_SUCCESSFUL) err = -EIO; break; default: err = -EINVAL; break; } pci_dev_put(dev); return err; }
static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos) { struct inode *ino = file_inode(file); struct pci_dev *dev = PDE_DATA(ino); int pos = *ppos; int size = dev->cfg_size; int cnt; if (pos >= size) return 0; if (nbytes >= size) nbytes = size; if (pos + nbytes > size) nbytes = size - pos; cnt = nbytes; if (!access_ok(VERIFY_READ, buf, cnt)) return -EINVAL; pci_config_pm_runtime_get(dev); if ((pos & 1) && cnt) { unsigned char val; __get_user(val, buf); pci_user_write_config_byte(dev, pos, val); buf++; pos++; cnt--; } if ((pos & 3) && cnt > 2) { __le16 val; __get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; cnt -= 2; } while (cnt >= 4) { __le32 val; __get_user(val, (__le32 __user *) buf); pci_user_write_config_dword(dev, pos, le32_to_cpu(val)); buf += 4; pos += 4; cnt -= 4; } if (cnt >= 2) { __le16 val; __get_user(val, (__le16 __user *) buf); pci_user_write_config_word(dev, pos, le16_to_cpu(val)); buf += 2; pos += 2; cnt -= 2; } if (cnt) { unsigned char val; __get_user(val, buf); pci_user_write_config_byte(dev, pos, val); buf++; pos++; cnt--; } pci_config_pm_runtime_put(dev); *ppos = pos; i_size_write(ino, dev->cfg_size); return nbytes; }