xref: /kvmtool/virtio/mmio-modern.c (revision 5fe5eb04de80b8bc68f4d57443596d0b935907ef)
1 #include "kvm/virtio.h"
2 #include "kvm/virtio-mmio.h"
3 
4 #include <linux/byteorder.h>
5 
6 #define vmmio_selected_vq(vmmio) \
7 	vdev->ops->get_vq((vmmio)->kvm, (vmmio)->dev, (vmmio)->hdr.queue_sel)
8 
virtio_mmio_config_in(struct kvm_cpu * vcpu,u64 addr,u32 * data,u32 len,struct virtio_device * vdev)9 static void virtio_mmio_config_in(struct kvm_cpu *vcpu,
10 				  u64 addr, u32 *data, u32 len,
11 				  struct virtio_device *vdev)
12 {
13 	struct virtio_mmio *vmmio = vdev->virtio;
14 	u64 features = 1ULL << VIRTIO_F_VERSION_1;
15 	u32 val = 0;
16 
17 	switch (addr) {
18 	case VIRTIO_MMIO_MAGIC_VALUE:
19 	case VIRTIO_MMIO_VERSION:
20 	case VIRTIO_MMIO_DEVICE_ID:
21 	case VIRTIO_MMIO_VENDOR_ID:
22 	case VIRTIO_MMIO_STATUS:
23 	case VIRTIO_MMIO_INTERRUPT_STATUS:
24 		val = *(u32 *)(((void *)&vmmio->hdr) + addr);
25 		break;
26 	case VIRTIO_MMIO_DEVICE_FEATURES:
27 		if (vmmio->hdr.host_features_sel > 1)
28 			break;
29 		features |= vdev->ops->get_host_features(vmmio->kvm, vmmio->dev);
30 		val = features >> (32 * vmmio->hdr.host_features_sel);
31 		break;
32 	case VIRTIO_MMIO_QUEUE_NUM_MAX:
33 		val = vdev->ops->get_size_vq(vmmio->kvm, vmmio->dev,
34 					     vmmio->hdr.queue_sel);
35 		break;
36 	case VIRTIO_MMIO_QUEUE_READY:
37 		val = vmmio_selected_vq(vmmio)->enabled;
38 		break;
39 	case VIRTIO_MMIO_QUEUE_DESC_LOW:
40 		val = vmmio_selected_vq(vmmio)->vring_addr.desc_lo;
41 		break;
42 	case VIRTIO_MMIO_QUEUE_DESC_HIGH:
43 		val = vmmio_selected_vq(vmmio)->vring_addr.desc_hi;
44 		break;
45 	case VIRTIO_MMIO_QUEUE_USED_LOW:
46 		val = vmmio_selected_vq(vmmio)->vring_addr.used_lo;
47 		break;
48 	case VIRTIO_MMIO_QUEUE_USED_HIGH:
49 		val = vmmio_selected_vq(vmmio)->vring_addr.used_hi;
50 		break;
51 	case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
52 		val = vmmio_selected_vq(vmmio)->vring_addr.avail_lo;
53 		break;
54 	case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
55 		val = vmmio_selected_vq(vmmio)->vring_addr.avail_hi;
56 		break;
57 	case VIRTIO_MMIO_CONFIG_GENERATION:
58 		/*
59 		 * The config generation changes when the device updates a
60 		 * config field larger than 32 bits, that the driver reads using
61 		 * multiple accesses. Since kvmtool doesn't use any mutable
62 		 * config field larger than 32 bits, the generation is constant.
63 		 */
64 		break;
65 	default:
66 		return;
67 	}
68 
69 	*data = cpu_to_le32(val);
70 }
71 
virtio_mmio_config_out(struct kvm_cpu * vcpu,u64 addr,u32 * data,u32 len,struct virtio_device * vdev)72 static void virtio_mmio_config_out(struct kvm_cpu *vcpu,
73 				   u64 addr, u32 *data, u32 len,
74 				   struct virtio_device *vdev)
75 {
76 	struct virtio_mmio *vmmio = vdev->virtio;
77 	struct kvm *kvm = vmmio->kvm;
78 	u32 val = le32_to_cpu(*data);
79 	u64 features;
80 
81 	switch (addr) {
82 	case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
83 	case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
84 	case VIRTIO_MMIO_QUEUE_SEL:
85 		*(u32 *)(((void *)&vmmio->hdr) + addr) = val;
86 		break;
87 	case VIRTIO_MMIO_STATUS:
88 		vmmio->hdr.status = val;
89 		virtio_notify_status(kvm, vdev, vmmio->dev, val);
90 		break;
91 	case VIRTIO_MMIO_DRIVER_FEATURES:
92 		if (vmmio->hdr.guest_features_sel > 1)
93 			break;
94 
95 		features = (u64)val << (32 * vmmio->hdr.guest_features_sel);
96 		virtio_set_guest_features(vmmio->kvm, vdev, vmmio->dev,
97 					  features);
98 		break;
99 	case VIRTIO_MMIO_QUEUE_NUM:
100 		vmmio->hdr.queue_num = val;
101 		vdev->ops->set_size_vq(vmmio->kvm, vmmio->dev,
102 				       vmmio->hdr.queue_sel, val);
103 		break;
104 	case VIRTIO_MMIO_QUEUE_READY:
105 		if (val)
106 			virtio_mmio_init_vq(kvm, vdev, vmmio->hdr.queue_sel);
107 		else
108 			virtio_mmio_exit_vq(kvm, vdev, vmmio->hdr.queue_sel);
109 		break;
110 	case VIRTIO_MMIO_QUEUE_NOTIFY:
111 		vdev->ops->notify_vq(vmmio->kvm, vmmio->dev, val);
112 		break;
113 	case VIRTIO_MMIO_INTERRUPT_ACK:
114 		vmmio->hdr.interrupt_state &= ~val;
115 		break;
116 	case VIRTIO_MMIO_QUEUE_DESC_LOW:
117 		vmmio_selected_vq(vmmio)->vring_addr.desc_lo = val;
118 		break;
119 	case VIRTIO_MMIO_QUEUE_DESC_HIGH:
120 		vmmio_selected_vq(vmmio)->vring_addr.desc_hi = val;
121 		break;
122 	case VIRTIO_MMIO_QUEUE_USED_LOW:
123 		vmmio_selected_vq(vmmio)->vring_addr.used_lo = val;
124 		break;
125 	case VIRTIO_MMIO_QUEUE_USED_HIGH:
126 		vmmio_selected_vq(vmmio)->vring_addr.used_hi = val;
127 		break;
128 	case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
129 		vmmio_selected_vq(vmmio)->vring_addr.avail_lo = val;
130 		break;
131 	case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
132 		vmmio_selected_vq(vmmio)->vring_addr.avail_hi = val;
133 		break;
134 	};
135 }
136 
virtio_mmio_modern_callback(struct kvm_cpu * vcpu,u64 addr,u8 * data,u32 len,u8 is_write,void * ptr)137 void virtio_mmio_modern_callback(struct kvm_cpu *vcpu, u64 addr, u8 *data,
138 				 u32 len, u8 is_write, void *ptr)
139 {
140 	struct virtio_device *vdev = ptr;
141 	struct virtio_mmio *vmmio = vdev->virtio;
142 	u32 offset = addr - vmmio->addr;
143 
144 	if (offset >= VIRTIO_MMIO_CONFIG) {
145 		offset -= VIRTIO_MMIO_CONFIG;
146 		virtio_access_config(vmmio->kvm, vdev, vmmio->dev, offset, data,
147 				     len, is_write);
148 		return;
149 	}
150 
151 	if (len != 4) {
152 		pr_debug("Invalid %s size %d at 0x%llx", is_write ? "write" :
153 			 "read", len, addr);
154 		return;
155 	}
156 
157 	if (is_write)
158 		virtio_mmio_config_out(vcpu, offset, (void *)data, len, ptr);
159 	else
160 		virtio_mmio_config_in(vcpu, offset, (void *)data, len, ptr);
161 }
162