18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/virtio.h>
38c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
48c2ecf20Sopenharmony_ci#include <linux/virtio_config.h>
58c2ecf20Sopenharmony_ci#include <linux/module.h>
68c2ecf20Sopenharmony_ci#include <linux/idr.h>
78c2ecf20Sopenharmony_ci#include <uapi/linux/virtio_ids.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/* Unique numbering for virtio devices. */
108c2ecf20Sopenharmony_cistatic DEFINE_IDA(virtio_index_ida);
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic ssize_t device_show(struct device *_d,
138c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
168c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%04x\n", dev->id.device);
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(device);
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic ssize_t vendor_show(struct device *_d,
218c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
248c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%04x\n", dev->id.vendor);
258c2ecf20Sopenharmony_ci}
268c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(vendor);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic ssize_t status_show(struct device *_d,
298c2ecf20Sopenharmony_ci			   struct device_attribute *attr, char *buf)
308c2ecf20Sopenharmony_ci{
318c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
328c2ecf20Sopenharmony_ci	return sprintf(buf, "0x%08x\n", dev->config->get_status(dev));
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(status);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *_d,
378c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
408c2ecf20Sopenharmony_ci	return sprintf(buf, "virtio:d%08Xv%08X\n",
418c2ecf20Sopenharmony_ci		       dev->id.device, dev->id.vendor);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic ssize_t features_show(struct device *_d,
468c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
498c2ecf20Sopenharmony_ci	unsigned int i;
508c2ecf20Sopenharmony_ci	ssize_t len = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* We actually represent this as a bitstring, as it could be
538c2ecf20Sopenharmony_ci	 * arbitrary length in future. */
548c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(dev->features)*8; i++)
558c2ecf20Sopenharmony_ci		len += sprintf(buf+len, "%c",
568c2ecf20Sopenharmony_ci			       __virtio_test_bit(dev, i) ? '1' : '0');
578c2ecf20Sopenharmony_ci	len += sprintf(buf+len, "\n");
588c2ecf20Sopenharmony_ci	return len;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(features);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct attribute *virtio_dev_attrs[] = {
638c2ecf20Sopenharmony_ci	&dev_attr_device.attr,
648c2ecf20Sopenharmony_ci	&dev_attr_vendor.attr,
658c2ecf20Sopenharmony_ci	&dev_attr_status.attr,
668c2ecf20Sopenharmony_ci	&dev_attr_modalias.attr,
678c2ecf20Sopenharmony_ci	&dev_attr_features.attr,
688c2ecf20Sopenharmony_ci	NULL,
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(virtio_dev);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic inline int virtio_id_match(const struct virtio_device *dev,
738c2ecf20Sopenharmony_ci				  const struct virtio_device_id *id)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	if (id->device != dev->id.device && id->device != VIRTIO_DEV_ANY_ID)
768c2ecf20Sopenharmony_ci		return 0;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return id->vendor == VIRTIO_DEV_ANY_ID || id->vendor == dev->id.vendor;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/* This looks through all the IDs a driver claims to support.  If any of them
828c2ecf20Sopenharmony_ci * match, we return 1 and the kernel will call virtio_dev_probe(). */
838c2ecf20Sopenharmony_cistatic int virtio_dev_match(struct device *_dv, struct device_driver *_dr)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	unsigned int i;
868c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_dv);
878c2ecf20Sopenharmony_ci	const struct virtio_device_id *ids;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	ids = drv_to_virtio(_dr)->id_table;
908c2ecf20Sopenharmony_ci	for (i = 0; ids[i].device; i++)
918c2ecf20Sopenharmony_ci		if (virtio_id_match(dev, &ids[i]))
928c2ecf20Sopenharmony_ci			return 1;
938c2ecf20Sopenharmony_ci	return 0;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int virtio_uevent(struct device *_dv, struct kobj_uevent_env *env)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_dv);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X",
1018c2ecf20Sopenharmony_ci			      dev->id.device, dev->id.vendor);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_civoid virtio_check_driver_offered_feature(const struct virtio_device *vdev,
1058c2ecf20Sopenharmony_ci					 unsigned int fbit)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	unsigned int i;
1088c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(vdev->dev.driver);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	for (i = 0; i < drv->feature_table_size; i++)
1118c2ecf20Sopenharmony_ci		if (drv->feature_table[i] == fbit)
1128c2ecf20Sopenharmony_ci			return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (drv->feature_table_legacy) {
1158c2ecf20Sopenharmony_ci		for (i = 0; i < drv->feature_table_size_legacy; i++)
1168c2ecf20Sopenharmony_ci			if (drv->feature_table_legacy[i] == fbit)
1178c2ecf20Sopenharmony_ci				return;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	BUG();
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_check_driver_offered_feature);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic void __virtio_config_changed(struct virtio_device *dev)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (!dev->config_enabled)
1298c2ecf20Sopenharmony_ci		dev->config_change_pending = true;
1308c2ecf20Sopenharmony_ci	else if (drv && drv->config_changed)
1318c2ecf20Sopenharmony_ci		drv->config_changed(dev);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_civoid virtio_config_changed(struct virtio_device *dev)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	unsigned long flags;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	spin_lock_irqsave(&dev->config_lock, flags);
1398c2ecf20Sopenharmony_ci	__virtio_config_changed(dev);
1408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&dev->config_lock, flags);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_config_changed);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_civoid virtio_config_disable(struct virtio_device *dev)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->config_lock);
1478c2ecf20Sopenharmony_ci	dev->config_enabled = false;
1488c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->config_lock);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_config_disable);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid virtio_config_enable(struct virtio_device *dev)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	spin_lock_irq(&dev->config_lock);
1558c2ecf20Sopenharmony_ci	dev->config_enabled = true;
1568c2ecf20Sopenharmony_ci	if (dev->config_change_pending)
1578c2ecf20Sopenharmony_ci		__virtio_config_changed(dev);
1588c2ecf20Sopenharmony_ci	dev->config_change_pending = false;
1598c2ecf20Sopenharmony_ci	spin_unlock_irq(&dev->config_lock);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_config_enable);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_civoid virtio_add_status(struct virtio_device *dev, unsigned int status)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	might_sleep();
1668c2ecf20Sopenharmony_ci	dev->config->set_status(dev, dev->config->get_status(dev) | status);
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_add_status);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/* Do some validation, then set FEATURES_OK */
1718c2ecf20Sopenharmony_cistatic int virtio_features_ok(struct virtio_device *dev)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	unsigned status;
1748c2ecf20Sopenharmony_ci	int ret;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	might_sleep();
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	ret = arch_has_restricted_virtio_memory_access();
1798c2ecf20Sopenharmony_ci	if (ret) {
1808c2ecf20Sopenharmony_ci		if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) {
1818c2ecf20Sopenharmony_ci			dev_warn(&dev->dev,
1828c2ecf20Sopenharmony_ci				 "device must provide VIRTIO_F_VERSION_1\n");
1838c2ecf20Sopenharmony_ci			return -ENODEV;
1848c2ecf20Sopenharmony_ci		}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) {
1878c2ecf20Sopenharmony_ci			dev_warn(&dev->dev,
1888c2ecf20Sopenharmony_ci				 "device must provide VIRTIO_F_ACCESS_PLATFORM\n");
1898c2ecf20Sopenharmony_ci			return -ENODEV;
1908c2ecf20Sopenharmony_ci		}
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1))
1948c2ecf20Sopenharmony_ci		return 0;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK);
1978c2ecf20Sopenharmony_ci	status = dev->config->get_status(dev);
1988c2ecf20Sopenharmony_ci	if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
1998c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "virtio: device refuses features: %x\n",
2008c2ecf20Sopenharmony_ci			status);
2018c2ecf20Sopenharmony_ci		return -ENODEV;
2028c2ecf20Sopenharmony_ci	}
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int virtio_dev_probe(struct device *_d)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	int err, i;
2098c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
2108c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
2118c2ecf20Sopenharmony_ci	u64 device_features;
2128c2ecf20Sopenharmony_ci	u64 driver_features;
2138c2ecf20Sopenharmony_ci	u64 driver_features_legacy;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* We have a driver! */
2168c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* Figure out what features the device supports. */
2198c2ecf20Sopenharmony_ci	device_features = dev->config->get_features(dev);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/* Figure out what features the driver supports. */
2228c2ecf20Sopenharmony_ci	driver_features = 0;
2238c2ecf20Sopenharmony_ci	for (i = 0; i < drv->feature_table_size; i++) {
2248c2ecf20Sopenharmony_ci		unsigned int f = drv->feature_table[i];
2258c2ecf20Sopenharmony_ci		BUG_ON(f >= 64);
2268c2ecf20Sopenharmony_ci		driver_features |= (1ULL << f);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/* Some drivers have a separate feature table for virtio v1.0 */
2308c2ecf20Sopenharmony_ci	if (drv->feature_table_legacy) {
2318c2ecf20Sopenharmony_ci		driver_features_legacy = 0;
2328c2ecf20Sopenharmony_ci		for (i = 0; i < drv->feature_table_size_legacy; i++) {
2338c2ecf20Sopenharmony_ci			unsigned int f = drv->feature_table_legacy[i];
2348c2ecf20Sopenharmony_ci			BUG_ON(f >= 64);
2358c2ecf20Sopenharmony_ci			driver_features_legacy |= (1ULL << f);
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		driver_features_legacy = driver_features;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (device_features & (1ULL << VIRTIO_F_VERSION_1))
2428c2ecf20Sopenharmony_ci		dev->features = driver_features & device_features;
2438c2ecf20Sopenharmony_ci	else
2448c2ecf20Sopenharmony_ci		dev->features = driver_features_legacy & device_features;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	/* Transport features always preserved to pass to finalize_features. */
2478c2ecf20Sopenharmony_ci	for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
2488c2ecf20Sopenharmony_ci		if (device_features & (1ULL << i))
2498c2ecf20Sopenharmony_ci			__virtio_set_bit(dev, i);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	err = dev->config->finalize_features(dev);
2528c2ecf20Sopenharmony_ci	if (err)
2538c2ecf20Sopenharmony_ci		goto err;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (drv->validate) {
2568c2ecf20Sopenharmony_ci		u64 features = dev->features;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		err = drv->validate(dev);
2598c2ecf20Sopenharmony_ci		if (err)
2608c2ecf20Sopenharmony_ci			goto err;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		/* Did validation change any features? Then write them again. */
2638c2ecf20Sopenharmony_ci		if (features != dev->features) {
2648c2ecf20Sopenharmony_ci			err = dev->config->finalize_features(dev);
2658c2ecf20Sopenharmony_ci			if (err)
2668c2ecf20Sopenharmony_ci				goto err;
2678c2ecf20Sopenharmony_ci		}
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	err = virtio_features_ok(dev);
2718c2ecf20Sopenharmony_ci	if (err)
2728c2ecf20Sopenharmony_ci		goto err;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	err = drv->probe(dev);
2758c2ecf20Sopenharmony_ci	if (err)
2768c2ecf20Sopenharmony_ci		goto err;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/* If probe didn't do it, mark device DRIVER_OK ourselves. */
2798c2ecf20Sopenharmony_ci	if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
2808c2ecf20Sopenharmony_ci		virtio_device_ready(dev);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (drv->scan)
2838c2ecf20Sopenharmony_ci		drv->scan(dev);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	virtio_config_enable(dev);
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return 0;
2888c2ecf20Sopenharmony_cierr:
2898c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
2908c2ecf20Sopenharmony_ci	return err;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistatic int virtio_dev_remove(struct device *_d)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct virtio_device *dev = dev_to_virtio(_d);
2978c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	virtio_config_disable(dev);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	drv->remove(dev);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/* Driver should have reset device. */
3048c2ecf20Sopenharmony_ci	WARN_ON_ONCE(dev->config->get_status(dev));
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Acknowledge the device's existence again. */
3078c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic struct bus_type virtio_bus = {
3128c2ecf20Sopenharmony_ci	.name  = "virtio",
3138c2ecf20Sopenharmony_ci	.match = virtio_dev_match,
3148c2ecf20Sopenharmony_ci	.dev_groups = virtio_dev_groups,
3158c2ecf20Sopenharmony_ci	.uevent = virtio_uevent,
3168c2ecf20Sopenharmony_ci	.probe = virtio_dev_probe,
3178c2ecf20Sopenharmony_ci	.remove = virtio_dev_remove,
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ciint register_virtio_driver(struct virtio_driver *driver)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	/* Catch this early. */
3238c2ecf20Sopenharmony_ci	BUG_ON(driver->feature_table_size && !driver->feature_table);
3248c2ecf20Sopenharmony_ci	driver->driver.bus = &virtio_bus;
3258c2ecf20Sopenharmony_ci	return driver_register(&driver->driver);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_virtio_driver);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_civoid unregister_virtio_driver(struct virtio_driver *driver)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	driver_unregister(&driver->driver);
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_virtio_driver);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci/**
3368c2ecf20Sopenharmony_ci * register_virtio_device - register virtio device
3378c2ecf20Sopenharmony_ci * @dev        : virtio device to be registered
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * On error, the caller must call put_device on &@dev->dev (and not kfree),
3408c2ecf20Sopenharmony_ci * as another code path may have obtained a reference to @dev.
3418c2ecf20Sopenharmony_ci *
3428c2ecf20Sopenharmony_ci * Returns: 0 on suceess, -error on failure
3438c2ecf20Sopenharmony_ci */
3448c2ecf20Sopenharmony_ciint register_virtio_device(struct virtio_device *dev)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	int err;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	dev->dev.bus = &virtio_bus;
3498c2ecf20Sopenharmony_ci	device_initialize(&dev->dev);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Assign a unique device index and hence name. */
3528c2ecf20Sopenharmony_ci	err = ida_simple_get(&virtio_index_ida, 0, 0, GFP_KERNEL);
3538c2ecf20Sopenharmony_ci	if (err < 0)
3548c2ecf20Sopenharmony_ci		goto out;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	dev->index = err;
3578c2ecf20Sopenharmony_ci	dev_set_name(&dev->dev, "virtio%u", dev->index);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	spin_lock_init(&dev->config_lock);
3608c2ecf20Sopenharmony_ci	dev->config_enabled = false;
3618c2ecf20Sopenharmony_ci	dev->config_change_pending = false;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* We always start by resetting the device, in case a previous
3648c2ecf20Sopenharmony_ci	 * driver messed it up.  This also tests that code path a little. */
3658c2ecf20Sopenharmony_ci	dev->config->reset(dev);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/* Acknowledge that we've seen the device. */
3688c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&dev->vqs);
3718c2ecf20Sopenharmony_ci	spin_lock_init(&dev->vqs_list_lock);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	/*
3748c2ecf20Sopenharmony_ci	 * device_add() causes the bus infrastructure to look for a matching
3758c2ecf20Sopenharmony_ci	 * driver.
3768c2ecf20Sopenharmony_ci	 */
3778c2ecf20Sopenharmony_ci	err = device_add(&dev->dev);
3788c2ecf20Sopenharmony_ci	if (err)
3798c2ecf20Sopenharmony_ci		ida_simple_remove(&virtio_index_ida, dev->index);
3808c2ecf20Sopenharmony_ciout:
3818c2ecf20Sopenharmony_ci	if (err)
3828c2ecf20Sopenharmony_ci		virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
3838c2ecf20Sopenharmony_ci	return err;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_virtio_device);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cibool is_virtio_device(struct device *dev)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	return dev->bus == &virtio_bus;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(is_virtio_device);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_civoid unregister_virtio_device(struct virtio_device *dev)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	int index = dev->index; /* save for after device release */
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	device_unregister(&dev->dev);
3988c2ecf20Sopenharmony_ci	ida_simple_remove(&virtio_index_ida, index);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_virtio_device);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4038c2ecf20Sopenharmony_ciint virtio_device_freeze(struct virtio_device *dev)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	virtio_config_disable(dev);
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (drv && drv->freeze)
4128c2ecf20Sopenharmony_ci		return drv->freeze(dev);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_device_freeze);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ciint virtio_device_restore(struct virtio_device *dev)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
4218c2ecf20Sopenharmony_ci	int ret;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	/* We always start by resetting the device, in case a previous
4248c2ecf20Sopenharmony_ci	 * driver messed it up. */
4258c2ecf20Sopenharmony_ci	dev->config->reset(dev);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	/* Acknowledge that we've seen the device. */
4288c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/* Maybe driver failed before freeze.
4318c2ecf20Sopenharmony_ci	 * Restore the failed status, for debugging. */
4328c2ecf20Sopenharmony_ci	if (dev->failed)
4338c2ecf20Sopenharmony_ci		virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (!drv)
4368c2ecf20Sopenharmony_ci		return 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* We have a driver! */
4398c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	ret = dev->config->finalize_features(dev);
4428c2ecf20Sopenharmony_ci	if (ret)
4438c2ecf20Sopenharmony_ci		goto err;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	ret = virtio_features_ok(dev);
4468c2ecf20Sopenharmony_ci	if (ret)
4478c2ecf20Sopenharmony_ci		goto err;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (drv->restore) {
4508c2ecf20Sopenharmony_ci		ret = drv->restore(dev);
4518c2ecf20Sopenharmony_ci		if (ret)
4528c2ecf20Sopenharmony_ci			goto err;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* Finally, tell the device we're all set */
4568c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	virtio_config_enable(dev);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	return 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cierr:
4638c2ecf20Sopenharmony_ci	virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
4648c2ecf20Sopenharmony_ci	return ret;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(virtio_device_restore);
4678c2ecf20Sopenharmony_ci#endif
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic int virtio_init(void)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	if (bus_register(&virtio_bus) != 0)
4728c2ecf20Sopenharmony_ci		panic("virtio bus registration failed");
4738c2ecf20Sopenharmony_ci	return 0;
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic void __exit virtio_exit(void)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	bus_unregister(&virtio_bus);
4798c2ecf20Sopenharmony_ci	ida_destroy(&virtio_index_ida);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_cicore_initcall(virtio_init);
4828c2ecf20Sopenharmony_cimodule_exit(virtio_exit);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
485