1376ac44cSSasha Levin #include "kvm/virtio-rng.h" 2376ac44cSSasha Levin 331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h" 4376ac44cSSasha Levin 5376ac44cSSasha Levin #include "kvm/virtio.h" 6376ac44cSSasha Levin #include "kvm/util.h" 7376ac44cSSasha Levin #include "kvm/kvm.h" 8376ac44cSSasha Levin #include "kvm/threadpool.h" 9c75b037fSSasha Levin #include "kvm/guest_compat.h" 10*1c47ce69SSasha Levin #include "kvm/virtio-trans.h" 11376ac44cSSasha Levin 12376ac44cSSasha Levin #include <linux/virtio_ring.h> 13376ac44cSSasha Levin #include <linux/virtio_rng.h> 14376ac44cSSasha Levin 1580ac1d05SSasha Levin #include <linux/list.h> 16376ac44cSSasha Levin #include <fcntl.h> 17376ac44cSSasha Levin #include <sys/types.h> 18376ac44cSSasha Levin #include <sys/stat.h> 19376ac44cSSasha Levin #include <pthread.h> 2053cbeb9bSSasha Levin #include <linux/kernel.h> 21376ac44cSSasha Levin 22376ac44cSSasha Levin #define NUM_VIRT_QUEUES 1 23376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE 128 24376ac44cSSasha Levin 2580ac1d05SSasha Levin struct rng_dev_job { 2680ac1d05SSasha Levin struct virt_queue *vq; 2780ac1d05SSasha Levin struct rng_dev *rdev; 28df0c7f57SSasha Levin struct thread_pool__job job_id; 292449f6e3SSasha Levin }; 302449f6e3SSasha Levin 3180ffe4d1SSasha Levin struct rng_dev { 3280ac1d05SSasha Levin struct list_head list; 33*1c47ce69SSasha Levin struct virtio_trans vtrans; 3480ac1d05SSasha Levin 3580ffe4d1SSasha Levin int fd; 36376ac44cSSasha Levin 37376ac44cSSasha Levin /* virtio queue */ 38376ac44cSSasha Levin struct virt_queue vqs[NUM_VIRT_QUEUES]; 3980ac1d05SSasha Levin struct rng_dev_job jobs[NUM_VIRT_QUEUES]; 40376ac44cSSasha Levin }; 41376ac44cSSasha Levin 4280ac1d05SSasha Levin static LIST_HEAD(rdevs); 43312c62d1SSasha Levin static int compat_id = -1; 44376ac44cSSasha Levin 4553cbeb9bSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset) 46376ac44cSSasha Levin { 4753cbeb9bSSasha Levin /* Unused */ 4853cbeb9bSSasha Levin } 49376ac44cSSasha Levin 5053cbeb9bSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset) 5153cbeb9bSSasha Levin { 5253cbeb9bSSasha Levin /* Unused */ 5353cbeb9bSSasha Levin return 0; 5453cbeb9bSSasha Levin } 55376ac44cSSasha Levin 5653cbeb9bSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev) 5753cbeb9bSSasha Levin { 5853cbeb9bSSasha Levin /* Unused */ 5953cbeb9bSSasha Levin return 0; 6053cbeb9bSSasha Levin } 61376ac44cSSasha Levin 6253cbeb9bSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features) 6353cbeb9bSSasha Levin { 6453cbeb9bSSasha Levin /* Unused */ 65376ac44cSSasha Levin } 66376ac44cSSasha Levin 6780ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue) 68376ac44cSSasha Levin { 69376ac44cSSasha Levin struct iovec iov[VIRTIO_RNG_QUEUE_SIZE]; 70376ac44cSSasha Levin unsigned int len = 0; 71407475bfSPekka Enberg u16 out, in, head; 72376ac44cSSasha Levin 7380ffe4d1SSasha Levin head = virt_queue__get_iov(queue, iov, &out, &in, kvm); 7480ac1d05SSasha Levin len = readv(rdev->fd, iov, in); 75407475bfSPekka Enberg 76376ac44cSSasha Levin virt_queue__set_used_elem(queue, head, len); 77376ac44cSSasha Levin 78376ac44cSSasha Levin return true; 79376ac44cSSasha Levin } 80376ac44cSSasha Levin 81376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param) 82376ac44cSSasha Levin { 8380ac1d05SSasha Levin struct rng_dev_job *job = param; 8480ac1d05SSasha Levin struct virt_queue *vq = job->vq; 8580ac1d05SSasha Levin struct rng_dev *rdev = job->rdev; 86376ac44cSSasha Levin 87bc485053SSasha Levin while (virt_queue__available(vq)) 8880ac1d05SSasha Levin virtio_rng_do_io_request(kvm, rdev, vq); 89bc485053SSasha Levin 90*1c47ce69SSasha Levin rdev->vtrans.trans_ops->signal_vq(kvm, &rdev->vtrans, vq - rdev->vqs); 91376ac44cSSasha Levin } 92376ac44cSSasha Levin 9353cbeb9bSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn) 94376ac44cSSasha Levin { 9553cbeb9bSSasha Levin struct rng_dev *rdev = dev; 96376ac44cSSasha Levin struct virt_queue *queue; 9780ac1d05SSasha Levin struct rng_dev_job *job; 98376ac44cSSasha Levin void *p; 99376ac44cSSasha Levin 100312c62d1SSasha Levin compat__remove_message(compat_id); 101c75b037fSSasha Levin 10253cbeb9bSSasha Levin queue = &rdev->vqs[vq]; 10353cbeb9bSSasha Levin queue->pfn = pfn; 104aaf0b445SSasha Levin p = guest_pfn_to_host(kvm, queue->pfn); 105376ac44cSSasha Levin 10653cbeb9bSSasha Levin job = &rdev->jobs[vq]; 10780ac1d05SSasha Levin 108b8f43678SSasha Levin vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN); 109376ac44cSSasha Levin 11080ac1d05SSasha Levin *job = (struct rng_dev_job) { 11180ac1d05SSasha Levin .vq = queue, 11280ac1d05SSasha Levin .rdev = rdev, 11380ac1d05SSasha Levin }; 11480ac1d05SSasha Levin 11553cbeb9bSSasha Levin thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job); 11653cbeb9bSSasha Levin 11753cbeb9bSSasha Levin return 0; 118b2533581SSasha Levin } 119c75b037fSSasha Levin 12053cbeb9bSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq) 12153cbeb9bSSasha Levin { 12253cbeb9bSSasha Levin struct rng_dev *rdev = dev; 12353cbeb9bSSasha Levin 12453cbeb9bSSasha Levin thread_pool__do_job(&rdev->jobs[vq].job_id); 12553cbeb9bSSasha Levin 12653cbeb9bSSasha Levin return 0; 12753cbeb9bSSasha Levin } 12853cbeb9bSSasha Levin 12953cbeb9bSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq) 13053cbeb9bSSasha Levin { 13153cbeb9bSSasha Levin struct rng_dev *rdev = dev; 13253cbeb9bSSasha Levin 13353cbeb9bSSasha Levin return rdev->vqs[vq].pfn; 13453cbeb9bSSasha Levin } 13553cbeb9bSSasha Levin 13653cbeb9bSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq) 13753cbeb9bSSasha Levin { 13853cbeb9bSSasha Levin return VIRTIO_RNG_QUEUE_SIZE; 13953cbeb9bSSasha Levin } 14053cbeb9bSSasha Levin 141*1c47ce69SSasha Levin static struct virtio_ops rng_dev_virtio_ops = (struct virtio_ops) { 142*1c47ce69SSasha Levin .set_config = set_config, 143*1c47ce69SSasha Levin .get_config = get_config, 144*1c47ce69SSasha Levin .get_host_features = get_host_features, 145*1c47ce69SSasha Levin .set_guest_features = set_guest_features, 146*1c47ce69SSasha Levin .init_vq = init_vq, 147*1c47ce69SSasha Levin .notify_vq = notify_vq, 148*1c47ce69SSasha Levin .get_pfn_vq = get_pfn_vq, 149*1c47ce69SSasha Levin .get_size_vq = get_size_vq, 150*1c47ce69SSasha Levin }; 151*1c47ce69SSasha Levin 15253cbeb9bSSasha Levin void virtio_rng__init(struct kvm *kvm) 15353cbeb9bSSasha Levin { 15453cbeb9bSSasha Levin struct rng_dev *rdev; 15553cbeb9bSSasha Levin 15653cbeb9bSSasha Levin rdev = malloc(sizeof(*rdev)); 15753cbeb9bSSasha Levin if (rdev == NULL) 15853cbeb9bSSasha Levin return; 15953cbeb9bSSasha Levin 16053cbeb9bSSasha Levin rdev->fd = open("/dev/urandom", O_RDONLY); 16153cbeb9bSSasha Levin if (rdev->fd < 0) 16253cbeb9bSSasha Levin die("Failed initializing RNG"); 16353cbeb9bSSasha Levin 164*1c47ce69SSasha Levin virtio_trans_init(&rdev->vtrans, VIRTIO_PCI); 165*1c47ce69SSasha Levin rdev->vtrans.trans_ops->init(kvm, &rdev->vtrans, rdev, PCI_DEVICE_ID_VIRTIO_RNG, 166*1c47ce69SSasha Levin VIRTIO_ID_RNG, PCI_CLASS_RNG); 167*1c47ce69SSasha Levin rdev->vtrans.virtio_ops = &rng_dev_virtio_ops; 16853cbeb9bSSasha Levin 16953cbeb9bSSasha Levin list_add_tail(&rdev->list, &rdevs); 17053cbeb9bSSasha Levin 171312c62d1SSasha Levin if (compat_id != -1) 172312c62d1SSasha Levin compat_id = compat__add_message("virtio-rng device was not detected", 173c75b037fSSasha Levin "While you have requested a virtio-rng device, " 174fc835ab3SSasha Levin "the guest kernel did not initialize it.\n" 175fc835ab3SSasha Levin "Please make sure that the guest kernel was " 176fc835ab3SSasha Levin "compiled with CONFIG_HW_RANDOM_VIRTIO=y enabled " 177fc835ab3SSasha Levin "in its .config"); 17880ac1d05SSasha Levin } 17980ac1d05SSasha Levin 18080ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm) 18180ac1d05SSasha Levin { 18280ac1d05SSasha Levin while (!list_empty(&rdevs)) { 18380ac1d05SSasha Levin struct rng_dev *rdev; 18480ac1d05SSasha Levin 18580ac1d05SSasha Levin rdev = list_first_entry(&rdevs, struct rng_dev, list); 18680ac1d05SSasha Levin list_del(&rdev->list); 18780ac1d05SSasha Levin free(rdev); 18880ac1d05SSasha Levin } 189376ac44cSSasha Levin } 190