| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | #include <linux/virtio.h> |
|---|
| 2 | 3 | #include <linux/spinlock.h> |
|---|
| 3 | 4 | #include <linux/virtio_config.h> |
|---|
| .. | .. |
|---|
| 161 | 162 | |
|---|
| 162 | 163 | void virtio_add_status(struct virtio_device *dev, unsigned int status) |
|---|
| 163 | 164 | { |
|---|
| 165 | + might_sleep(); |
|---|
| 164 | 166 | dev->config->set_status(dev, dev->config->get_status(dev) | status); |
|---|
| 165 | 167 | } |
|---|
| 166 | 168 | EXPORT_SYMBOL_GPL(virtio_add_status); |
|---|
| 167 | 169 | |
|---|
| 168 | | -int virtio_finalize_features(struct virtio_device *dev) |
|---|
| 170 | +/* Do some validation, then set FEATURES_OK */ |
|---|
| 171 | +static int virtio_features_ok(struct virtio_device *dev) |
|---|
| 169 | 172 | { |
|---|
| 170 | | - int ret = dev->config->finalize_features(dev); |
|---|
| 171 | 173 | unsigned status; |
|---|
| 174 | + int ret; |
|---|
| 172 | 175 | |
|---|
| 173 | | - if (ret) |
|---|
| 174 | | - return ret; |
|---|
| 176 | + might_sleep(); |
|---|
| 177 | + |
|---|
| 178 | + ret = arch_has_restricted_virtio_memory_access(); |
|---|
| 179 | + if (ret) { |
|---|
| 180 | + if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) { |
|---|
| 181 | + dev_warn(&dev->dev, |
|---|
| 182 | + "device must provide VIRTIO_F_VERSION_1\n"); |
|---|
| 183 | + return -ENODEV; |
|---|
| 184 | + } |
|---|
| 185 | + |
|---|
| 186 | + if (!virtio_has_feature(dev, VIRTIO_F_ACCESS_PLATFORM)) { |
|---|
| 187 | + dev_warn(&dev->dev, |
|---|
| 188 | + "device must provide VIRTIO_F_ACCESS_PLATFORM\n"); |
|---|
| 189 | + return -ENODEV; |
|---|
| 190 | + } |
|---|
| 191 | + } |
|---|
| 175 | 192 | |
|---|
| 176 | 193 | if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) |
|---|
| 177 | 194 | return 0; |
|---|
| .. | .. |
|---|
| 185 | 202 | } |
|---|
| 186 | 203 | return 0; |
|---|
| 187 | 204 | } |
|---|
| 188 | | -EXPORT_SYMBOL_GPL(virtio_finalize_features); |
|---|
| 189 | 205 | |
|---|
| 190 | 206 | static int virtio_dev_probe(struct device *_d) |
|---|
| 191 | 207 | { |
|---|
| .. | .. |
|---|
| 222 | 238 | driver_features_legacy = driver_features; |
|---|
| 223 | 239 | } |
|---|
| 224 | 240 | |
|---|
| 225 | | - /* |
|---|
| 226 | | - * Some devices detect legacy solely via F_VERSION_1. Write |
|---|
| 227 | | - * F_VERSION_1 to force LE config space accesses before FEATURES_OK for |
|---|
| 228 | | - * these when needed. |
|---|
| 229 | | - */ |
|---|
| 230 | | - if (drv->validate && !virtio_legacy_is_little_endian() |
|---|
| 231 | | - && device_features & BIT_ULL(VIRTIO_F_VERSION_1)) { |
|---|
| 232 | | - dev->features = BIT_ULL(VIRTIO_F_VERSION_1); |
|---|
| 233 | | - dev->config->finalize_features(dev); |
|---|
| 234 | | - } |
|---|
| 235 | | - |
|---|
| 236 | 241 | if (device_features & (1ULL << VIRTIO_F_VERSION_1)) |
|---|
| 237 | 242 | dev->features = driver_features & device_features; |
|---|
| 238 | 243 | else |
|---|
| .. | .. |
|---|
| 243 | 248 | if (device_features & (1ULL << i)) |
|---|
| 244 | 249 | __virtio_set_bit(dev, i); |
|---|
| 245 | 250 | |
|---|
| 251 | + err = dev->config->finalize_features(dev); |
|---|
| 252 | + if (err) |
|---|
| 253 | + goto err; |
|---|
| 254 | + |
|---|
| 246 | 255 | if (drv->validate) { |
|---|
| 256 | + u64 features = dev->features; |
|---|
| 257 | + |
|---|
| 247 | 258 | err = drv->validate(dev); |
|---|
| 248 | 259 | if (err) |
|---|
| 249 | 260 | goto err; |
|---|
| 261 | + |
|---|
| 262 | + /* Did validation change any features? Then write them again. */ |
|---|
| 263 | + if (features != dev->features) { |
|---|
| 264 | + err = dev->config->finalize_features(dev); |
|---|
| 265 | + if (err) |
|---|
| 266 | + goto err; |
|---|
| 267 | + } |
|---|
| 250 | 268 | } |
|---|
| 251 | 269 | |
|---|
| 252 | | - err = virtio_finalize_features(dev); |
|---|
| 270 | + err = virtio_features_ok(dev); |
|---|
| 253 | 271 | if (err) |
|---|
| 254 | 272 | goto err; |
|---|
| 255 | 273 | |
|---|
| .. | .. |
|---|
| 365 | 383 | } |
|---|
| 366 | 384 | EXPORT_SYMBOL_GPL(register_virtio_device); |
|---|
| 367 | 385 | |
|---|
| 386 | +bool is_virtio_device(struct device *dev) |
|---|
| 387 | +{ |
|---|
| 388 | + return dev->bus == &virtio_bus; |
|---|
| 389 | +} |
|---|
| 390 | +EXPORT_SYMBOL_GPL(is_virtio_device); |
|---|
| 391 | + |
|---|
| 368 | 392 | void unregister_virtio_device(struct virtio_device *dev) |
|---|
| 369 | 393 | { |
|---|
| 370 | 394 | int index = dev->index; /* save for after device release */ |
|---|
| .. | .. |
|---|
| 395 | 419 | struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); |
|---|
| 396 | 420 | int ret; |
|---|
| 397 | 421 | |
|---|
| 422 | + /* Short path for stateful devices. Here we assume that if the device |
|---|
| 423 | + * does not have a freeze callback, its state was not changed when |
|---|
| 424 | + * suspended. |
|---|
| 425 | + */ |
|---|
| 426 | + if (drv && !drv->freeze) |
|---|
| 427 | + goto on_config_enable; |
|---|
| 428 | + |
|---|
| 398 | 429 | /* We always start by resetting the device, in case a previous |
|---|
| 399 | 430 | * driver messed it up. */ |
|---|
| 400 | 431 | dev->config->reset(dev); |
|---|
| .. | .. |
|---|
| 413 | 444 | /* We have a driver! */ |
|---|
| 414 | 445 | virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER); |
|---|
| 415 | 446 | |
|---|
| 416 | | - ret = virtio_finalize_features(dev); |
|---|
| 447 | + ret = dev->config->finalize_features(dev); |
|---|
| 448 | + if (ret) |
|---|
| 449 | + goto err; |
|---|
| 450 | + |
|---|
| 451 | + ret = virtio_features_ok(dev); |
|---|
| 417 | 452 | if (ret) |
|---|
| 418 | 453 | goto err; |
|---|
| 419 | 454 | |
|---|
| .. | .. |
|---|
| 426 | 461 | /* Finally, tell the device we're all set */ |
|---|
| 427 | 462 | virtio_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); |
|---|
| 428 | 463 | |
|---|
| 464 | +on_config_enable: |
|---|
| 429 | 465 | virtio_config_enable(dev); |
|---|
| 430 | 466 | |
|---|
| 431 | 467 | return 0; |
|---|