uboot/drivers/virtio/virtio-uclass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
   4 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
   5 *
   6 * VirtIO is a virtualization standard for network and disk device drivers
   7 * where just the guest's device driver "knows" it is running in a virtual
   8 * environment, and cooperates with the hypervisor. This enables guests to
   9 * get high performance network and disk operations, and gives most of the
  10 * performance benefits of paravirtualization. In the U-Boot case, the guest
  11 * is U-Boot itself, while the virtual environment are normally QEMU targets
  12 * like ARM, RISC-V and x86.
  13 *
  14 * See http://docs.oasis-open.org/virtio/virtio/v1.0/virtio-v1.0.pdf for
  15 * the VirtIO specification v1.0.
  16 */
  17
  18#include <common.h>
  19#include <dm.h>
  20#include <virtio_types.h>
  21#include <virtio.h>
  22#include <dm/lists.h>
  23
  24static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
  25        [VIRTIO_ID_NET]         = VIRTIO_NET_DRV_NAME,
  26        [VIRTIO_ID_BLOCK]       = VIRTIO_BLK_DRV_NAME,
  27};
  28
  29int virtio_get_config(struct udevice *vdev, unsigned int offset,
  30                      void *buf, unsigned int len)
  31{
  32        struct dm_virtio_ops *ops;
  33
  34        ops = virtio_get_ops(vdev->parent);
  35
  36        return ops->get_config(vdev->parent, offset, buf, len);
  37}
  38
  39int virtio_set_config(struct udevice *vdev, unsigned int offset,
  40                      void *buf, unsigned int len)
  41{
  42        struct dm_virtio_ops *ops;
  43
  44        ops = virtio_get_ops(vdev->parent);
  45
  46        return ops->set_config(vdev->parent, offset, buf, len);
  47}
  48
  49int virtio_generation(struct udevice *vdev, u32 *counter)
  50{
  51        struct dm_virtio_ops *ops;
  52
  53        ops = virtio_get_ops(vdev->parent);
  54        if (!ops->generation)
  55                return -ENOSYS;
  56
  57        return ops->generation(vdev->parent, counter);
  58}
  59
  60int virtio_get_status(struct udevice *vdev, u8 *status)
  61{
  62        struct dm_virtio_ops *ops;
  63
  64        ops = virtio_get_ops(vdev->parent);
  65
  66        return ops->get_status(vdev->parent, status);
  67}
  68
  69int virtio_set_status(struct udevice *vdev, u8 status)
  70{
  71        struct dm_virtio_ops *ops;
  72
  73        ops = virtio_get_ops(vdev->parent);
  74
  75        return ops->set_status(vdev->parent, status);
  76}
  77
  78int virtio_reset(struct udevice *vdev)
  79{
  80        struct dm_virtio_ops *ops;
  81
  82        ops = virtio_get_ops(vdev->parent);
  83
  84        return ops->reset(vdev->parent);
  85}
  86
  87int virtio_get_features(struct udevice *vdev, u64 *features)
  88{
  89        struct dm_virtio_ops *ops;
  90
  91        ops = virtio_get_ops(vdev->parent);
  92
  93        return ops->get_features(vdev->parent, features);
  94}
  95
  96int virtio_set_features(struct udevice *vdev)
  97{
  98        struct dm_virtio_ops *ops;
  99
 100        ops = virtio_get_ops(vdev->parent);
 101
 102        return ops->set_features(vdev->parent);
 103}
 104
 105int virtio_find_vqs(struct udevice *vdev, unsigned int nvqs,
 106                    struct virtqueue *vqs[])
 107{
 108        struct dm_virtio_ops *ops;
 109
 110        ops = virtio_get_ops(vdev->parent);
 111
 112        return ops->find_vqs(vdev->parent, nvqs, vqs);
 113}
 114
 115int virtio_del_vqs(struct udevice *vdev)
 116{
 117        struct dm_virtio_ops *ops;
 118
 119        ops = virtio_get_ops(vdev->parent);
 120
 121        return ops->del_vqs(vdev->parent);
 122}
 123
 124int virtio_notify(struct udevice *vdev, struct virtqueue *vq)
 125{
 126        struct dm_virtio_ops *ops;
 127
 128        ops = virtio_get_ops(vdev->parent);
 129
 130        return ops->notify(vdev->parent, vq);
 131}
 132
 133void virtio_add_status(struct udevice *vdev, u8 status)
 134{
 135        u8 old;
 136
 137        if (!virtio_get_status(vdev, &old))
 138                virtio_set_status(vdev, old | status);
 139}
 140
 141int virtio_finalize_features(struct udevice *vdev)
 142{
 143        struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
 144        u8 status;
 145        int ret;
 146
 147        ret = virtio_set_features(vdev);
 148        if (ret)
 149                return ret;
 150
 151        if (uc_priv->legacy)
 152                return 0;
 153
 154        virtio_add_status(vdev, VIRTIO_CONFIG_S_FEATURES_OK);
 155        ret = virtio_get_status(vdev, &status);
 156        if (ret)
 157                return ret;
 158        if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
 159                debug("(%s): device refuses features %x\n", vdev->name, status);
 160                return -ENODEV;
 161        }
 162
 163        return 0;
 164}
 165
 166void virtio_driver_features_init(struct virtio_dev_priv *priv,
 167                                 const u32 *feature,
 168                                 u32 feature_size,
 169                                 const u32 *feature_legacy,
 170                                 u32 feature_legacy_size)
 171{
 172        priv->feature_table = feature;
 173        priv->feature_table_size = feature_size;
 174        priv->feature_table_legacy = feature_legacy;
 175        priv->feature_table_size_legacy = feature_legacy_size;
 176}
 177
 178int virtio_init(void)
 179{
 180        struct udevice *bus;
 181        int ret;
 182
 183        /* Enumerate all known virtio devices */
 184        ret = uclass_first_device(UCLASS_VIRTIO, &bus);
 185        if (ret)
 186                return ret;
 187
 188        while (bus) {
 189                ret = uclass_next_device(&bus);
 190                if (ret)
 191                        break;
 192        }
 193
 194        return ret;
 195}
 196
 197static int virtio_uclass_pre_probe(struct udevice *udev)
 198{
 199        struct dm_virtio_ops *ops;
 200
 201        ops = (struct dm_virtio_ops *)(udev->driver->ops);
 202
 203        /*
 204         * Check virtio transport driver ops here so that we don't need
 205         * check these ops each time when the virtio_xxx APIs are called.
 206         *
 207         * Only generation op is optional. All other ops are must-have.
 208         */
 209        if (!ops->get_config || !ops->set_config ||
 210            !ops->get_status || !ops->set_status ||
 211            !ops->get_features || !ops->set_features ||
 212            !ops->find_vqs || !ops->del_vqs ||
 213            !ops->reset || !ops->notify)
 214                return -ENOENT;
 215
 216        return 0;
 217}
 218
 219static int virtio_uclass_post_probe(struct udevice *udev)
 220{
 221        struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
 222        char dev_name[30], *str;
 223        struct udevice *vdev;
 224        int ret;
 225
 226        if (uc_priv->device > VIRTIO_ID_MAX_NUM) {
 227                debug("(%s): virtio device ID %d exceeds maximum num\n",
 228                      udev->name, uc_priv->device);
 229                return 0;
 230        }
 231
 232        if (!virtio_drv_name[uc_priv->device]) {
 233                debug("(%s): underlying virtio device driver unavailable\n",
 234                      udev->name);
 235                return 0;
 236        }
 237
 238        snprintf(dev_name, sizeof(dev_name), "%s#%d",
 239                 virtio_drv_name[uc_priv->device], udev->seq);
 240        str = strdup(dev_name);
 241        if (!str)
 242                return -ENOMEM;
 243
 244        ret = device_bind_driver(udev, virtio_drv_name[uc_priv->device],
 245                                 str, &vdev);
 246        if (ret == -ENOENT) {
 247                debug("(%s): no driver configured\n", udev->name);
 248                return 0;
 249        }
 250        if (ret) {
 251                free(str);
 252                return ret;
 253        }
 254        device_set_name_alloced(vdev);
 255
 256        INIT_LIST_HEAD(&uc_priv->vqs);
 257
 258        return 0;
 259}
 260
 261static int virtio_uclass_child_post_bind(struct udevice *vdev)
 262{
 263        /* Acknowledge that we've seen the device */
 264        virtio_add_status(vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE);
 265
 266        return 0;
 267}
 268
 269static int virtio_uclass_child_pre_probe(struct udevice *vdev)
 270{
 271        struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(vdev->parent);
 272        u64 device_features;
 273        u64 driver_features;
 274        u64 driver_features_legacy;
 275        int i;
 276        int ret;
 277
 278        /*
 279         * Save the real virtio device (eg: virtio-net, virtio-blk) to
 280         * the transport (parent) device's uclass priv for future use.
 281         */
 282        uc_priv->vdev = vdev;
 283
 284        /*
 285         * We always start by resetting the device, in case a previous driver
 286         * messed it up. This also tests that code path a little.
 287         */
 288        ret = virtio_reset(vdev);
 289        if (ret)
 290                goto err;
 291
 292        /* We have a driver! */
 293        virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER);
 294
 295        /* Figure out what features the device supports */
 296        virtio_get_features(vdev, &device_features);
 297        debug("(%s) plain device features supported %016llx\n",
 298              vdev->name, device_features);
 299        if (!(device_features & (1ULL << VIRTIO_F_VERSION_1)))
 300                uc_priv->legacy = true;
 301
 302        /* Figure out what features the driver supports */
 303        driver_features = 0;
 304        for (i = 0; i < uc_priv->feature_table_size; i++) {
 305                unsigned int f = uc_priv->feature_table[i];
 306
 307                WARN_ON(f >= 64);
 308                driver_features |= (1ULL << f);
 309        }
 310
 311        /* Some drivers have a separate feature table for virtio v1.0 */
 312        if (uc_priv->feature_table_legacy) {
 313                driver_features_legacy = 0;
 314                for (i = 0; i < uc_priv->feature_table_size_legacy; i++) {
 315                        unsigned int f = uc_priv->feature_table_legacy[i];
 316
 317                        WARN_ON(f >= 64);
 318                        driver_features_legacy |= (1ULL << f);
 319                }
 320        } else {
 321                driver_features_legacy = driver_features;
 322        }
 323
 324        if (uc_priv->legacy) {
 325                debug("(%s): legacy virtio device\n", vdev->name);
 326                uc_priv->features = driver_features_legacy & device_features;
 327        } else {
 328                debug("(%s): v1.0 complaint virtio device\n", vdev->name);
 329                uc_priv->features = driver_features & device_features;
 330        }
 331
 332        /* Transport features always preserved to pass to finalize_features */
 333        for (i = VIRTIO_TRANSPORT_F_START; i < VIRTIO_TRANSPORT_F_END; i++)
 334                if ((device_features & (1ULL << i)) &&
 335                    (i == VIRTIO_F_VERSION_1))
 336                        __virtio_set_bit(vdev->parent, i);
 337
 338        debug("(%s) final negotiated features supported %016llx\n",
 339              vdev->name, uc_priv->features);
 340        ret = virtio_finalize_features(vdev);
 341        if (ret)
 342                goto err;
 343
 344        return 0;
 345
 346err:
 347        virtio_add_status(vdev, VIRTIO_CONFIG_S_FAILED);
 348        return ret;
 349}
 350
 351static int virtio_uclass_child_post_probe(struct udevice *vdev)
 352{
 353        /* Indicates that the driver is set up and ready to drive the device */
 354        virtio_add_status(vdev, VIRTIO_CONFIG_S_DRIVER_OK);
 355
 356        return 0;
 357}
 358
 359UCLASS_DRIVER(virtio) = {
 360        .name   = "virtio",
 361        .id     = UCLASS_VIRTIO,
 362        .flags  = DM_UC_FLAG_SEQ_ALIAS,
 363        .pre_probe = virtio_uclass_pre_probe,
 364        .post_probe = virtio_uclass_post_probe,
 365        .child_post_bind = virtio_uclass_child_post_bind,
 366        .child_pre_probe = virtio_uclass_child_pre_probe,
 367        .child_post_probe = virtio_uclass_child_post_probe,
 368        .per_device_auto_alloc_size = sizeof(struct virtio_dev_priv),
 369};
 370