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