1 /******************************************************************************
2  * Copyright (c) 2016 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12 
13 /*
14  * Virtio serial device definitions.
15  * See Virtio 1.0 - 5.3 Console Device, for details
16  */
17 #include <stdio.h>
18 #include <string.h>
19 #include <cpu.h>
20 #include <helpers.h>
21 #include <byteorder.h>
22 #include "virtio.h"
23 #include "virtio-serial.h"
24 #include "virtio-internal.h"
25 
26 #define DRIVER_FEATURE_SUPPORT  VIRTIO_F_VERSION_1
27 #define RX_ELEM_SIZE            4
28 #define RX_NUM_ELEMS            128
29 
30 #define RX_Q 0
31 #define TX_Q 1
32 
33 static struct vqs vq_rx;
34 static struct vqs vq_tx;
35 static uint16_t last_rx_idx;	/* Last index in RX "used" ring */
36 
virtio_serial_init(struct virtio_device * dev)37 int virtio_serial_init(struct virtio_device *dev)
38 {
39 	struct vring_avail *vq_avail;
40 	int status = VIRTIO_STAT_ACKNOWLEDGE;
41 	int i;
42 
43 	/* Reset device */
44 	virtio_reset_device(dev);
45 
46 	/* Acknowledge device. */
47 	virtio_set_status(dev, status);
48 
49 	/* Tell HV that we know how to drive the device. */
50 	status |= VIRTIO_STAT_DRIVER;
51 	virtio_set_status(dev, status);
52 
53 	if (dev->is_modern) {
54 		/* Negotiate features and sets FEATURES_OK if successful */
55 		if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT))
56 			goto dev_error;
57 
58 		virtio_get_status(dev, &status);
59 	}
60 
61 	if (virtio_queue_init_vq(dev, &vq_rx, RX_Q))
62 		goto dev_error;
63 
64 	/* Allocate memory for multiple receive buffers */
65 	vq_rx.buf_mem = SLOF_alloc_mem(RX_ELEM_SIZE * RX_NUM_ELEMS);
66 	if (!vq_rx.buf_mem) {
67 		printf("virtio-serial: Failed to allocate buffers!\n");
68 		goto dev_error;
69 	}
70 
71 	/* Prepare receive buffer queue */
72 	for (i = 0; i < RX_NUM_ELEMS; i++) {
73 		uint64_t addr = (uint64_t)vq_rx.buf_mem + i * RX_ELEM_SIZE;
74 
75 		/* Descriptor for data: */
76 		virtio_fill_desc(&vq_rx.desc[i], dev->is_modern, addr, 1, VRING_DESC_F_WRITE, 0);
77 		vq_rx.avail->ring[i] = virtio_cpu_to_modern16(dev, i);
78 	}
79 	vq_rx.avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
80 	vq_rx.avail->idx = virtio_cpu_to_modern16(dev, RX_NUM_ELEMS);
81 	sync();
82 
83 	last_rx_idx = virtio_modern16_to_cpu(dev, vq_rx.used->idx);
84 
85 	if (virtio_queue_init_vq(dev, &vq_tx, TX_Q))
86 		goto dev_error;
87 
88 	vq_avail = virtio_get_vring_avail(dev, TX_Q);
89 	vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
90 	vq_avail->idx = 0;
91 
92 	/* Tell HV that setup succeeded */
93 	status |= VIRTIO_STAT_DRIVER_OK;
94 	virtio_set_status(dev, status);
95 
96 	return 1;
97  dev_error:
98 	printf("%s: failed\n", __func__);
99 	status |= VIRTIO_STAT_FAILED;
100 	virtio_set_status(dev, status);
101 	return 0;
102 }
103 
virtio_serial_shutdown(struct virtio_device * dev)104 void virtio_serial_shutdown(struct virtio_device *dev)
105 {
106 	/* Quiesce device */
107 	virtio_set_status(dev, VIRTIO_STAT_FAILED);
108 
109 	/* Reset device */
110 	virtio_reset_device(dev);
111 }
112 
virtio_serial_putchar(struct virtio_device * dev,char c)113 int virtio_serial_putchar(struct virtio_device *dev, char c)
114 {
115 	struct vring_desc *desc;
116 	int id;
117 	uint32_t vq_size, time;
118 	struct vring_desc *vq_desc;
119 	struct vring_avail *vq_avail;
120 	struct vring_used *vq_used;
121 	volatile uint16_t *current_used_idx;
122 	uint16_t last_used_idx, avail_idx;
123 
124 	vq_size = virtio_get_qsize(dev, TX_Q);
125 	vq_desc = virtio_get_vring_desc(dev, TX_Q);
126 	vq_avail = virtio_get_vring_avail(dev, TX_Q);
127 	vq_used = virtio_get_vring_used(dev, TX_Q);
128 
129 	avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx);
130 
131 	last_used_idx = vq_used->idx;
132 	current_used_idx = &vq_used->idx;
133 
134 	/* Determine descriptor index */
135 	id = avail_idx % vq_size;
136 
137 	/* Set up virtqueue descriptor for header */
138 	desc = &vq_desc[id];
139 	virtio_fill_desc(desc, dev->is_modern, (uint64_t)&c, 1, 0, 0);
140 
141 	vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16 (dev, id);
142 	mb();
143 	vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
144 
145 	/* Tell HV that the queue is ready */
146 	virtio_queue_notify(dev, TX_Q);
147 
148 	/* Wait for host to consume the descriptor */
149 	time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
150 	while (*current_used_idx == last_used_idx) {
151 		// do something better
152 		mb();
153 		if (time < SLOF_GetTimer()) {
154 			printf("virtio_serial_putchar failed! \n");
155 			return 0;
156 		}
157 	}
158 
159 	return 1;
160 }
161 
162 static uint16_t last_rx_idx;	/* Last index in RX "used" ring */
163 
virtio_serial_getchar(struct virtio_device * dev)164 char virtio_serial_getchar(struct virtio_device *dev)
165 {
166 	int id, idx;
167 	char buf[RX_NUM_ELEMS] = {0};
168 	uint16_t avail_idx;
169 
170 	idx = virtio_modern16_to_cpu(dev, vq_rx.used->idx);
171 	if (last_rx_idx == idx) {
172 		/* Nothing received yet */
173 		return 0;
174 	}
175 
176 	id = (virtio_modern32_to_cpu(dev, vq_rx.used->ring[last_rx_idx % vq_rx.size].id) + 1)
177 		% vq_rx.size;
178 
179 	/* Copy data to destination buffer */
180 	memcpy(buf, (void *)virtio_modern64_to_cpu(dev, vq_rx.desc[id - 1].addr), RX_ELEM_SIZE);
181 
182 	/* Move indices to next entries */
183 	last_rx_idx = last_rx_idx + 1;
184 
185 	avail_idx = virtio_modern16_to_cpu(dev, vq_rx.avail->idx);
186 	vq_rx.avail->ring[avail_idx % vq_rx.size] = virtio_cpu_to_modern16(dev, id - 1);
187 	sync();
188 	vq_rx.avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
189 	sync();
190 
191 	/* Tell HV that RX queue entry is ready */
192 	virtio_queue_notify(dev, RX_Q);
193 
194 	return buf[0];
195 }
196 
virtio_serial_haschar(struct virtio_device * dev)197 int virtio_serial_haschar(struct virtio_device *dev)
198 {
199 	if (last_rx_idx == virtio_modern16_to_cpu(dev, vq_rx.used->idx))
200 		return 0;
201 	else
202 		return 1;
203 }
204