aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikunj A Dadhania <nikunj@linux.vnet.ibm.com>2016-10-10 16:47:06 +0530
committerAlexey Kardashevskiy <aik@ozlabs.ru>2016-10-13 10:47:05 +1100
commit8e92d6b3e8d6ac0625356f058da6ddae316e2cdc (patch)
tree1df9cff96c1bec5ebac4c3bbbe856202d83be38c /lib
parentc1ab43d25be823fce882771f8407ddd93b893ac6 (diff)
downloadSLOF-8e92d6b3e8d6ac0625356f058da6ddae316e2cdc.zip
SLOF-8e92d6b3e8d6ac0625356f058da6ddae316e2cdc.tar.gz
SLOF-8e92d6b3e8d6ac0625356f058da6ddae316e2cdc.tar.bz2
Add virtio-serial device support
Add support for virtio serial device to be used as a console device. Currently, SLOF only supports spapr-vty device. With this addition virtio console can be used during boot. Signed-off-by: Nikunj A Dadhania <nikunj@linux.vnet.ibm.com> Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
Diffstat (limited to 'lib')
-rw-r--r--lib/libvirtio/Makefile2
-rw-r--r--lib/libvirtio/virtio-serial.c202
-rw-r--r--lib/libvirtio/virtio-serial.h27
-rw-r--r--lib/libvirtio/virtio.code33
-rw-r--r--lib/libvirtio/virtio.in6
5 files changed, 269 insertions, 1 deletions
diff --git a/lib/libvirtio/Makefile b/lib/libvirtio/Makefile
index bd6a1fa..87d9513 100644
--- a/lib/libvirtio/Makefile
+++ b/lib/libvirtio/Makefile
@@ -24,7 +24,7 @@ TARGET = ../libvirtio.a
all: $(TARGET)
-SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c
+SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c virtio-serial.c
OBJS = $(SRCS:%.c=%.o)
diff --git a/lib/libvirtio/virtio-serial.c b/lib/libvirtio/virtio-serial.c
new file mode 100644
index 0000000..6234346
--- /dev/null
+++ b/lib/libvirtio/virtio-serial.c
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * Copyright (c) 2016 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio serial device definitions.
+ * See Virtio 1.0 - 5.3 Console Device, for details
+ */
+#include <stdio.h>
+#include <string.h>
+#include <cpu.h>
+#include <helpers.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "virtio-serial.h"
+#include "virtio-internal.h"
+
+#define DRIVER_FEATURE_SUPPORT VIRTIO_F_VERSION_1
+#define RX_ELEM_SIZE 4
+#define RX_NUM_ELEMS 128
+
+#define RX_Q 0
+#define TX_Q 1
+
+static struct vqs vq_rx;
+static struct vqs vq_tx;
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+int virtio_serial_init(struct virtio_device *dev)
+{
+ struct vring_avail *vq_avail;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Reset device */
+ virtio_reset_device(dev);
+
+ /* Acknowledge device. */
+ virtio_set_status(dev, status);
+
+ /* Tell HV that we know how to drive the device. */
+ status |= VIRTIO_STAT_DRIVER;
+ virtio_set_status(dev, status);
+
+ if (dev->is_modern) {
+ /* Negotiate features and sets FEATURES_OK if successful */
+ if (virtio_negotiate_guest_features(dev, DRIVER_FEATURE_SUPPORT))
+ goto dev_error;
+
+ virtio_get_status(dev, &status);
+ }
+
+ if (virtio_queue_init_vq(dev, &vq_rx, RX_Q))
+ goto dev_error;
+
+ /* Allocate memory for multiple receive buffers */
+ vq_rx.buf_mem = SLOF_alloc_mem(RX_ELEM_SIZE * RX_NUM_ELEMS);
+ if (!vq_rx.buf_mem) {
+ printf("virtio-serial: Failed to allocate buffers!\n");
+ goto dev_error;
+ }
+
+ /* Prepare receive buffer queue */
+ for (int i = 0; i < RX_NUM_ELEMS; i++) {
+ uint64_t addr = (uint64_t)vq_rx.buf_mem + i * RX_ELEM_SIZE;
+
+ /* Descriptor for data: */
+ virtio_fill_desc(&vq_rx.desc[i], dev->is_modern, addr, 1, VRING_DESC_F_WRITE, 0);
+ vq_rx.avail->ring[i] = virtio_cpu_to_modern16(dev, i);
+ }
+ vq_rx.avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_rx.avail->idx = virtio_cpu_to_modern16(dev, RX_NUM_ELEMS);
+ sync();
+
+ last_rx_idx = virtio_modern16_to_cpu(dev, vq_rx.used->idx);
+
+ if (virtio_queue_init_vq(dev, &vq_tx, TX_Q))
+ goto dev_error;
+
+ vq_avail = virtio_get_vring_avail(dev, TX_Q);
+ vq_avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ return 1;
+ dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return 0;
+}
+
+void virtio_serial_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+int virtio_serial_putchar(struct virtio_device *dev, char c)
+{
+ struct vring_desc *desc;
+ int id;
+ uint32_t vq_size, time;
+ struct vring_desc *vq_desc;
+ struct vring_avail *vq_avail;
+ struct vring_used *vq_used;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+
+ vq_size = virtio_get_qsize(dev, TX_Q);
+ vq_desc = virtio_get_vring_desc(dev, TX_Q);
+ vq_avail = virtio_get_vring_avail(dev, TX_Q);
+ vq_used = virtio_get_vring_used(dev, TX_Q);
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq_avail->idx);
+
+ last_used_idx = vq_used->idx;
+ current_used_idx = &vq_used->idx;
+
+ /* Determine descriptor index */
+ id = avail_idx % vq_size;
+
+ /* Set up virtqueue descriptor for header */
+ desc = &vq_desc[id];
+ virtio_fill_desc(desc, dev->is_modern, (uint64_t)&c, 1, 0, 0);
+
+ vq_avail->ring[avail_idx % vq_size] = virtio_cpu_to_modern16 (dev, id);
+ mb();
+ vq_avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+
+ /* Tell HV that the queue is ready */
+ virtio_queue_notify(dev, TX_Q);
+
+ /* Wait for host to consume the descriptor */
+ time = SLOF_GetTimer() + VIRTIO_TIMEOUT;
+ while (*current_used_idx == last_used_idx) {
+ // do something better
+ mb();
+ if (time < SLOF_GetTimer()) {
+ printf("virtio_serial_putchar failed! \n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+char virtio_serial_getchar(struct virtio_device *dev)
+{
+ int id, idx;
+ char buf[RX_NUM_ELEMS] = {0};
+ uint16_t avail_idx;
+
+ idx = virtio_modern16_to_cpu(dev, vq_rx.used->idx);
+ if (last_rx_idx == idx) {
+ /* Nothing received yet */
+ return 0;
+ }
+
+ id = (virtio_modern32_to_cpu(dev, vq_rx.used->ring[last_rx_idx % vq_rx.size].id) + 1)
+ % vq_rx.size;
+
+ /* Copy data to destination buffer */
+ memcpy(buf, (void *)virtio_modern64_to_cpu(dev, vq_rx.desc[id - 1].addr), RX_ELEM_SIZE);
+
+ /* Move indices to next entries */
+ last_rx_idx = last_rx_idx + 1;
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq_rx.avail->idx);
+ vq_rx.avail->ring[avail_idx % vq_rx.size] = virtio_cpu_to_modern16(dev, id - 1);
+ sync();
+ vq_rx.avail->idx = virtio_cpu_to_modern16(dev, avail_idx + 1);
+ sync();
+
+ /* Tell HV that RX queue entry is ready */
+ virtio_queue_notify(dev, RX_Q);
+
+ return buf[0];
+}
+
+int virtio_serial_haschar(struct virtio_device *dev)
+{
+ if (last_rx_idx == virtio_modern16_to_cpu(dev, vq_rx.used->idx))
+ return 0;
+ else
+ return 1;
+}
diff --git a/lib/libvirtio/virtio-serial.h b/lib/libvirtio/virtio-serial.h
new file mode 100644
index 0000000..68af46d
--- /dev/null
+++ b/lib/libvirtio/virtio-serial.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * Copyright (c) 2016 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * Virtio serial device definitions.
+ * See Virtio 1.0 - 5.3 Console Device, for details
+ */
+
+#ifndef _VIRTIO_SERIAL_H
+#define _VIRTIO_SERIAL_H
+
+extern int virtio_serial_init(struct virtio_device *dev);
+extern void virtio_serial_shutdown(struct virtio_device *dev);
+extern int virtio_serial_putchar(struct virtio_device *dev, char c);
+extern char virtio_serial_getchar(struct virtio_device *dev);
+extern int virtio_serial_haschar(struct virtio_device *dev);
+
+#endif /* _VIRTIO_SERIAL_H */
diff --git a/lib/libvirtio/virtio.code b/lib/libvirtio/virtio.code
index 8eec8f0..5cfffcc 100644
--- a/lib/libvirtio/virtio.code
+++ b/lib/libvirtio/virtio.code
@@ -15,6 +15,7 @@
#include <virtio-9p.h>
#include <virtio-scsi.h>
#include <virtio-net.h>
+#include <virtio-serial.h>
/******** core virtio ********/
@@ -165,3 +166,35 @@ PRIM(virtio_X2d_net_X2d_write)
TOS.n = virtionet_write(TOS.a, len);
}
MIRP
+
+/*********** virtio-serial ***********/
+// : virtio-serial-init ( dev -- false | true)
+PRIM(virtio_X2d_serial_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtio_serial_init(dev);
+MIRP
+
+// : virtio-serial-shutdown ( dev -- )
+PRIM(virtio_X2d_serial_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtio_serial_shutdown(dev);
+MIRP
+
+// : virtio-serial-putchar ( dev char -- )
+PRIM(virtio_X2d_serial_X2d_putchar)
+ unsigned long c = TOS.n; POP;
+ void *dev = TOS.a; POP;
+ virtio_serial_putchar(dev, c);
+MIRP
+
+// : virtio-serial-getchar ( dev -- char)
+PRIM(virtio_X2d_serial_X2d_getchar)
+ void *dev = TOS.a;
+ TOS.n = virtio_serial_getchar(dev);
+MIRP
+
+// : virtio-serial-haschar ( dev -- true | false)
+PRIM(virtio_X2d_serial_X2d_haschar)
+ void *dev = TOS.a;
+ TOS.n = virtio_serial_haschar(dev);
+MIRP
diff --git a/lib/libvirtio/virtio.in b/lib/libvirtio/virtio.in
index 195840e..d2b1641 100644
--- a/lib/libvirtio/virtio.in
+++ b/lib/libvirtio/virtio.in
@@ -33,3 +33,9 @@ cod(virtio-net-open)
cod(virtio-net-close)
cod(virtio-net-read)
cod(virtio-net-write)
+
+cod(virtio-serial-init)
+cod(virtio-serial-shutdown)
+cod(virtio-serial-putchar)
+cod(virtio-serial-getchar)
+cod(virtio-serial-haschar)