162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Simple test of virtio code, entirely in userpsace. */ 362306a36Sopenharmony_ci#define _GNU_SOURCE 462306a36Sopenharmony_ci#include <sched.h> 562306a36Sopenharmony_ci#include <err.h> 662306a36Sopenharmony_ci#include <linux/kernel.h> 762306a36Sopenharmony_ci#include <linux/err.h> 862306a36Sopenharmony_ci#include <linux/virtio.h> 962306a36Sopenharmony_ci#include <linux/vringh.h> 1062306a36Sopenharmony_ci#include <linux/virtio_ring.h> 1162306a36Sopenharmony_ci#include <linux/virtio_config.h> 1262306a36Sopenharmony_ci#include <linux/uaccess.h> 1362306a36Sopenharmony_ci#include <sys/types.h> 1462306a36Sopenharmony_ci#include <sys/stat.h> 1562306a36Sopenharmony_ci#include <sys/mman.h> 1662306a36Sopenharmony_ci#include <sys/wait.h> 1762306a36Sopenharmony_ci#include <fcntl.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define USER_MEM (1024*1024) 2062306a36Sopenharmony_civoid *__user_addr_min, *__user_addr_max; 2162306a36Sopenharmony_civoid *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end; 2262306a36Sopenharmony_cistatic u64 user_addr_offset; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define RINGSIZE 256 2562306a36Sopenharmony_ci#define ALIGN 4096 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic bool never_notify_host(struct virtqueue *vq) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci abort(); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void never_callback_guest(struct virtqueue *vq) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci abort(); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic bool getrange_iov(struct vringh *vrh, u64 addr, struct vringh_range *r) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (addr < (u64)(unsigned long)__user_addr_min - user_addr_offset) 4062306a36Sopenharmony_ci return false; 4162306a36Sopenharmony_ci if (addr >= (u64)(unsigned long)__user_addr_max - user_addr_offset) 4262306a36Sopenharmony_ci return false; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci r->start = (u64)(unsigned long)__user_addr_min - user_addr_offset; 4562306a36Sopenharmony_ci r->end_incl = (u64)(unsigned long)__user_addr_max - 1 - user_addr_offset; 4662306a36Sopenharmony_ci r->offset = user_addr_offset; 4762306a36Sopenharmony_ci return true; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* We return single byte ranges. */ 5162306a36Sopenharmony_cistatic bool getrange_slow(struct vringh *vrh, u64 addr, struct vringh_range *r) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci if (addr < (u64)(unsigned long)__user_addr_min - user_addr_offset) 5462306a36Sopenharmony_ci return false; 5562306a36Sopenharmony_ci if (addr >= (u64)(unsigned long)__user_addr_max - user_addr_offset) 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci r->start = addr; 5962306a36Sopenharmony_ci r->end_incl = r->start; 6062306a36Sopenharmony_ci r->offset = user_addr_offset; 6162306a36Sopenharmony_ci return true; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct guest_virtio_device { 6562306a36Sopenharmony_ci struct virtio_device vdev; 6662306a36Sopenharmony_ci int to_host_fd; 6762306a36Sopenharmony_ci unsigned long notifies; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic bool parallel_notify_host(struct virtqueue *vq) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci int rc; 7362306a36Sopenharmony_ci struct guest_virtio_device *gvdev; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci gvdev = container_of(vq->vdev, struct guest_virtio_device, vdev); 7662306a36Sopenharmony_ci rc = write(gvdev->to_host_fd, "", 1); 7762306a36Sopenharmony_ci if (rc < 0) 7862306a36Sopenharmony_ci return false; 7962306a36Sopenharmony_ci gvdev->notifies++; 8062306a36Sopenharmony_ci return true; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic bool no_notify_host(struct virtqueue *vq) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define NUM_XFERS (10000000) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* We aim for two "distant" cpus. */ 9162306a36Sopenharmony_cistatic void find_cpus(unsigned int *first, unsigned int *last) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned int i; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci *first = -1U; 9662306a36Sopenharmony_ci *last = 0; 9762306a36Sopenharmony_ci for (i = 0; i < 4096; i++) { 9862306a36Sopenharmony_ci cpu_set_t set; 9962306a36Sopenharmony_ci CPU_ZERO(&set); 10062306a36Sopenharmony_ci CPU_SET(i, &set); 10162306a36Sopenharmony_ci if (sched_setaffinity(getpid(), sizeof(set), &set) == 0) { 10262306a36Sopenharmony_ci if (i < *first) 10362306a36Sopenharmony_ci *first = i; 10462306a36Sopenharmony_ci if (i > *last) 10562306a36Sopenharmony_ci *last = i; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* Opencoded version for fast mode */ 11162306a36Sopenharmony_cistatic inline int vringh_get_head(struct vringh *vrh, u16 *head) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci u16 avail_idx, i; 11462306a36Sopenharmony_ci int err; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci err = get_user(avail_idx, &vrh->vring.avail->idx); 11762306a36Sopenharmony_ci if (err) 11862306a36Sopenharmony_ci return err; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (vrh->last_avail_idx == avail_idx) 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Only get avail ring entries after they have been exposed by guest. */ 12462306a36Sopenharmony_ci virtio_rmb(vrh->weak_barriers); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci i = vrh->last_avail_idx & (vrh->vring.num - 1); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci err = get_user(*head, &vrh->vring.avail->ring[i]); 12962306a36Sopenharmony_ci if (err) 13062306a36Sopenharmony_ci return err; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci vrh->last_avail_idx++; 13362306a36Sopenharmony_ci return 1; 13462306a36Sopenharmony_ci} 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cistatic int parallel_test(u64 features, 13762306a36Sopenharmony_ci bool (*getrange)(struct vringh *vrh, 13862306a36Sopenharmony_ci u64 addr, struct vringh_range *r), 13962306a36Sopenharmony_ci bool fast_vringh) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci void *host_map, *guest_map; 14262306a36Sopenharmony_ci int fd, mapsize, to_guest[2], to_host[2]; 14362306a36Sopenharmony_ci unsigned long xfers = 0, notifies = 0, receives = 0; 14462306a36Sopenharmony_ci unsigned int first_cpu, last_cpu; 14562306a36Sopenharmony_ci cpu_set_t cpu_set; 14662306a36Sopenharmony_ci char buf[128]; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Create real file to mmap. */ 14962306a36Sopenharmony_ci fd = open("/tmp/vringh_test-file", O_RDWR|O_CREAT|O_TRUNC, 0600); 15062306a36Sopenharmony_ci if (fd < 0) 15162306a36Sopenharmony_ci err(1, "Opening /tmp/vringh_test-file"); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci /* Extra room at the end for some data, and indirects */ 15462306a36Sopenharmony_ci mapsize = vring_size(RINGSIZE, ALIGN) 15562306a36Sopenharmony_ci + RINGSIZE * 2 * sizeof(int) 15662306a36Sopenharmony_ci + RINGSIZE * 6 * sizeof(struct vring_desc); 15762306a36Sopenharmony_ci mapsize = (mapsize + getpagesize() - 1) & ~(getpagesize() - 1); 15862306a36Sopenharmony_ci ftruncate(fd, mapsize); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Parent and child use separate addresses, to check our mapping logic! */ 16162306a36Sopenharmony_ci host_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 16262306a36Sopenharmony_ci guest_map = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci pipe(to_guest); 16562306a36Sopenharmony_ci pipe(to_host); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci CPU_ZERO(&cpu_set); 16862306a36Sopenharmony_ci find_cpus(&first_cpu, &last_cpu); 16962306a36Sopenharmony_ci printf("Using CPUS %u and %u\n", first_cpu, last_cpu); 17062306a36Sopenharmony_ci fflush(stdout); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci if (fork() != 0) { 17362306a36Sopenharmony_ci struct vringh vrh; 17462306a36Sopenharmony_ci int status, err, rlen = 0; 17562306a36Sopenharmony_ci char rbuf[5]; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* We are the host: never access guest addresses! */ 17862306a36Sopenharmony_ci munmap(guest_map, mapsize); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci __user_addr_min = host_map; 18162306a36Sopenharmony_ci __user_addr_max = __user_addr_min + mapsize; 18262306a36Sopenharmony_ci user_addr_offset = host_map - guest_map; 18362306a36Sopenharmony_ci assert(user_addr_offset); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci close(to_guest[0]); 18662306a36Sopenharmony_ci close(to_host[1]); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci vring_init(&vrh.vring, RINGSIZE, host_map, ALIGN); 18962306a36Sopenharmony_ci vringh_init_user(&vrh, features, RINGSIZE, true, 19062306a36Sopenharmony_ci vrh.vring.desc, vrh.vring.avail, vrh.vring.used); 19162306a36Sopenharmony_ci CPU_SET(first_cpu, &cpu_set); 19262306a36Sopenharmony_ci if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set)) 19362306a36Sopenharmony_ci errx(1, "Could not set affinity to cpu %u", first_cpu); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci while (xfers < NUM_XFERS) { 19662306a36Sopenharmony_ci struct iovec host_riov[2], host_wiov[2]; 19762306a36Sopenharmony_ci struct vringh_iov riov, wiov; 19862306a36Sopenharmony_ci u16 head, written; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (fast_vringh) { 20162306a36Sopenharmony_ci for (;;) { 20262306a36Sopenharmony_ci err = vringh_get_head(&vrh, &head); 20362306a36Sopenharmony_ci if (err != 0) 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci err = vringh_need_notify_user(&vrh); 20662306a36Sopenharmony_ci if (err < 0) 20762306a36Sopenharmony_ci errx(1, "vringh_need_notify_user: %i", 20862306a36Sopenharmony_ci err); 20962306a36Sopenharmony_ci if (err) { 21062306a36Sopenharmony_ci write(to_guest[1], "", 1); 21162306a36Sopenharmony_ci notifies++; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci if (err != 1) 21562306a36Sopenharmony_ci errx(1, "vringh_get_head"); 21662306a36Sopenharmony_ci written = 0; 21762306a36Sopenharmony_ci goto complete; 21862306a36Sopenharmony_ci } else { 21962306a36Sopenharmony_ci vringh_iov_init(&riov, 22062306a36Sopenharmony_ci host_riov, 22162306a36Sopenharmony_ci ARRAY_SIZE(host_riov)); 22262306a36Sopenharmony_ci vringh_iov_init(&wiov, 22362306a36Sopenharmony_ci host_wiov, 22462306a36Sopenharmony_ci ARRAY_SIZE(host_wiov)); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, 22762306a36Sopenharmony_ci getrange, &head); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci if (err == 0) { 23062306a36Sopenharmony_ci err = vringh_need_notify_user(&vrh); 23162306a36Sopenharmony_ci if (err < 0) 23262306a36Sopenharmony_ci errx(1, "vringh_need_notify_user: %i", 23362306a36Sopenharmony_ci err); 23462306a36Sopenharmony_ci if (err) { 23562306a36Sopenharmony_ci write(to_guest[1], "", 1); 23662306a36Sopenharmony_ci notifies++; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (!vringh_notify_enable_user(&vrh)) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci /* Swallow all notifies at once. */ 24362306a36Sopenharmony_ci if (read(to_host[0], buf, sizeof(buf)) < 1) 24462306a36Sopenharmony_ci break; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci vringh_notify_disable_user(&vrh); 24762306a36Sopenharmony_ci receives++; 24862306a36Sopenharmony_ci continue; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci if (err != 1) 25162306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* We simply copy bytes. */ 25462306a36Sopenharmony_ci if (riov.used) { 25562306a36Sopenharmony_ci rlen = vringh_iov_pull_user(&riov, rbuf, 25662306a36Sopenharmony_ci sizeof(rbuf)); 25762306a36Sopenharmony_ci if (rlen != 4) 25862306a36Sopenharmony_ci errx(1, "vringh_iov_pull_user: %i", 25962306a36Sopenharmony_ci rlen); 26062306a36Sopenharmony_ci assert(riov.i == riov.used); 26162306a36Sopenharmony_ci written = 0; 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci err = vringh_iov_push_user(&wiov, rbuf, rlen); 26462306a36Sopenharmony_ci if (err != rlen) 26562306a36Sopenharmony_ci errx(1, "vringh_iov_push_user: %i", 26662306a36Sopenharmony_ci err); 26762306a36Sopenharmony_ci assert(wiov.i == wiov.used); 26862306a36Sopenharmony_ci written = err; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci complete: 27162306a36Sopenharmony_ci xfers++; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci err = vringh_complete_user(&vrh, head, written); 27462306a36Sopenharmony_ci if (err != 0) 27562306a36Sopenharmony_ci errx(1, "vringh_complete_user: %i", err); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci err = vringh_need_notify_user(&vrh); 27962306a36Sopenharmony_ci if (err < 0) 28062306a36Sopenharmony_ci errx(1, "vringh_need_notify_user: %i", err); 28162306a36Sopenharmony_ci if (err) { 28262306a36Sopenharmony_ci write(to_guest[1], "", 1); 28362306a36Sopenharmony_ci notifies++; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci wait(&status); 28662306a36Sopenharmony_ci if (!WIFEXITED(status)) 28762306a36Sopenharmony_ci errx(1, "Child died with signal %i?", WTERMSIG(status)); 28862306a36Sopenharmony_ci if (WEXITSTATUS(status) != 0) 28962306a36Sopenharmony_ci errx(1, "Child exited %i?", WEXITSTATUS(status)); 29062306a36Sopenharmony_ci printf("Host: notified %lu, pinged %lu\n", notifies, receives); 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci } else { 29362306a36Sopenharmony_ci struct guest_virtio_device gvdev; 29462306a36Sopenharmony_ci struct virtqueue *vq; 29562306a36Sopenharmony_ci unsigned int *data; 29662306a36Sopenharmony_ci struct vring_desc *indirects; 29762306a36Sopenharmony_ci unsigned int finished = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* We pass sg[]s pointing into here, but we need RINGSIZE+1 */ 30062306a36Sopenharmony_ci data = guest_map + vring_size(RINGSIZE, ALIGN); 30162306a36Sopenharmony_ci indirects = (void *)data + (RINGSIZE + 1) * 2 * sizeof(int); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* We are the guest. */ 30462306a36Sopenharmony_ci munmap(host_map, mapsize); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci close(to_guest[1]); 30762306a36Sopenharmony_ci close(to_host[0]); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci gvdev.vdev.features = features; 31062306a36Sopenharmony_ci INIT_LIST_HEAD(&gvdev.vdev.vqs); 31162306a36Sopenharmony_ci spin_lock_init(&gvdev.vdev.vqs_list_lock); 31262306a36Sopenharmony_ci gvdev.to_host_fd = to_host[1]; 31362306a36Sopenharmony_ci gvdev.notifies = 0; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci CPU_SET(first_cpu, &cpu_set); 31662306a36Sopenharmony_ci if (sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set)) 31762306a36Sopenharmony_ci err(1, "Could not set affinity to cpu %u", first_cpu); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &gvdev.vdev, true, 32062306a36Sopenharmony_ci false, guest_map, 32162306a36Sopenharmony_ci fast_vringh ? no_notify_host 32262306a36Sopenharmony_ci : parallel_notify_host, 32362306a36Sopenharmony_ci never_callback_guest, "guest vq"); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Don't kfree indirects. */ 32662306a36Sopenharmony_ci __kfree_ignore_start = indirects; 32762306a36Sopenharmony_ci __kfree_ignore_end = indirects + RINGSIZE * 6; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci while (xfers < NUM_XFERS) { 33062306a36Sopenharmony_ci struct scatterlist sg[4]; 33162306a36Sopenharmony_ci unsigned int num_sg, len; 33262306a36Sopenharmony_ci int *dbuf, err; 33362306a36Sopenharmony_ci bool output = !(xfers % 2); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Consume bufs. */ 33662306a36Sopenharmony_ci while ((dbuf = virtqueue_get_buf(vq, &len)) != NULL) { 33762306a36Sopenharmony_ci if (len == 4) 33862306a36Sopenharmony_ci assert(*dbuf == finished - 1); 33962306a36Sopenharmony_ci else if (!fast_vringh) 34062306a36Sopenharmony_ci assert(*dbuf == finished); 34162306a36Sopenharmony_ci finished++; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Produce a buffer. */ 34562306a36Sopenharmony_ci dbuf = data + (xfers % (RINGSIZE + 1)); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (output) 34862306a36Sopenharmony_ci *dbuf = xfers; 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci *dbuf = -1; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci switch ((xfers / sizeof(*dbuf)) % 4) { 35362306a36Sopenharmony_ci case 0: 35462306a36Sopenharmony_ci /* Nasty three-element sg list. */ 35562306a36Sopenharmony_ci sg_init_table(sg, num_sg = 3); 35662306a36Sopenharmony_ci sg_set_buf(&sg[0], (void *)dbuf, 1); 35762306a36Sopenharmony_ci sg_set_buf(&sg[1], (void *)dbuf + 1, 2); 35862306a36Sopenharmony_ci sg_set_buf(&sg[2], (void *)dbuf + 3, 1); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case 1: 36162306a36Sopenharmony_ci sg_init_table(sg, num_sg = 2); 36262306a36Sopenharmony_ci sg_set_buf(&sg[0], (void *)dbuf, 1); 36362306a36Sopenharmony_ci sg_set_buf(&sg[1], (void *)dbuf + 1, 3); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci case 2: 36662306a36Sopenharmony_ci sg_init_table(sg, num_sg = 1); 36762306a36Sopenharmony_ci sg_set_buf(&sg[0], (void *)dbuf, 4); 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci case 3: 37062306a36Sopenharmony_ci sg_init_table(sg, num_sg = 4); 37162306a36Sopenharmony_ci sg_set_buf(&sg[0], (void *)dbuf, 1); 37262306a36Sopenharmony_ci sg_set_buf(&sg[1], (void *)dbuf + 1, 1); 37362306a36Sopenharmony_ci sg_set_buf(&sg[2], (void *)dbuf + 2, 1); 37462306a36Sopenharmony_ci sg_set_buf(&sg[3], (void *)dbuf + 3, 1); 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* May allocate an indirect, so force it to allocate 37962306a36Sopenharmony_ci * user addr */ 38062306a36Sopenharmony_ci __kmalloc_fake = indirects + (xfers % RINGSIZE) * 4; 38162306a36Sopenharmony_ci if (output) 38262306a36Sopenharmony_ci err = virtqueue_add_outbuf(vq, sg, num_sg, dbuf, 38362306a36Sopenharmony_ci GFP_KERNEL); 38462306a36Sopenharmony_ci else 38562306a36Sopenharmony_ci err = virtqueue_add_inbuf(vq, sg, num_sg, 38662306a36Sopenharmony_ci dbuf, GFP_KERNEL); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (err == -ENOSPC) { 38962306a36Sopenharmony_ci if (!virtqueue_enable_cb_delayed(vq)) 39062306a36Sopenharmony_ci continue; 39162306a36Sopenharmony_ci /* Swallow all notifies at once. */ 39262306a36Sopenharmony_ci if (read(to_guest[0], buf, sizeof(buf)) < 1) 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci receives++; 39662306a36Sopenharmony_ci virtqueue_disable_cb(vq); 39762306a36Sopenharmony_ci continue; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (err) 40162306a36Sopenharmony_ci errx(1, "virtqueue_add_in/outbuf: %i", err); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci xfers++; 40462306a36Sopenharmony_ci virtqueue_kick(vq); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Any extra? */ 40862306a36Sopenharmony_ci while (finished != xfers) { 40962306a36Sopenharmony_ci int *dbuf; 41062306a36Sopenharmony_ci unsigned int len; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* Consume bufs. */ 41362306a36Sopenharmony_ci dbuf = virtqueue_get_buf(vq, &len); 41462306a36Sopenharmony_ci if (dbuf) { 41562306a36Sopenharmony_ci if (len == 4) 41662306a36Sopenharmony_ci assert(*dbuf == finished - 1); 41762306a36Sopenharmony_ci else 41862306a36Sopenharmony_ci assert(len == 0); 41962306a36Sopenharmony_ci finished++; 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!virtqueue_enable_cb_delayed(vq)) 42462306a36Sopenharmony_ci continue; 42562306a36Sopenharmony_ci if (read(to_guest[0], buf, sizeof(buf)) < 1) 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci receives++; 42962306a36Sopenharmony_ci virtqueue_disable_cb(vq); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci printf("Guest: notified %lu, pinged %lu\n", 43362306a36Sopenharmony_ci gvdev.notifies, receives); 43462306a36Sopenharmony_ci vring_del_virtqueue(vq); 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciint main(int argc, char *argv[]) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct virtio_device vdev; 44262306a36Sopenharmony_ci struct virtqueue *vq; 44362306a36Sopenharmony_ci struct vringh vrh; 44462306a36Sopenharmony_ci struct scatterlist guest_sg[RINGSIZE], *sgs[2]; 44562306a36Sopenharmony_ci struct iovec host_riov[2], host_wiov[2]; 44662306a36Sopenharmony_ci struct vringh_iov riov, wiov; 44762306a36Sopenharmony_ci struct vring_used_elem used[RINGSIZE]; 44862306a36Sopenharmony_ci char buf[28]; 44962306a36Sopenharmony_ci u16 head; 45062306a36Sopenharmony_ci int err; 45162306a36Sopenharmony_ci unsigned i; 45262306a36Sopenharmony_ci void *ret; 45362306a36Sopenharmony_ci bool (*getrange)(struct vringh *vrh, u64 addr, struct vringh_range *r); 45462306a36Sopenharmony_ci bool fast_vringh = false, parallel = false; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci getrange = getrange_iov; 45762306a36Sopenharmony_ci vdev.features = 0; 45862306a36Sopenharmony_ci INIT_LIST_HEAD(&vdev.vqs); 45962306a36Sopenharmony_ci spin_lock_init(&vdev.vqs_list_lock); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci while (argv[1]) { 46262306a36Sopenharmony_ci if (strcmp(argv[1], "--indirect") == 0) 46362306a36Sopenharmony_ci __virtio_set_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC); 46462306a36Sopenharmony_ci else if (strcmp(argv[1], "--eventidx") == 0) 46562306a36Sopenharmony_ci __virtio_set_bit(&vdev, VIRTIO_RING_F_EVENT_IDX); 46662306a36Sopenharmony_ci else if (strcmp(argv[1], "--virtio-1") == 0) 46762306a36Sopenharmony_ci __virtio_set_bit(&vdev, VIRTIO_F_VERSION_1); 46862306a36Sopenharmony_ci else if (strcmp(argv[1], "--slow-range") == 0) 46962306a36Sopenharmony_ci getrange = getrange_slow; 47062306a36Sopenharmony_ci else if (strcmp(argv[1], "--fast-vringh") == 0) 47162306a36Sopenharmony_ci fast_vringh = true; 47262306a36Sopenharmony_ci else if (strcmp(argv[1], "--parallel") == 0) 47362306a36Sopenharmony_ci parallel = true; 47462306a36Sopenharmony_ci else 47562306a36Sopenharmony_ci errx(1, "Unknown arg %s", argv[1]); 47662306a36Sopenharmony_ci argv++; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (parallel) 48062306a36Sopenharmony_ci return parallel_test(vdev.features, getrange, fast_vringh); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (posix_memalign(&__user_addr_min, PAGE_SIZE, USER_MEM) != 0) 48362306a36Sopenharmony_ci abort(); 48462306a36Sopenharmony_ci __user_addr_max = __user_addr_min + USER_MEM; 48562306a36Sopenharmony_ci memset(__user_addr_min, 0, vring_size(RINGSIZE, ALIGN)); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Set up guest side. */ 48862306a36Sopenharmony_ci vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true, false, 48962306a36Sopenharmony_ci __user_addr_min, 49062306a36Sopenharmony_ci never_notify_host, never_callback_guest, 49162306a36Sopenharmony_ci "guest vq"); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* Set up host side. */ 49462306a36Sopenharmony_ci vring_init(&vrh.vring, RINGSIZE, __user_addr_min, ALIGN); 49562306a36Sopenharmony_ci vringh_init_user(&vrh, vdev.features, RINGSIZE, true, 49662306a36Sopenharmony_ci vrh.vring.desc, vrh.vring.avail, vrh.vring.used); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* No descriptor to get yet... */ 49962306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); 50062306a36Sopenharmony_ci if (err != 0) 50162306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* Guest puts in a descriptor. */ 50462306a36Sopenharmony_ci memcpy(__user_addr_max - 1, "a", 1); 50562306a36Sopenharmony_ci sg_init_table(guest_sg, 1); 50662306a36Sopenharmony_ci sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); 50762306a36Sopenharmony_ci sg_init_table(guest_sg+1, 1); 50862306a36Sopenharmony_ci sg_set_buf(&guest_sg[1], __user_addr_max - 3, 2); 50962306a36Sopenharmony_ci sgs[0] = &guest_sg[0]; 51062306a36Sopenharmony_ci sgs[1] = &guest_sg[1]; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* May allocate an indirect, so force it to allocate user addr */ 51362306a36Sopenharmony_ci __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); 51462306a36Sopenharmony_ci err = virtqueue_add_sgs(vq, sgs, 1, 1, &err, GFP_KERNEL); 51562306a36Sopenharmony_ci if (err) 51662306a36Sopenharmony_ci errx(1, "virtqueue_add_sgs: %i", err); 51762306a36Sopenharmony_ci __kmalloc_fake = NULL; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Host retreives it. */ 52062306a36Sopenharmony_ci vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); 52162306a36Sopenharmony_ci vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); 52462306a36Sopenharmony_ci if (err != 1) 52562306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci assert(riov.used == 1); 52862306a36Sopenharmony_ci assert(riov.iov[0].iov_base == __user_addr_max - 1); 52962306a36Sopenharmony_ci assert(riov.iov[0].iov_len == 1); 53062306a36Sopenharmony_ci if (getrange != getrange_slow) { 53162306a36Sopenharmony_ci assert(wiov.used == 1); 53262306a36Sopenharmony_ci assert(wiov.iov[0].iov_base == __user_addr_max - 3); 53362306a36Sopenharmony_ci assert(wiov.iov[0].iov_len == 2); 53462306a36Sopenharmony_ci } else { 53562306a36Sopenharmony_ci assert(wiov.used == 2); 53662306a36Sopenharmony_ci assert(wiov.iov[0].iov_base == __user_addr_max - 3); 53762306a36Sopenharmony_ci assert(wiov.iov[0].iov_len == 1); 53862306a36Sopenharmony_ci assert(wiov.iov[1].iov_base == __user_addr_max - 2); 53962306a36Sopenharmony_ci assert(wiov.iov[1].iov_len == 1); 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci err = vringh_iov_pull_user(&riov, buf, 5); 54362306a36Sopenharmony_ci if (err != 1) 54462306a36Sopenharmony_ci errx(1, "vringh_iov_pull_user: %i", err); 54562306a36Sopenharmony_ci assert(buf[0] == 'a'); 54662306a36Sopenharmony_ci assert(riov.i == 1); 54762306a36Sopenharmony_ci assert(vringh_iov_pull_user(&riov, buf, 5) == 0); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci memcpy(buf, "bcdef", 5); 55062306a36Sopenharmony_ci err = vringh_iov_push_user(&wiov, buf, 5); 55162306a36Sopenharmony_ci if (err != 2) 55262306a36Sopenharmony_ci errx(1, "vringh_iov_push_user: %i", err); 55362306a36Sopenharmony_ci assert(memcmp(__user_addr_max - 3, "bc", 2) == 0); 55462306a36Sopenharmony_ci assert(wiov.i == wiov.used); 55562306a36Sopenharmony_ci assert(vringh_iov_push_user(&wiov, buf, 5) == 0); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Host is done. */ 55862306a36Sopenharmony_ci err = vringh_complete_user(&vrh, head, err); 55962306a36Sopenharmony_ci if (err != 0) 56062306a36Sopenharmony_ci errx(1, "vringh_complete_user: %i", err); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Guest should see used token now. */ 56362306a36Sopenharmony_ci __kfree_ignore_start = __user_addr_min + vring_size(RINGSIZE, ALIGN); 56462306a36Sopenharmony_ci __kfree_ignore_end = __kfree_ignore_start + 1; 56562306a36Sopenharmony_ci ret = virtqueue_get_buf(vq, &i); 56662306a36Sopenharmony_ci if (ret != &err) 56762306a36Sopenharmony_ci errx(1, "virtqueue_get_buf: %p", ret); 56862306a36Sopenharmony_ci assert(i == 2); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Guest puts in a huge descriptor. */ 57162306a36Sopenharmony_ci sg_init_table(guest_sg, RINGSIZE); 57262306a36Sopenharmony_ci for (i = 0; i < RINGSIZE; i++) { 57362306a36Sopenharmony_ci sg_set_buf(&guest_sg[i], 57462306a36Sopenharmony_ci __user_addr_max - USER_MEM/4, USER_MEM/4); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Fill contents with recognisable garbage. */ 57862306a36Sopenharmony_ci for (i = 0; i < USER_MEM/4; i++) 57962306a36Sopenharmony_ci ((char *)__user_addr_max - USER_MEM/4)[i] = i; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* This will allocate an indirect, so force it to allocate user addr */ 58262306a36Sopenharmony_ci __kmalloc_fake = __user_addr_min + vring_size(RINGSIZE, ALIGN); 58362306a36Sopenharmony_ci err = virtqueue_add_outbuf(vq, guest_sg, RINGSIZE, &err, GFP_KERNEL); 58462306a36Sopenharmony_ci if (err) 58562306a36Sopenharmony_ci errx(1, "virtqueue_add_outbuf (large): %i", err); 58662306a36Sopenharmony_ci __kmalloc_fake = NULL; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* Host picks it up (allocates new iov). */ 58962306a36Sopenharmony_ci vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); 59062306a36Sopenharmony_ci vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); 59362306a36Sopenharmony_ci if (err != 1) 59462306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci assert(riov.max_num & VRINGH_IOV_ALLOCATED); 59762306a36Sopenharmony_ci assert(riov.iov != host_riov); 59862306a36Sopenharmony_ci if (getrange != getrange_slow) 59962306a36Sopenharmony_ci assert(riov.used == RINGSIZE); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci assert(riov.used == RINGSIZE * USER_MEM/4); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci assert(!(wiov.max_num & VRINGH_IOV_ALLOCATED)); 60462306a36Sopenharmony_ci assert(wiov.used == 0); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* Pull data back out (in odd chunks), should be as expected. */ 60762306a36Sopenharmony_ci for (i = 0; i < RINGSIZE * USER_MEM/4; i += 3) { 60862306a36Sopenharmony_ci err = vringh_iov_pull_user(&riov, buf, 3); 60962306a36Sopenharmony_ci if (err != 3 && i + err != RINGSIZE * USER_MEM/4) 61062306a36Sopenharmony_ci errx(1, "vringh_iov_pull_user large: %i", err); 61162306a36Sopenharmony_ci assert(buf[0] == (char)i); 61262306a36Sopenharmony_ci assert(err < 2 || buf[1] == (char)(i + 1)); 61362306a36Sopenharmony_ci assert(err < 3 || buf[2] == (char)(i + 2)); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci assert(riov.i == riov.used); 61662306a36Sopenharmony_ci vringh_iov_cleanup(&riov); 61762306a36Sopenharmony_ci vringh_iov_cleanup(&wiov); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* Complete using multi interface, just because we can. */ 62062306a36Sopenharmony_ci used[0].id = head; 62162306a36Sopenharmony_ci used[0].len = 0; 62262306a36Sopenharmony_ci err = vringh_complete_multi_user(&vrh, used, 1); 62362306a36Sopenharmony_ci if (err) 62462306a36Sopenharmony_ci errx(1, "vringh_complete_multi_user(1): %i", err); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Free up those descriptors. */ 62762306a36Sopenharmony_ci ret = virtqueue_get_buf(vq, &i); 62862306a36Sopenharmony_ci if (ret != &err) 62962306a36Sopenharmony_ci errx(1, "virtqueue_get_buf: %p", ret); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Add lots of descriptors. */ 63262306a36Sopenharmony_ci sg_init_table(guest_sg, 1); 63362306a36Sopenharmony_ci sg_set_buf(&guest_sg[0], __user_addr_max - 1, 1); 63462306a36Sopenharmony_ci for (i = 0; i < RINGSIZE; i++) { 63562306a36Sopenharmony_ci err = virtqueue_add_outbuf(vq, guest_sg, 1, &err, GFP_KERNEL); 63662306a36Sopenharmony_ci if (err) 63762306a36Sopenharmony_ci errx(1, "virtqueue_add_outbuf (multiple): %i", err); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci /* Now get many, and consume them all at once. */ 64162306a36Sopenharmony_ci vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); 64262306a36Sopenharmony_ci vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci for (i = 0; i < RINGSIZE; i++) { 64562306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); 64662306a36Sopenharmony_ci if (err != 1) 64762306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 64862306a36Sopenharmony_ci used[i].id = head; 64962306a36Sopenharmony_ci used[i].len = 0; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci /* Make sure it wraps around ring, to test! */ 65262306a36Sopenharmony_ci assert(vrh.vring.used->idx % RINGSIZE != 0); 65362306a36Sopenharmony_ci err = vringh_complete_multi_user(&vrh, used, RINGSIZE); 65462306a36Sopenharmony_ci if (err) 65562306a36Sopenharmony_ci errx(1, "vringh_complete_multi_user: %i", err); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* Free those buffers. */ 65862306a36Sopenharmony_ci for (i = 0; i < RINGSIZE; i++) { 65962306a36Sopenharmony_ci unsigned len; 66062306a36Sopenharmony_ci assert(virtqueue_get_buf(vq, &len) != NULL); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Test weird (but legal!) indirect. */ 66462306a36Sopenharmony_ci if (__virtio_test_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC)) { 66562306a36Sopenharmony_ci char *data = __user_addr_max - USER_MEM/4; 66662306a36Sopenharmony_ci struct vring_desc *d = __user_addr_max - USER_MEM/2; 66762306a36Sopenharmony_ci struct vring vring; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* Force creation of direct, which we modify. */ 67062306a36Sopenharmony_ci __virtio_clear_bit(&vdev, VIRTIO_RING_F_INDIRECT_DESC); 67162306a36Sopenharmony_ci vq = vring_new_virtqueue(0, RINGSIZE, ALIGN, &vdev, true, 67262306a36Sopenharmony_ci false, __user_addr_min, 67362306a36Sopenharmony_ci never_notify_host, 67462306a36Sopenharmony_ci never_callback_guest, 67562306a36Sopenharmony_ci "guest vq"); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci sg_init_table(guest_sg, 4); 67862306a36Sopenharmony_ci sg_set_buf(&guest_sg[0], d, sizeof(*d)*2); 67962306a36Sopenharmony_ci sg_set_buf(&guest_sg[1], d + 2, sizeof(*d)*1); 68062306a36Sopenharmony_ci sg_set_buf(&guest_sg[2], data + 6, 4); 68162306a36Sopenharmony_ci sg_set_buf(&guest_sg[3], d + 3, sizeof(*d)*3); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci err = virtqueue_add_outbuf(vq, guest_sg, 4, &err, GFP_KERNEL); 68462306a36Sopenharmony_ci if (err) 68562306a36Sopenharmony_ci errx(1, "virtqueue_add_outbuf (indirect): %i", err); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci vring_init(&vring, RINGSIZE, __user_addr_min, ALIGN); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* They're used in order, but double-check... */ 69062306a36Sopenharmony_ci assert(vring.desc[0].addr == (unsigned long)d); 69162306a36Sopenharmony_ci assert(vring.desc[1].addr == (unsigned long)(d+2)); 69262306a36Sopenharmony_ci assert(vring.desc[2].addr == (unsigned long)data + 6); 69362306a36Sopenharmony_ci assert(vring.desc[3].addr == (unsigned long)(d+3)); 69462306a36Sopenharmony_ci vring.desc[0].flags |= VRING_DESC_F_INDIRECT; 69562306a36Sopenharmony_ci vring.desc[1].flags |= VRING_DESC_F_INDIRECT; 69662306a36Sopenharmony_ci vring.desc[3].flags |= VRING_DESC_F_INDIRECT; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* First indirect */ 69962306a36Sopenharmony_ci d[0].addr = (unsigned long)data; 70062306a36Sopenharmony_ci d[0].len = 1; 70162306a36Sopenharmony_ci d[0].flags = VRING_DESC_F_NEXT; 70262306a36Sopenharmony_ci d[0].next = 1; 70362306a36Sopenharmony_ci d[1].addr = (unsigned long)data + 1; 70462306a36Sopenharmony_ci d[1].len = 2; 70562306a36Sopenharmony_ci d[1].flags = 0; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci /* Second indirect */ 70862306a36Sopenharmony_ci d[2].addr = (unsigned long)data + 3; 70962306a36Sopenharmony_ci d[2].len = 3; 71062306a36Sopenharmony_ci d[2].flags = 0; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* Third indirect */ 71362306a36Sopenharmony_ci d[3].addr = (unsigned long)data + 10; 71462306a36Sopenharmony_ci d[3].len = 5; 71562306a36Sopenharmony_ci d[3].flags = VRING_DESC_F_NEXT; 71662306a36Sopenharmony_ci d[3].next = 1; 71762306a36Sopenharmony_ci d[4].addr = (unsigned long)data + 15; 71862306a36Sopenharmony_ci d[4].len = 6; 71962306a36Sopenharmony_ci d[4].flags = VRING_DESC_F_NEXT; 72062306a36Sopenharmony_ci d[4].next = 2; 72162306a36Sopenharmony_ci d[5].addr = (unsigned long)data + 21; 72262306a36Sopenharmony_ci d[5].len = 7; 72362306a36Sopenharmony_ci d[5].flags = 0; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* Host picks it up (allocates new iov). */ 72662306a36Sopenharmony_ci vringh_iov_init(&riov, host_riov, ARRAY_SIZE(host_riov)); 72762306a36Sopenharmony_ci vringh_iov_init(&wiov, host_wiov, ARRAY_SIZE(host_wiov)); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci err = vringh_getdesc_user(&vrh, &riov, &wiov, getrange, &head); 73062306a36Sopenharmony_ci if (err != 1) 73162306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: %i", err); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (head != 0) 73462306a36Sopenharmony_ci errx(1, "vringh_getdesc_user: head %i not 0", head); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci assert(riov.max_num & VRINGH_IOV_ALLOCATED); 73762306a36Sopenharmony_ci if (getrange != getrange_slow) 73862306a36Sopenharmony_ci assert(riov.used == 7); 73962306a36Sopenharmony_ci else 74062306a36Sopenharmony_ci assert(riov.used == 28); 74162306a36Sopenharmony_ci err = vringh_iov_pull_user(&riov, buf, 29); 74262306a36Sopenharmony_ci assert(err == 28); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Data should be linear. */ 74562306a36Sopenharmony_ci for (i = 0; i < err; i++) 74662306a36Sopenharmony_ci assert(buf[i] == i); 74762306a36Sopenharmony_ci vringh_iov_cleanup(&riov); 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* Don't leak memory... */ 75162306a36Sopenharmony_ci vring_del_virtqueue(vq); 75262306a36Sopenharmony_ci free(__user_addr_min); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci return 0; 75562306a36Sopenharmony_ci} 756