// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #include static int status_show(struct param_d *param, void *_dev) { struct virtio_device *dev = _dev; dev->status_param = dev->config->get_status(dev); return 0; } static struct param_d *virtio_dev_add_param_features(struct virtio_device *dev) { struct param_d *param; unsigned int i; char *buf; int len = 0; buf = xmalloc(sizeof(dev->features)*8 + 1); /* We actually represent this as a bitstring, as it could be * arbitrary length in future. */ for (i = 0; i < sizeof(dev->features)*8; i++) len += sprintf(buf+len, "%c", __virtio_test_bit(dev, i) ? '1' : '0'); param = dev_add_param_string_fixed(&dev->dev, "features", buf); free(buf); return param; } static inline int virtio_id_match(const struct virtio_device *dev, const struct virtio_device_id *id) { if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID) return 0; return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor; } /* This looks through all the IDs a driver claims to support. If any of them * match, we return 1 and the kernel will call virtio_dev_probe(). */ static int virtio_dev_match(struct device *_dv, struct driver *_dr) { unsigned int i; struct virtio_device *dev = dev_to_virtio(_dv); const struct virtio_device_id *ids; ids = drv_to_virtio(_dr)->id_table; for (i = 0; ids[i].device; i++) if (virtio_id_match(dev, &ids[i])) return 0; return -1; } void virtio_check_driver_offered_feature(const struct virtio_device *vdev, unsigned int fbit) { unsigned int i; struct virtio_driver *drv = drv_to_virtio(vdev->dev.driver); for (i = 0; i < drv->feature_table_size; i++) if (drv->feature_table[i] == fbit) return; if (drv->feature_table_legacy) { for (i = 0; i < drv->feature_table_size_legacy; i++) if (drv->feature_table_legacy[i] == fbit) return; } BUG(); } EXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature); static void __virtio_config_changed(struct virtio_device *dev) { struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); if (!dev->config_enabled) dev->config_change_pending = true; else if (drv && drv->config_changed) drv->config_changed(dev); } void virtio_config_changed(struct virtio_device *dev) { unsigned long flags; spin_lock_irqsave(&dev->config_lock, flags); __virtio_config_changed(dev); spin_unlock_irqrestore(&dev->config_lock, flags); } EXPORT_SYMBOL_GPL(virtio_config_changed); void virtio_config_disable(struct virtio_device *dev) { dev->config_enabled = false; } EXPORT_SYMBOL_GPL(virtio_config_disable); void virtio_config_enable(struct virtio_device *dev) { dev->config_enabled = true; if (dev->config_change_pending) __virtio_config_changed(dev); dev->config_change_pending = false; } EXPORT_SYMBOL_GPL(virtio_config_enable); void virtio_add_status(struct virtio_device *dev, unsigned int status) { dev->config->set_status(dev, dev->config->get_status(dev) | status); } EXPORT_SYMBOL_GPL(virtio_add_status); int virtio_finalize_features(struct virtio_device *dev) { int ret = dev->config->finalize_features(dev); unsigned status; if (ret) return ret; ret = arch_has_restricted_virtio_memory_access(); if (ret) { if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) { dev_warn(&dev->dev, "device must provide VIRTIO_F_VERSION_1\n"); return -ENODEV; } if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) { dev_warn(&dev->dev, "device must provide VIRTIO_F_ACCESS_PLATFORM\n"); return -ENODEV; } } if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) return 0; virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); status = dev->config->get_status(dev); if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) { dev_err(&dev->dev, "virtio: device refuses features: %x\n", status); return -ENODEV; } return 0; } EXPORT_SYMBOL_GPL(virtio_finalize_features); int virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, struct virtqueue *vqs[]) { return vdev->config->find_vqs(vdev, nvqs, vqs); } EXPORT_SYMBOL_GPL(virtio_find_vqs); static int virtio_dev_probe(struct device *_d) { int err, i; struct virtio_device *dev = dev_to_virtio(_d); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); u64 device_features; u64 driver_features; u64 driver_features_legacy; /* We have a driver! */ virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER); /* Figure out what features the device supports. */ device_features = dev->config->get_features(dev); /* Figure out what features the driver supports. */ driver_features = 0; for (i = 0; i < drv->feature_table_size; i++) { unsigned int f = drv->feature_table[i]; BUG_ON(f >= 64); driver_features |= (1ULL << f); } /* Some drivers have a separate feature table for virtio v1.0 */ if (drv->feature_table_legacy) { driver_features_legacy = 0; for (i = 0; i < drv->feature_table_size_legacy; i++) { unsigned int f = drv->feature_table_legacy[i]; BUG_ON(f >= 64); driver_features_legacy |= (1ULL << f); } } else { driver_features_legacy = driver_features; } if (device_features & (1ULL << VIRTIO_F_VERSION_1)) dev->features = driver_features & device_features; else dev->features = driver_features_legacy & device_features; /* Transport features always preserved to pass to finalize_features. */ for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++) if (device_features & (1ULL << i)) __virtio_set_bit(dev, i); if (drv->validate) { err = drv->validate(dev); if (err) goto err; } err = virtio_finalize_features(dev); if (err) goto err; err = drv->probe(dev); if (err) goto err; /* If probe didn't do it, mark device DRIVER_OK ourselves. */ if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) virtio_device_ready(dev); if (drv->scan) drv->scan(dev); virtio_config_enable(dev); return 0; err: virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); return err; } static void virtio_dev_remove(struct device *_d) { struct virtio_device *dev = dev_to_virtio(_d); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); virtio_config_disable(dev); drv->remove(dev); WARN_ONCE(dev->config->get_status(dev), "Driver should have reset device"); /* Acknowledge the device's existence again. */ virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); } static struct bus_type virtio_bus = { .name = "virtio", .match = virtio_dev_match, .probe = virtio_dev_probe, .remove = virtio_dev_remove, }; int virtio_driver_register(struct virtio_driver *driver) { /* Catch this early. */ BUG_ON(driver->feature_table_size && !driver->feature_table); driver->driver.bus = &virtio_bus; return register_driver(&driver->driver); } EXPORT_SYMBOL_GPL(virtio_driver_register); /** * register_virtio_device - register virtio device * @dev : virtio device to be registered * * On error, the caller must call put_device on &@dev->dev (and not kfree), * as another code path may have obtained a reference to @dev. * * Returns: 0 on suceess, -error on failure */ int register_virtio_device(struct virtio_device *dev) { int err; dev->dev.bus = &virtio_bus; dev->dev.id = DEVICE_ID_DYNAMIC; dev->dev.name = "virtio"; spin_lock_init(&dev->config_lock); dev->config_enabled = false; dev->config_change_pending = false; /* We always start by resetting the device, in case a previous * driver messed it up. This also tests that code path a little. */ dev->config->reset(dev); /* Acknowledge that we've seen the device. */ virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE); INIT_LIST_HEAD(&dev->vqs); /* * register_device() causes the bus infrastructure to look for a matching * driver. */ err = register_device(&dev->dev); if (err) goto out; dev_add_param_uint32_ro(&dev->dev, "device", &dev->id.device, "0x%04x"); dev_add_param_uint32_ro(&dev->dev, "vendor", &dev->id.vendor, "0x%04x"); dev_add_param_uint32(&dev->dev, "status", param_set_readonly, status_show, &dev->status_param, "0x%08x", dev); virtio_dev_add_param_features(dev); out: if (err) virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED); return err; } EXPORT_SYMBOL_GPL(register_virtio_device); bool is_virtio_device(struct device *dev) { return dev->bus == &virtio_bus; } EXPORT_SYMBOL_GPL(is_virtio_device); void unregister_virtio_device(struct virtio_device *dev) { unregister_device(&dev->dev); } EXPORT_SYMBOL_GPL(unregister_virtio_device); static int virtio_init(void) { if (bus_register(&virtio_bus) != 0) panic("virtio bus registration failed"); return 0; } core_initcall(virtio_init); MODULE_LICENSE("GPL");