virtio_mmio defines the config space to be little-endian. This means
that a big-endian guest has to perform the access with byte-swapping
accessors.

The config space accessors are changed to take an "access_size"
parameter, allowing the low-level code to use the correct primitives.
Drivers and transports are updated to use the modified API. Only
virtio_mmio is actually changed to do something different.

Cc: Rusty Russell <[email protected]>
Cc: Michael S. Tsirkin <[email protected]>
Cc: Pawel Moll <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
---
 drivers/block/virtio_blk.c             |  6 +--
 drivers/char/virtio_console.c          |  4 +-
 drivers/lguest/lguest_device.c         |  8 +++-
 drivers/net/caif/caif_virtio.c         |  2 +-
 drivers/net/virtio_net.c               |  2 +-
 drivers/remoteproc/remoteproc_virtio.c |  8 +++-
 drivers/s390/kvm/kvm_virtio.c          |  8 +++-
 drivers/s390/kvm/virtio_ccw.c          |  9 +++-
 drivers/scsi/virtio_scsi.c             |  4 +-
 drivers/virtio/virtio_balloon.c        |  8 ++--
 drivers/virtio/virtio_mmio.c           | 86 ++++++++++++++++++++++++++++++----
 drivers/virtio/virtio_pci.c            |  8 +++-
 include/linux/virtio_config.h          | 19 ++++----
 net/9p/trans_virtio.c                  |  4 +-
 14 files changed, 133 insertions(+), 43 deletions(-)

diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 5cdf88b..9eadf52 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -530,7 +530,7 @@ static void virtblk_config_changed_work(struct work_struct 
*work)
 
        /* Host must always specify the capacity. */
        vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
-                         &capacity, sizeof(capacity));
+                         &capacity, 1, sizeof(capacity));
 
        /* If capacity is too big, truncate with warning. */
        if ((sector_t)capacity != capacity) {
@@ -655,7 +655,7 @@ virtblk_cache_type_store(struct device *dev, struct 
device_attribute *attr,
        writeback = i;
        vdev->config->set(vdev,
                          offsetof(struct virtio_blk_config, wce),
-                         &writeback, sizeof(writeback));
+                         &writeback, 1, sizeof(writeback));
 
        virtblk_update_cache_mode(vdev);
        return count;
@@ -773,7 +773,7 @@ static int virtblk_probe(struct virtio_device *vdev)
 
        /* Host must always specify the capacity. */
        vdev->config->get(vdev, offsetof(struct virtio_blk_config, capacity),
-                         &cap, sizeof(cap));
+                         &cap, 1, sizeof(cap));
 
        /* If capacity is too big, truncate with warning. */
        if ((sector_t)cap != cap) {
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index b79cf3e..5ca3eb1 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1839,10 +1839,10 @@ static void config_intr(struct virtio_device *vdev)
 
                vdev->config->get(vdev,
                                  offsetof(struct virtio_console_config, cols),
-                                 &cols, sizeof(u16));
+                                 &cols, 1, sizeof(u16));
                vdev->config->get(vdev,
                                  offsetof(struct virtio_console_config, rows),
-                                 &rows, sizeof(u16));
+                                 &rows, 1, sizeof(u16));
 
                port = find_port_by_id(portdev, 0);
                set_console_size(port, rows, cols);
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index b3256ff..5858a9b 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -154,10 +154,12 @@ static void lg_finalize_features(struct virtio_device 
*vdev)
 
 /* Once they've found a field, getting a copy of it is easy. */
 static void lg_get(struct virtio_device *vdev, unsigned int offset,
-                  void *buf, unsigned len)
+                  void *buf, unsigned len, unsigned access_size)
 {
        struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
 
+       len *= access_size;
+
        /* Check they didn't ask for more than the length of the config! */
        BUG_ON(offset + len > desc->config_len);
        memcpy(buf, lg_config(desc) + offset, len);
@@ -165,10 +167,12 @@ static void lg_get(struct virtio_device *vdev, unsigned 
int offset,
 
 /* Setting the contents is also trivial. */
 static void lg_set(struct virtio_device *vdev, unsigned int offset,
-                  const void *buf, unsigned len)
+                  const void *buf, unsigned len, unsigned access_size)
 {
        struct lguest_device_desc *desc = to_lgdev(vdev)->desc;
 
+       len *= access_size;
+
        /* Check they didn't ask for more than the length of the config! */
        BUG_ON(offset + len > desc->config_len);
        memcpy(lg_config(desc) + offset, buf, len);
diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c
index b9ed128..5ace13d 100644
--- a/drivers/net/caif/caif_virtio.c
+++ b/drivers/net/caif/caif_virtio.c
@@ -689,7 +689,7 @@ static int cfv_probe(struct virtio_device *vdev)
 #define GET_VIRTIO_CONFIG_OPS(_v, _var, _f) \
        ((_v)->config->get(_v, offsetof(struct virtio_caif_transf_config, _f), \
                           &_var, \
-                          FIELD_SIZEOF(struct virtio_caif_transf_config, _f)))
+                          1, FIELD_SIZEOF(struct virtio_caif_transf_config, 
_f)))
 
        if (vdev->config->get) {
                GET_VIRTIO_CONFIG_OPS(vdev, cfv->tx_hr, headroom);
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index defec2b..54e9a22 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -853,7 +853,7 @@ static int virtnet_set_mac_address(struct net_device *dev, 
void *p)
                }
        } else if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) {
                vdev->config->set(vdev, offsetof(struct virtio_net_config, mac),
-                                 addr->sa_data, dev->addr_len);
+                                 addr->sa_data, dev->addr_len, 1);
        }
 
        eth_commit_mac_addr_change(dev, p);
diff --git a/drivers/remoteproc/remoteproc_virtio.c 
b/drivers/remoteproc/remoteproc_virtio.c
index b09c75c..40af32f 100644
--- a/drivers/remoteproc/remoteproc_virtio.c
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -234,12 +234,14 @@ static void rproc_virtio_finalize_features(struct 
virtio_device *vdev)
 }
 
 static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
-                                                       void *buf, unsigned len)
+                            void *buf, unsigned len, unsigned access_size)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
        struct fw_rsc_vdev *rsc;
        void *cfg;
 
+       len *= access_size;
+
        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
        cfg = &rsc->vring[rsc->num_of_vrings];
 
@@ -252,12 +254,14 @@ static void rproc_virtio_get(struct virtio_device *vdev, 
unsigned offset,
 }
 
 static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
-                     const void *buf, unsigned len)
+                            const void *buf, unsigned len, unsigned 
access_size)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
        struct fw_rsc_vdev *rsc;
        void *cfg;
 
+       len *= access_size;
+
        rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
        cfg = &rsc->vring[rsc->num_of_vrings];
 
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index af2166f..119b106 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -115,19 +115,23 @@ static void kvm_finalize_features(struct virtio_device 
*vdev)
  * Reading and writing elements in config space
  */
 static void kvm_get(struct virtio_device *vdev, unsigned int offset,
-                  void *buf, unsigned len)
+                   void *buf, unsigned len, unsigned access_size)
 {
        struct kvm_device_desc *desc = to_kvmdev(vdev)->desc;
 
+       len *= access_size;
+
        BUG_ON(offset + len > desc->config_len);
        memcpy(buf, kvm_vq_configspace(desc) + offset, len);
 }
 
 static void kvm_set(struct virtio_device *vdev, unsigned int offset,
-                  const void *buf, unsigned len)
+                   const void *buf, unsigned len, unsigned access_size)
 {
        struct kvm_device_desc *desc = to_kvmdev(vdev)->desc;
 
+       len *= access_size;
+
        BUG_ON(offset + len > desc->config_len);
        memcpy(kvm_vq_configspace(desc) + offset, buf, len);
 }
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 779dc51..2c36788 100644
--- a/drivers/s390/kvm/virtio_ccw.c
+++ b/drivers/s390/kvm/virtio_ccw.c
@@ -477,13 +477,16 @@ out_free:
 }
 
 static void virtio_ccw_get_config(struct virtio_device *vdev,
-                                 unsigned int offset, void *buf, unsigned len)
+                                 unsigned int offset, void *buf,
+                                 unsigned len, unsigned access_size)
 {
        struct virtio_ccw_device *vcdev = to_vc_device(vdev);
        int ret;
        struct ccw1 *ccw;
        void *config_area;
 
+       len *= access_size;
+
        ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
        if (!ccw)
                return;
@@ -511,12 +514,14 @@ out_free:
 
 static void virtio_ccw_set_config(struct virtio_device *vdev,
                                  unsigned int offset, const void *buf,
-                                 unsigned len)
+                                 unsigned len, unsigned access_size)
 {
        struct virtio_ccw_device *vcdev = to_vc_device(vdev);
        struct ccw1 *ccw;
        void *config_area;
 
+       len *= access_size;
+
        ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
        if (!ccw)
                return;
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index 74b88ef..652d64e 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -712,7 +712,7 @@ static struct scsi_host_template 
virtscsi_host_template_multi = {
                typeof(((struct virtio_scsi_config *)0)->fld) __val; \
                vdev->config->get(vdev, \
                                  offsetof(struct virtio_scsi_config, fld), \
-                                 &__val, sizeof(__val)); \
+                                 &__val, 1, sizeof(__val));            \
                __val; \
        })
 
@@ -721,7 +721,7 @@ static struct scsi_host_template 
virtscsi_host_template_multi = {
                typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \
                vdev->config->set(vdev, \
                                  offsetof(struct virtio_scsi_config, fld), \
-                                 &__val, sizeof(__val)); \
+                                 &__val, 1, sizeof(__val));            \
        })
 
 static void __virtscsi_set_affinity(struct virtio_scsi *vscsi, bool affinity)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index 1f572c0..ccac818 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -272,23 +272,21 @@ static void virtballoon_changed(struct virtio_device 
*vdev)
 
 static inline s64 towards_target(struct virtio_balloon *vb)
 {
-       __le32 v;
        s64 target;
 
        vb->vdev->config->get(vb->vdev,
                              offsetof(struct virtio_balloon_config, num_pages),
-                             &v, sizeof(v));
-       target = le32_to_cpu(v);
+                             &target, 1, sizeof(target));
        return target - vb->num_pages;
 }
 
 static void update_balloon_size(struct virtio_balloon *vb)
 {
-       __le32 actual = cpu_to_le32(vb->num_pages);
+       u32 actual = vb->num_pages;
 
        vb->vdev->config->set(vb->vdev,
                              offsetof(struct virtio_balloon_config, actual),
-                             &actual, sizeof(actual));
+                             &actual, 1, sizeof(actual));
 }
 
 static int balloon(void *_vballoon)
diff --git a/drivers/virtio/virtio_mmio.c b/drivers/virtio/virtio_mmio.c
index 57f24fd..bbc4410 100644
--- a/drivers/virtio/virtio_mmio.c
+++ b/drivers/virtio/virtio_mmio.c
@@ -101,7 +101,7 @@
 #include <linux/virtio_mmio.h>
 #include <linux/virtio_ring.h>
 
-
+#include <asm-generic/io-64-nonatomic-lo-hi.h>
 
 /* The alignment to use between consumer and producer parts of vring.
  * Currently hardcoded to the page size. */
@@ -168,25 +168,93 @@ static void vm_finalize_features(struct virtio_device 
*vdev)
 }
 
 static void vm_get(struct virtio_device *vdev, unsigned offset,
-                  void *buf, unsigned len)
+                  void *buf, unsigned len, unsigned access_size)
 {
        struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
-       u8 *ptr = buf;
        int i;
 
-       for (i = 0; i < len; i++)
-               ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+       switch (access_size) {
+       case 1: {
+               u8 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       ptr[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 2: {
+               u16 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       ptr[i] = readw(vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 4: {
+               u32 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       ptr[i] = readl(vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 8: {
+               u64 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       ptr[i] = readq(vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       default:
+               pr_err("virtio: illegal access size %d\n", access_size);
+               BUG();
+       }
 }
 
 static void vm_set(struct virtio_device *vdev, unsigned offset,
-                  const void *buf, unsigned len)
+                  const void *buf, unsigned len, unsigned access_size)
 {
        struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
-       const u8 *ptr = buf;
        int i;
 
-       for (i = 0; i < len; i++)
-               writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
+       switch (access_size) {
+       case 1: {
+               const u8 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       writeb(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 2: {
+               const u16 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       writew(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 4: {
+               const u32 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       writel(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       case 8: {
+               const u64 *ptr = buf;
+
+               for (i = 0; i < len; i++)
+                       writeq(ptr[i], vm_dev->base + VIRTIO_MMIO_CONFIG + 
offset + i);
+
+               break;
+       }
+       default:
+               pr_err("virtio: illegal access size %d\n", access_size);
+               BUG();
+       }
 }
 
 static u8 vm_get_status(struct virtio_device *vdev)
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 98917fc..f06671c 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -129,7 +129,7 @@ static void vp_finalize_features(struct virtio_device *vdev)
 
 /* virtio config->get() implementation */
 static void vp_get(struct virtio_device *vdev, unsigned offset,
-                  void *buf, unsigned len)
+                  void *buf, unsigned len, unsigned access_size)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        void __iomem *ioaddr = vp_dev->ioaddr +
@@ -137,6 +137,8 @@ static void vp_get(struct virtio_device *vdev, unsigned 
offset,
        u8 *ptr = buf;
        int i;
 
+       len *= access_size;
+
        for (i = 0; i < len; i++)
                ptr[i] = ioread8(ioaddr + i);
 }
@@ -144,7 +146,7 @@ static void vp_get(struct virtio_device *vdev, unsigned 
offset,
 /* the config->set() implementation.  it's symmetric to the config->get()
  * implementation */
 static void vp_set(struct virtio_device *vdev, unsigned offset,
-                  const void *buf, unsigned len)
+                  const void *buf, unsigned len, unsigned access_size)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        void __iomem *ioaddr = vp_dev->ioaddr +
@@ -152,6 +154,8 @@ static void vp_set(struct virtio_device *vdev, unsigned 
offset,
        const u8 *ptr = buf;
        int i;
 
+       len *= access_size;
+
        for (i = 0; i < len; i++)
                iowrite8(ptr[i], ioaddr + i);
 }
diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h
index 29b9104..69d1884 100644
--- a/include/linux/virtio_config.h
+++ b/include/linux/virtio_config.h
@@ -12,12 +12,14 @@
  *     vdev: the virtio_device
  *     offset: the offset of the configuration field
  *     buf: the buffer to write the field value into.
- *     len: the length of the buffer
+ *     len: the length of the buffer in access_size unit
+ *     access_size: access length
  * @set: write the value of a configuration field
  *     vdev: the virtio_device
  *     offset: the offset of the configuration field
  *     buf: the buffer to read the field value from.
- *     len: the length of the buffer
+ *     len: the length of the buffer in access_size unit
+ *     access_size: access length
  * @get_status: read the status byte
  *     vdev: the virtio_device
  *     Returns the status byte
@@ -55,9 +57,9 @@
 typedef void vq_callback_t(struct virtqueue *);
 struct virtio_config_ops {
        void (*get)(struct virtio_device *vdev, unsigned offset,
-                   void *buf, unsigned len);
+                   void *buf, unsigned len, unsigned access_size);
        void (*set)(struct virtio_device *vdev, unsigned offset,
-                   const void *buf, unsigned len);
+                   const void *buf, unsigned len, unsigned access_size);
        u8 (*get_status)(struct virtio_device *vdev);
        void (*set_status)(struct virtio_device *vdev, u8 status);
        void (*reset)(struct virtio_device *vdev);
@@ -106,20 +108,21 @@ static inline bool virtio_has_feature(const struct 
virtio_device *vdev,
  * The return value is -ENOENT if the feature doesn't exist.  Otherwise
  * the config value is copied into whatever is pointed to by v. */
 #define virtio_config_val(vdev, fbit, offset, v) \
-       virtio_config_buf((vdev), (fbit), (offset), (v), sizeof(*v))
+       virtio_config_buf((vdev), (fbit), (offset), (v), 1, sizeof(*v))
 
 #define virtio_config_val_len(vdev, fbit, offset, v, len) \
-       virtio_config_buf((vdev), (fbit), (offset), (v), (len))
+       virtio_config_buf((vdev), (fbit), (offset), (v), (len), 1)
 
 static inline int virtio_config_buf(struct virtio_device *vdev,
                                    unsigned int fbit,
                                    unsigned int offset,
-                                   void *buf, unsigned len)
+                                   void *buf, unsigned len,
+                                   unsigned access_size)
 {
        if (!virtio_has_feature(vdev, fbit))
                return -ENOENT;
 
-       vdev->config->get(vdev, offset, buf, len);
+       vdev->config->get(vdev, offset, buf, len, access_size);
        return 0;
 }
 
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 990afab..d12a2aa 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -546,7 +546,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)
        if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) {
                vdev->config->get(vdev,
                                offsetof(struct virtio_9p_config, tag_len),
-                               &tag_len, sizeof(tag_len));
+                                 &tag_len, 1, sizeof(tag_len));
        } else {
                err = -EINVAL;
                goto out_free_vq;
@@ -557,7 +557,7 @@ static int p9_virtio_probe(struct virtio_device *vdev)
                goto out_free_vq;
        }
        vdev->config->get(vdev, offsetof(struct virtio_9p_config, tag),
-                       tag, tag_len);
+                         tag, tag_len, 1);
        chan->tag = tag;
        chan->tag_len = tag_len;
        err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr);
-- 
1.8.2.3


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to