aboutsummaryrefslogtreecommitdiffstats
path: root/roms/SLOF/lib/libvirtio
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/SLOF/lib/libvirtio
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/SLOF/lib/libvirtio')
-rw-r--r--roms/SLOF/lib/libvirtio/Makefile55
-rw-r--r--roms/SLOF/lib/libvirtio/p9.c575
-rw-r--r--roms/SLOF/lib/libvirtio/p9.h68
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-9p.c340
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-9p.h32
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-blk.c209
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-blk.h61
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-internal.h48
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-net.c384
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-net.h40
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-scsi.c152
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-scsi.h69
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-serial.c202
-rw-r--r--roms/SLOF/lib/libvirtio/virtio-serial.h27
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.c645
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.code203
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.h132
-rw-r--r--roms/SLOF/lib/libvirtio/virtio.in41
18 files changed, 3283 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libvirtio/Makefile b/roms/SLOF/lib/libvirtio/Makefile
new file mode 100644
index 000000000..87d951390
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/Makefile
@@ -0,0 +1,55 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 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
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+LDFLAGS = -nostdlib
+
+TARGET = ../libvirtio.a
+
+
+all: $(TARGET)
+
+SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c virtio-scsi.c virtio-net.c virtio-serial.c
+
+OBJS = $(SRCS:%.c=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.S
+ $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/roms/SLOF/lib/libvirtio/p9.c b/roms/SLOF/lib/libvirtio/p9.c
new file mode 100644
index 000000000..e9ba22886
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/p9.c
@@ -0,0 +1,575 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <byteorder.h>
+#include "p9.h"
+
+
+/* Protocol stack marshaling. */
+uint8_t *sp;
+
+#define GET_08(s,i) (s)[(i)]
+#define GET_16(s,i) le16_to_cpu(*(uint16_t*)(&(s)[(i)]))
+#define GET_32(s,i) le32_to_cpu(*(uint32_t*)(&(s)[(i)]))
+#define GET_64(s,i) le64_to_cpu(*(uint64_t*)(&(s)[(i)]))
+
+#define SET_08(s,i,v) (s)[(i)] = (v)
+#define SET_16(s,i,v) *(uint16_t*)(&(s)[(i)]) = cpu_to_le16(v)
+#define SET_32(s,i,v) *(uint32_t*)(&(s)[(i)]) = cpu_to_le32(v)
+#define SET_64(s,i,v) *(uint64_t*)(&(s)[(i)]) = cpu_to_le64(v)
+
+#define PUT_08(v) sp[0] = (v);sp+=1
+#define PUT_16(v) *(uint16_t*)(&sp[0]) = cpu_to_le16(v);sp+=2
+#define PUT_32(v) *(uint32_t*)(&sp[0]) = cpu_to_le32(v);sp+=4
+#define PUT_64(v) *(uint64_t*)(&sp[0]) = cpu_to_le64(v);sp+=8
+
+#define PUT_HD(m,t) PUT_32(0);PUT_08(m);PUT_16(t)
+#define PUT_SN(v,n) PUT_16(n);memcpy(sp,(v),(n));sp+=n
+#define PUT_ST(v) PUT_16(strlen(v));memcpy(sp,(v),strlen(v));\
+ sp+=strlen(v)
+
+#define GET_SIZE (sp - tx)
+
+
+/* General defines. */
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+#define NOTAG ((uint16_t)~0)
+#define NOFID ((uint32_t)~0)
+#define TAG 1
+#define BUF_SIZE (8*1024)
+
+#define VERSION "9P2000.u"
+#define UNKNOWN_VER "unknown"
+
+#define MSG_SIZE 0
+#define MSG_ID 4
+#define MSG_ERR 0x6b
+#define MSG_ERR_STR 9
+#define MSG_ERR_STR_LEN 7
+#define MSG_TAG 5
+#define MSG_VER_MSIZE 7
+#define MSG_VER_STR_LEN 11
+#define MSG_VER_STR 13
+#define MSG_WALK_TX_ELMT 15
+#define MSG_WALK_RX_ELMT 7
+#define MSG_SIZE 0
+#define MSG_WALK_MAX_ELMT 16
+#define MSG_QID_SIZE 13
+#define MSG_WALK_RX_HDR_SIZE 9
+#define MSG_OPEN_IOUNIT 20
+#define MSG_OPEN_MODE_MASK 0x5f
+#define MSG_READ_COUNT 7
+#define MSG_READ_DATA 11
+#define MSG_STAT_LEN 42
+#define MSG_STAT_TYPE 17
+
+#define T_VERSION 100
+#define R_VERSION (T_VERSION + 1)
+#define T_ATTACH 104
+#define R_ATTACH (T_ATTACH + 1)
+#define T_ERROR 106
+#define R_ERROR (T_ERROR + 1)
+#define T_WALK 110
+#define R_WALK (T_WALK + 1)
+#define T_OPEN 112
+#define R_OPEN (T_OPEN + 1)
+#define T_READ 116
+#define R_READ (T_READ + 1)
+#define T_CLUNK 120
+#define R_CLUNK (T_CLUNK + 1)
+#define T_STAT 124
+#define R_STAT (T_STAT + 1)
+
+static p9_transact_t transact;
+static void *transact_opaque;
+static uint8_t *tx;
+static uint8_t *rx;
+
+
+/**
+ * p9_reg_transport
+ *
+ * Registers a transport function for use by the P9 protocol. The transport
+ * connects the P9 Client (this library) to a server instance.
+ *
+ * @param transact_func[in] Function pointer to type p9_transact_t.
+ * @param tx_buffer[in] TX buffer, must be 8k in size.
+ * @param rx_buffer[in] RX buffer, must be 8k in size.
+ */
+void p9_reg_transport(p9_transact_t transact_func, void *opaque,
+ uint8_t *tx_buffer, uint8_t *rx_buffer)
+{
+ transact = transact_func;
+ transact_opaque = opaque;
+ tx = tx_buffer;
+ rx = rx_buffer;
+}
+
+/**
+ * reset_buffers
+ *
+ * Reset the RX and TX buffers to BUF_SIZE (8k) and reset the Stack Pointer
+ * for the TX buffer, which is referenced by the PUT_* macro's.
+ */
+void reset_buffers(void)
+{
+ memset(tx, 0, BUF_SIZE);
+ memset(rx, 0, BUF_SIZE);
+ sp = tx;
+}
+
+/**
+ * p9_transaction
+ *
+ * Perform a transaction (send/recv) over the registered transport.
+ *
+ * @param connection[in|out] Connection object.
+ * @return 0 = success, -ve = error.
+ */
+int p9_transaction(p9_connection_t *connection)
+{
+ int rc;
+ uint32_t tx_size = GET_SIZE;
+ uint32_t rx_size = connection->message_size;
+
+ if (transact == NULL) {
+ return P9_NO_TRANSPORT;
+ }
+ if (tx == NULL || rx == NULL) {
+ return P9_NO_BUFFER;
+ }
+ if (connection->message_size > BUF_SIZE) {
+ return P9_MSG_SIZE_TOO_BIG;
+ }
+ if (tx_size > connection->message_size) {
+ return P9_MSG_TOO_LONG;
+ }
+
+ SET_32(tx, MSG_SIZE, tx_size);
+ rc = transact(transact_opaque, tx, tx_size, rx, &rx_size);
+
+ if (rc != 0) {
+ return P9_TRANSPORT_ERROR;
+ }
+ if (GET_16(tx, MSG_TAG) != GET_16(rx, MSG_TAG)) {
+ return P9_UNEXPECTED_TAG;
+ }
+ if (GET_08(rx, MSG_ID) == MSG_ERR) {
+ char error_string[200];
+
+ memset(error_string, 0, 200);
+ strncpy(error_string, (char *)&rx[MSG_ERR_STR],
+ MIN(200 - 1, GET_16(rx, MSG_ERR_STR_LEN)));
+#ifndef TEST
+ printf("\nError: %s\n", error_string);
+#endif
+ return P9_R_ERROR;
+ }
+ if ((GET_08(tx, MSG_ID) + 1) != GET_08(rx, MSG_ID)) {
+ return P9_UNEXPECTED_MSG;
+ }
+
+ return 0;
+}
+
+/**
+ * p9_version
+ *
+ * Called to start a session. Negotiates the maximum message size for the
+ * P9 protocol.
+ *
+ * @param connection[in|out] Connection object, contains message_size.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tversion tag[2] msize[4] version[s]
+ * size[4] Rversion tag[2] msize[4] version[s]
+ */
+int p9_version(p9_connection_t *connection)
+{
+ int rc;
+ char *ver_str;
+ int ver_len;
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_VERSION, NOTAG);
+ PUT_32(connection->message_size);
+ PUT_ST(VERSION);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ connection->message_size = MIN(connection->message_size,
+ GET_32(rx, MSG_VER_MSIZE));
+
+ ver_str = (char *)&rx[MSG_VER_STR];
+ ver_len = GET_16(rx, MSG_VER_STR_LEN);
+ if (strncmp(UNKNOWN_VER, ver_str, ver_len) == 0) {
+ return P9_UNKNOWN_VERSION;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_attach
+ *
+ * Called to open a connection for a user to a file tree on the server. There
+ * is no authorisation undertaken (NOFID).
+ *
+ * @param connection[in|out] Connection object, contains uname and aname as
+ * well as the connection fid and returned qid.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4]
+ * size[4] Rattach tag[2] qid[13]
+ */
+int p9_attach(p9_connection_t *connection)
+{
+ int rc;
+ unsigned length = 19 + strlen(connection->uname) + strlen(connection->aname);
+
+ if (length > connection->message_size) {
+ return P9_MSG_TOO_LONG;
+ }
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_ATTACH, TAG);
+ PUT_32(connection->fid);
+ PUT_32(NOFID);
+ PUT_ST(connection->uname);
+ PUT_ST(connection->aname);
+ PUT_32(~0); /* ??? */
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_clunk
+ *
+ * Called when closing a file or connection (or after failed opens). Tells the
+ * server that the supplied fid is no longer needed by this client.
+ *
+ * @param connection[in|out] Connection object.
+ * @param fid[in] Fid to be clunked (released) on the server.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tclunk tag[2] fid[4]
+ * size[4] Rclunk tag[2]
+ */
+int p9_clunk(p9_connection_t *connection, uint32_t fid)
+{
+ int rc;
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_CLUNK, TAG);
+ PUT_32(fid);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+
+ return 0;
+}
+
+/**
+ * p9_walk
+ *
+ * Walk the provided path to a file (or directory) starting at the directory
+ * indicated by fid and assigning new_fid to the last successfully walked
+ * element. If not all elements of the path can be walked then the pos
+ * pointer is set to the part of the path following the last successful
+ * walked element. The function can be called again to walk the remainder
+ * of the path (or produce an error).
+ *
+ * @param connection[in] Connection object.
+ * @param fid[in] Fid to start walk from, must be directory or root (from
+ * call to p9_attach).
+ * @param new_fid[in] Fid to be used for the last walked element.
+ * @param pos[in|out] Position in path that remains to be walked. If the
+ * path was completely walked without error this will point to the NULL
+ * at the end of path.
+ * @return 1 = partial walk, 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s])
+ * size[4] Rwalk tag[2] nwqid[2] nwqid*(qid[13])
+ */
+int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid,
+ uint8_t **pos)
+{
+ int rc;
+ const char *path = (const char *)*pos;
+ uint8_t *s_tok;
+ uint8_t *e_tok;
+ int element_count = 0;
+
+ if (path == NULL) {
+ *pos = NULL;
+ return P9_NULL_PATH;
+ }
+
+ reset_buffers();
+
+ /* Build message. */
+ PUT_HD(T_WALK, TAG); /* Length to 0, set later. */
+ PUT_32(fid);
+ PUT_32(new_fid);
+ PUT_16(0); /* Element count to 0, set later. */
+
+ /* Get elements from path, and append to message. */
+ s_tok = (uint8_t *)path;
+ e_tok = s_tok;
+
+ while (*s_tok != 0) {
+ while (*s_tok == '/') {
+ s_tok++;
+ }
+ e_tok = s_tok;
+ while ((*e_tok != '/') && (*e_tok != 0)) {
+ e_tok++;
+ }
+
+ /* Check the element is OK. */
+ if (strncmp(".", (const char *)s_tok, (e_tok - s_tok)) == 0) {
+ /* Don't send ".", continue to next. */
+ s_tok = e_tok;
+ continue;
+ }
+ unsigned tx_size = (e_tok - s_tok + 2 + GET_SIZE);
+ unsigned rx_size = ((element_count + 1) * MSG_QID_SIZE
+ + MSG_WALK_RX_HDR_SIZE);
+ if ((tx_size > connection->message_size)
+ || (rx_size > connection->message_size)) {
+ /*
+ * Element makes TX msg too long OR expected RX msg
+ * too long. Move pos to previous element and do
+ * partial walk.
+ */
+ e_tok = s_tok;
+ if (*(e_tok - 1) == '/') {
+ e_tok--;
+ }
+ break;
+ }
+
+ /* Add the element to the message. */
+ PUT_SN(s_tok, e_tok - s_tok);
+ element_count++;
+
+ /* Server supports no more than 16 elements, partial walk. */
+ if (element_count == MSG_WALK_MAX_ELMT) {
+ break;
+ }
+
+ /* Ready to find the next element. */
+ s_tok = e_tok;
+ }
+
+ if ((element_count == 0) && (strlen(path) > 0)) {
+ return P9_PATH_ELEMENT_TOO_LONG;
+ }
+
+ *pos = e_tok;
+
+ /* Update counts and then send message. */
+ SET_16(tx, MSG_WALK_TX_ELMT, element_count);
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Check for special return conditions. */
+ if (element_count != GET_16(rx, MSG_WALK_RX_ELMT)) {
+ /* Find the last element successfully walked */
+ s_tok = (uint8_t *)path;
+ e_tok = s_tok;
+ element_count = GET_16(rx, MSG_WALK_RX_ELMT);
+
+ while (element_count--) {
+ while (*s_tok == '/') {
+ s_tok++;
+ }
+
+ e_tok = s_tok;
+
+ while ((*e_tok != '/') && (*e_tok != 0)) {
+ e_tok++;
+ }
+
+ s_tok = e_tok;
+ }
+
+ *pos = e_tok;
+ }
+ if (**pos != 0) {
+ rc = P9_PARTIAL_WALK;
+ }
+
+
+ return rc;
+}
+
+/**
+ * p9_open
+ *
+ * Opens the file represented by fid with associated mode bit mask. The iounit
+ * size returned from the server is written to the connection object.
+ *
+ * @param file[in|out] File object, contains fid for file.
+ * @param mode[in] Mode to open with. Bit's 0=R, 1=W, 2=RW, 3=EX, 4=Trunc
+ * and 6=Delete on Close.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Topen tag[2] fid[4] mode[1]
+ * size[4] Ropen tag[2] qid[13] iounit[4]
+ */
+int p9_open(p9_file_t *file, uint8_t mode)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+
+ reset_buffers();
+ file->iounit = 0;
+
+ /* Build message. */
+ PUT_HD(T_OPEN, TAG);
+ PUT_32(file->fid);
+ PUT_08(mode & MSG_OPEN_MODE_MASK);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ file->iounit = GET_32(rx, MSG_OPEN_IOUNIT);
+
+
+ return 0;
+}
+
+/**
+ * p9_read
+ *
+ * Reads the file in to buffer.
+ *
+ * @param file[in] File object, contains fid for file.
+ * @param buffer[out] Buffer for data.
+ * @param count[in] Number of bytes to read (less bytes than requested
+ * may be read).
+ * @param offset[in] Offset in file to read from.
+ * @return Bytes read, -ve = error.
+ *
+ * @remark
+ * size[4] Tread tag[2] fid[4] offset[8] count[4]
+ * size[4] Rread tag[2] count[4] data[count]
+ */
+int p9_read(p9_file_t *file, uint8_t *buffer,
+ uint32_t count, uint64_t offset)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+ uint32_t got;
+
+ reset_buffers();
+ count = MIN((connection->message_size - MSG_READ_DATA), count);
+
+ /* Build message. */
+ PUT_HD(T_READ, TAG);
+ PUT_32(file->fid);
+ PUT_64(offset);
+ PUT_32(count);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+ got = GET_32(rx, MSG_READ_COUNT);
+ if (got > count) {
+ return P9_READ_UNEXPECTED_DATA;
+ }
+
+ /* Handle response. */
+ memcpy(buffer, &rx[MSG_READ_DATA], got);
+
+ return got;
+}
+
+/**
+ * p9_stat
+ *
+ * Stat's the fid and writes the type and length to the file object.
+ *
+ * @param file[in|out] File object, contains fid for file.
+ * @return 0 = success, -ve = error.
+ *
+ * @remark
+ * size[4] Tstat tag[2] fid[4]
+ * size[4] Rstat tag[2] size[2] stat[n]
+ */
+int p9_stat(p9_file_t *file)
+{
+ int rc;
+ p9_connection_t *connection = file->connection;
+
+ reset_buffers();
+ file->length = 0;
+ file->type = 0;
+
+ /* Build message. */
+ PUT_HD(T_STAT, TAG);
+ PUT_32(file->fid);
+
+ /* Send message. */
+ rc = p9_transaction(connection);
+ if (rc != 0) {
+ return rc;
+ }
+
+ /* Handle response. */
+ file->length = GET_64(rx, MSG_STAT_LEN);
+ file->type = GET_08(rx, MSG_STAT_TYPE);
+
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libvirtio/p9.h b/roms/SLOF/lib/libvirtio/p9.h
new file mode 100644
index 000000000..3a35e80ed
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/p9.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#ifndef P9_H
+#define P9_H
+
+#include <stdint.h>
+
+
+#define P9_ERROR -1
+#define P9_UNKNOWN_VERSION -2
+#define P9_R_ERROR -3
+#define P9_MSG_TOO_LONG -4
+#define P9_UNEXPECTED_MSG -5
+#define P9_UNEXPECTED_TAG -6
+#define P9_TRANSPORT_ERROR -7
+#define P9_NO_TRANSPORT -8
+#define P9_NULL_PATH -9
+#define P9_PATH_ELEMENT_TOO_LONG -10
+#define P9_READ_UNEXPECTED_DATA -11
+#define P9_NO_BUFFER -12
+#define P9_MSG_SIZE_TOO_BIG -13
+
+#define P9_PARTIAL_WALK 1
+
+typedef int (*p9_transact_t)(void *opaque, uint8_t *tx, int tx_size,
+ uint8_t *rx, uint32_t *rx_size);
+
+typedef struct {
+ uint32_t message_size;
+ char *uname; /* User name. */
+ char *aname; /* Tree/mount name/path. */
+ uint32_t fid; /* Represents mount point. */
+} p9_connection_t;
+
+typedef struct {
+ uint32_t fid; /* Identifies the file to P9 server. */
+ uint32_t iounit; /* Maximum read size in bytes. */
+ uint8_t type; /* Type of file. */
+ uint64_t length; /* Length of file. */
+ p9_connection_t *connection;
+} p9_file_t;
+
+
+void reset_buffers(void);
+void p9_reg_transport(p9_transact_t transact_func, void *opaque,
+ uint8_t *tx_buffer, uint8_t *rx_buffer);
+int p9_transaction(p9_connection_t *connection);
+int p9_version(p9_connection_t *connection);
+int p9_attach(p9_connection_t *connection);
+int p9_clunk(p9_connection_t *connection, uint32_t fid);
+int p9_walk(p9_connection_t *connection, uint32_t fid, uint32_t new_fid,
+ uint8_t **pos);
+int p9_open(p9_file_t *file, uint8_t mode);
+int p9_read(p9_file_t *file, uint8_t *buffer,
+ uint32_t count, uint64_t offset);
+int p9_stat(p9_file_t *file);
+
+#endif
diff --git a/roms/SLOF/lib/libvirtio/virtio-9p.c b/roms/SLOF/lib/libvirtio/virtio-9p.c
new file mode 100644
index 000000000..76078612b
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-9p.c
@@ -0,0 +1,340 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <byteorder.h>
+#include <cpu.h>
+
+#include "virtio-9p.h"
+#include "p9.h"
+#include "virtio-internal.h"
+
+/**
+ * Notes for 9P Server config:
+ *
+ * make distclean; cm make qemu
+ * sudo cp boot_rom.bin /opt/qemu/share/qemu/slof.bin
+ * /opt/qemu/bin/qemu-system-ppc64 -M pseries -m 512 -boot d -nographic -fsdev
+ * local,id=trule,path=/home/trule/virtfs,security_model=none -device
+ * virtio-9p-spapr,fsdev=trule,mount_tag=trule
+ * load virtfs:\some\file
+ */
+
+/* We support only one instance due to the (ab)use of globals. We
+ * use the buffer size as an open marker as well.
+ */
+static int __buf_size;
+
+
+#define ROOT_FID 1
+#define FILE_FID 2
+#define TAG_SIZE 128
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define dprintf(_x ...) do { printf(_x); } while(0)
+#else
+#define dprintf(_x ...)
+#endif
+
+#ifdef DEBUG
+static void dprint_buffer(const char *name, uint8_t *buffer, int length)
+{
+ int i;
+
+ printf("*** %s ***", name);
+
+ for (i = 0; i < length; i++) {
+ if (i % 16 == 0) {
+ printf("\n %04x:", i);
+ }
+
+ printf(" %02x", buffer[i]);
+ }
+
+ printf("\n");
+}
+#else
+#define dprint_buffer(n, b, l)
+#endif
+
+/**
+ * virtio_9p_transact
+ *
+ * Perform a 9P transaction over the VIRTIO queue interface. This function is
+ * registered with the p9.c library via p9_reg_transport() to provide
+ * connectivity to the 9P server.
+ *
+ * @param tx[in] Data to send, mapped to first queue item.
+ * @param tx_size[in] Size of data to send.
+ * @param rx[out] Data to receive, mappend to second queue item.
+ * @param rx_size[out] Size of data received.
+ * @return 0 = success, -ve = error.
+ */
+static int virtio_9p_transact(void *opaque, uint8_t *tx, int tx_size, uint8_t *rx,
+ uint32_t *rx_size)
+{
+ struct virtio_device *dev = opaque;
+ int id, i;
+ uint32_t vq_size;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ struct vqs *vq = &dev->vq[0];
+
+ /* Virt IO queues. */
+ vq_size = virtio_get_qsize(dev, 0);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ /* Determine descriptor index */
+ id = (avail_idx * 3) % vq_size;
+
+ /* TX in first queue item. */
+ dprint_buffer("TX", tx, tx_size);
+
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)tx, tx_size,
+ VRING_DESC_F_NEXT, id + 1);
+
+ /* RX in the second queue item. */
+ virtio_fill_desc(vq, id + 1, dev->features, (uint64_t)rx,
+ *rx_size,
+ VRING_DESC_F_WRITE, 0);
+
+ /* Tell HV that the queue is ready */
+ 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);
+ virtio_queue_notify(dev, 0);
+
+ /* Receive the response. */
+ i = 10000000;
+ while (*current_used_idx == last_used_idx && i-- > 0) {
+ // do something better
+ mb();
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+
+ if (i == 0) {
+ return -1;
+ }
+
+ *rx_size = MIN(*rx_size, le32_to_cpu(*(uint32_t*)(&rx[0])));
+ dprint_buffer("RX", rx, *rx_size);
+
+ return 0;
+}
+
+/**
+ * virtio_9p_init
+ *
+ * Establish the VIRTIO connection for use with the 9P server. Setup queues
+ * and negotiate capabilities. Setup the 9P (Client) library.
+ *
+ * @param reg[in] Pointer to device tree node for VIRTIO/9P interface.
+ * @param tx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
+ * @param rx_buf[in] TX buffer for use by 9P Client lib - 8K in size.
+ * @param buf_size Somewhat redundant, buffer size expected to be 8k.
+ * @return 0 = success, -ve = error.
+ */
+int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf,
+ int buf_size)
+{
+ struct vqs *vq;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Check for double open */
+ if (__buf_size)
+ return -1;
+ __buf_size = buf_size;
+
+ dprintf("%s : device at %p\n", __func__, dev->base);
+ dprintf("%s : type is %04x\n", __func__, dev->type);
+
+ 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);
+
+ /* Device specific setup - we do not support special features */
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1))
+ goto dev_error;
+ virtio_get_status(dev, &status);
+ } else {
+ virtio_set_guest_features(dev, 0);
+ }
+
+ vq = virtio_queue_init_vq(dev, 0);
+ if (!vq)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ /* Setup 9P library. */
+ p9_reg_transport(virtio_9p_transact, dev,(uint8_t *)tx_buf,
+ (uint8_t *)rx_buf);
+
+ dprintf("%s : complete\n", __func__);
+ return 0;
+
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return -1;
+}
+
+/**
+ * virtio_9p_shutdown
+ */
+void virtio_9p_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+
+ __buf_size = 0;
+}
+
+/**
+ * virtio_9p_load
+ *
+ * Read a file from the 9P Server on the VIRTIO interface.
+ *
+ * @param file_name[in] File to read, use Linux style paths.
+ * @param buffer[out] Where to read the file to.
+ * @return +ve = amount of data read, -ve = error.
+ */
+long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer)
+{
+ int rc;
+ uint16_t tag_len;
+ char tag_name[TAG_SIZE];
+ uint64_t offset = 0;
+ uint8_t *pos = (uint8_t *)file_name;
+ int start_fid = ROOT_FID;
+ p9_connection_t connection = {
+ .message_size = __buf_size,
+ .fid = ROOT_FID,
+ .uname = "slof"
+ };
+ p9_file_t file = {
+ .connection = &connection,
+ .fid = FILE_FID,
+ };
+
+
+ /* Get the share name from 9P config space. */
+ tag_len = virtio_get_config(dev, 0, sizeof(tag_len));
+ if (tag_len >= TAG_SIZE)
+ tag_len = TAG_SIZE - 1;
+ __virtio_read_config(dev, tag_name, 2, tag_len);
+ connection.aname = tag_name;
+
+ /* Connect to the 9P server. */
+ dprintf("%s : connecting, tag = %s, user = %s, msgsize = %d\n",
+ __func__, connection.aname, connection.uname,
+ connection.message_size);
+ rc = p9_version(&connection);
+ if (rc != 0) {
+ printf("Version check failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+ rc = p9_attach(&connection);
+ if (rc != 0) {
+ printf("Attach failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+ dprintf("%s : connected, msgsize = %d\n", __func__,
+ connection.message_size);
+
+ /* Walk to the file. */
+ do {
+ dprintf("%s : walk path %s\n", __func__, pos);
+ rc = p9_walk(&connection, start_fid, FILE_FID, &pos);
+
+ if (rc < 0) { /* Some error. */
+ printf("Walk failed, rc = %d\n", rc);
+ goto cleanup_connection;
+ }
+
+ /*
+ * If partial walk (*pos != 0) then continue the walk from
+ * mid point with start_fid updated to current position
+ * FILE_FID. FILE_FID will then be reused for the result of
+ * the next call to walk.
+ */
+ start_fid = FILE_FID;
+ } while (*pos != 0);
+
+ /* Open the file. */
+ dprintf("%s : stat and open\n", __func__);
+ rc = p9_stat(&file);
+ if (rc != 0) {
+ printf("Stat failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ rc = p9_open(&file, 0x00); /* TODO find include for "read mode" */
+ if (rc != 0) {
+ printf("Open failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ dprintf("%s : file opened, size %lld\n", __func__, file.length);
+
+ /* Read the file contents to buffer. */
+ while (offset < file.length) {
+ dprintf("%s : read from offset %llu\n", __func__, offset);
+ rc = p9_read(&file, buffer + offset,
+ file.length - offset, offset);
+ dprintf("%s : read done, length was %d\n", __func__, rc);
+ if (rc < 0) {
+ printf("Read failed, rc = %d\n", rc);
+ goto cleanup_file;
+ }
+ if (rc == 0) {
+ break;
+ }
+ offset += rc;
+ rc = 0;
+ }
+
+ /* Cleanup and disconnect. */
+cleanup_file:
+ dprintf("%s : clunking file\n", __func__);
+ p9_clunk(&connection, file.fid);
+
+cleanup_connection:
+ dprintf("%s : clunking connection\n", __func__);
+ p9_clunk(&connection, connection.fid);
+
+
+ dprintf("%s : complete, read %llu bytes\n", __func__, offset);
+ return rc == 0 ? (long)offset : rc;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-9p.h b/roms/SLOF/lib/libvirtio/virtio-9p.h
new file mode 100644
index 000000000..db2cf6f11
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-9p.h
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#ifndef VIRTIO_9P_H_
+#define VIRTIO_9P_H_
+
+#include <stdint.h>
+
+#include "virtio.h"
+
+#if 0
+typedef struct {
+ uint16_t tag_lenth;
+ char tag[0];
+} virtio_9p_config_t;
+#endif
+int virtio_9p_init(struct virtio_device *dev, void *tx_buf, void *rx_buf,
+ int buf_size);
+void virtio_9p_shutdown(struct virtio_device *dev);
+long virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer);
+
+
+#endif /* VIRTIO_9P_H_ */
diff --git a/roms/SLOF/lib/libvirtio/virtio-blk.c b/roms/SLOF/lib/libvirtio/virtio-blk.c
new file mode 100644
index 000000000..0363038e5
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-blk.c
@@ -0,0 +1,209 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <cpu.h>
+#include <helpers.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "virtio-blk.h"
+#include "virtio-internal.h"
+
+#define DEFAULT_SECTOR_SIZE 512
+#define DRIVER_FEATURE_SUPPORT (VIRTIO_BLK_F_BLK_SIZE | VIRTIO_F_VERSION_1)
+
+/**
+ * Initialize virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+int
+virtioblk_init(struct virtio_device *dev)
+{
+ struct vqs *vq;
+ int blk_size = DEFAULT_SECTOR_SIZE;
+ uint64_t features;
+ 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->features & VIRTIO_F_VERSION_1) {
+ /* 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);
+ } else {
+ /* Device specific setup - we support F_BLK_SIZE */
+ virtio_set_guest_features(dev, VIRTIO_BLK_F_BLK_SIZE);
+ }
+
+ vq = virtio_queue_init_vq(dev, 0);
+ if (!vq)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ features = virtio_get_host_features(dev);
+ if (features & VIRTIO_BLK_F_BLK_SIZE) {
+ blk_size = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, blk_size),
+ sizeof(blk_size));
+ }
+
+ return blk_size;
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return 0;
+}
+
+
+/**
+ * Shutdown the virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+void
+virtioblk_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+static void fill_blk_hdr(struct virtio_blk_req *blkhdr, bool is_modern,
+ uint32_t type, uint32_t ioprio, uint32_t sector)
+{
+ if (is_modern) {
+ blkhdr->type = cpu_to_le32(type);
+ blkhdr->ioprio = cpu_to_le32(ioprio);
+ blkhdr->sector = cpu_to_le64(sector);
+ } else {
+ blkhdr->type = type;
+ blkhdr->ioprio = ioprio;
+ blkhdr->sector = sector;
+ }
+}
+
+/**
+ * Read / write blocks
+ * @param reg pointer to "reg" property
+ * @param buf pointer to destination buffer
+ * @param blocknum block number of the first block that should be transfered
+ * @param cnt amount of blocks that should be transfered
+ * @param type VIRTIO_BLK_T_OUT for write, VIRTIO_BLK_T_IN for read transfers
+ * @return number of blocks that have been transfered successfully
+ */
+int
+virtioblk_transfer(struct virtio_device *dev, char *buf, uint64_t blocknum,
+ long cnt, unsigned int type)
+{
+ int id;
+ static struct virtio_blk_req blkhdr;
+ //struct virtio_blk_config *blkconf;
+ uint64_t capacity;
+ uint32_t time;
+ struct vqs *vq = &dev->vq[0];
+ volatile uint8_t status = -1;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ int blk_size = DEFAULT_SECTOR_SIZE;
+
+ //printf("virtioblk_transfer: dev=%p buf=%p blocknum=%lli cnt=%li type=%i\n",
+ // dev, buf, blocknum, cnt, type);
+
+ /* Check whether request is within disk capacity */
+ capacity = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, capacity),
+ sizeof(capacity));
+ if (blocknum + cnt - 1 > capacity) {
+ puts("virtioblk_transfer: Access beyond end of device!");
+ return 0;
+ }
+
+ blk_size = virtio_get_config(dev,
+ offset_of(struct virtio_blk_cfg, blk_size),
+ sizeof(blk_size));
+ if (blk_size % DEFAULT_SECTOR_SIZE) {
+ fprintf(stderr, "virtio-blk: Unaligned sector size %d\n", blk_size);
+ return 0;
+ }
+
+ avail_idx = virtio_modern16_to_cpu(dev, vq->avail->idx);
+
+ last_used_idx = vq->used->idx;
+ current_used_idx = &vq->used->idx;
+
+ /* Set up header */
+ fill_blk_hdr(&blkhdr, dev->features, type | VIRTIO_BLK_T_BARRIER,
+ 1, blocknum * blk_size / DEFAULT_SECTOR_SIZE);
+
+ /* Determine descriptor index */
+ id = (avail_idx * 3) % vq->size;
+
+ /* Set up virtqueue descriptor for header */
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)&blkhdr,
+ sizeof(struct virtio_blk_req),
+ VRING_DESC_F_NEXT, id + 1);
+
+ /* Set up virtqueue descriptor for data */
+ virtio_fill_desc(vq, id + 1, dev->features, (uint64_t)buf,
+ cnt * blk_size,
+ VRING_DESC_F_NEXT | ((type & 1) ? 0 : VRING_DESC_F_WRITE),
+ id + 2);
+
+ /* Set up virtqueue descriptor for status */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)&status, 1,
+ VRING_DESC_F_WRITE, 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, 0);
+
+ /* 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())
+ break;
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+ virtio_free_desc(vq, id + 2, dev->features);
+
+ if (status == 0)
+ return cnt;
+
+ printf("virtioblk_transfer failed! type=%i, status = %i\n",
+ type, status);
+
+ return 0;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-blk.h b/roms/SLOF/lib/libvirtio/virtio-blk.h
new file mode 100644
index 000000000..21f0adca0
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-blk.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * Copyright (c) 2011 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 block device definitions.
+ * See Virtio Spec, Appendix D, for details
+ */
+
+#ifndef _VIRTIO_BLK_H
+#define _VIRTIO_BLK_H
+
+#include <stdint.h>
+
+
+struct virtio_blk_cfg {
+ uint64_t capacity;
+ uint32_t size_max;
+ uint32_t seg_max;
+ struct virtio_blk_geometry {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } geometry;
+ uint32_t blk_size;
+ uint32_t sectors_max;
+} __attribute__ ((packed)) ;
+
+/* Block request */
+struct virtio_blk_req {
+ uint32_t type ;
+ uint32_t ioprio ;
+ uint64_t sector ;
+};
+
+/* Block request types */
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_SCSI_CMD 2
+#define VIRTIO_BLK_T_SCSI_CMD_OUT 3
+#define VIRTIO_BLK_T_FLUSH 4
+#define VIRTIO_BLK_T_FLUSH_OUT 5
+#define VIRTIO_BLK_T_BARRIER 0x80000000
+
+/* VIRTIO_BLK Feature bits */
+#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
+
+extern int virtioblk_init(struct virtio_device *dev);
+extern void virtioblk_shutdown(struct virtio_device *dev);
+extern int virtioblk_transfer(struct virtio_device *dev, char *buf,
+ uint64_t blocknum, long cnt, unsigned int type);
+
+#endif /* _VIRTIO_BLK_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-internal.h b/roms/SLOF/lib/libvirtio/virtio-internal.h
new file mode 100644
index 000000000..fe59c6b99
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-internal.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ * 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
+ *****************************************************************************/
+
+#ifndef _LIBVIRTIO_INTERNAL_H
+#define _LIBVIRTIO_INTERNAL_H
+
+#include <byteorder.h>
+
+static inline uint16_t virtio_cpu_to_modern16(struct virtio_device *dev, uint16_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le16(val) : val;
+}
+
+static inline uint32_t virtio_cpu_to_modern32(struct virtio_device *dev, uint32_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le32(val) : val;
+}
+
+static inline uint64_t virtio_cpu_to_modern64(struct virtio_device *dev, uint64_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? cpu_to_le64(val) : val;
+}
+
+static inline uint16_t virtio_modern16_to_cpu(struct virtio_device *dev, uint16_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le16_to_cpu(val) : val;
+}
+
+static inline uint32_t virtio_modern32_to_cpu(struct virtio_device *dev, uint32_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le32_to_cpu(val) : val;
+}
+
+static inline uint64_t virtio_modern64_to_cpu(struct virtio_device *dev, uint64_t val)
+{
+ return (dev->features & VIRTIO_F_VERSION_1) ? le64_to_cpu(val) : val;
+}
+
+#endif /* _LIBVIRTIO_INTERNAL_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-net.c b/roms/SLOF/lib/libvirtio/virtio-net.c
new file mode 100644
index 000000000..5a0d19088
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-net.c
@@ -0,0 +1,384 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+/*
+ * This is the implementation for the Virtio network device driver. Details
+ * about the virtio-net interface can be found in Rusty Russel's "Virtio PCI
+ * Card Specification v0.8.10", appendix C, which can be found here:
+ *
+ * http://ozlabs.org/~rusty/virtio-spec/virtio-spec.pdf
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <helpers.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio-net.h"
+#include "virtio-internal.h"
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+# define dprintf(fmt...) do { printf(fmt); } while(0)
+#else
+# define dprintf(fmt...)
+#endif
+
+#define sync() asm volatile (" sync \n" ::: "memory")
+
+#define DRIVER_FEATURE_SUPPORT (VIRTIO_NET_F_MAC | VIRTIO_F_VERSION_1)
+
+/* See Virtio Spec, appendix C, "Device Operation" */
+struct virtio_net_hdr {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+ // uint16_t num_buffers; /* Only if VIRTIO_NET_F_MRG_RXBUF */
+};
+
+static unsigned int net_hdr_size;
+
+struct virtio_net_hdr_v1 {
+ uint8_t flags;
+ uint8_t gso_type;
+ le16 hdr_len;
+ le16 gso_size;
+ le16 csum_start;
+ le16 csum_offset;
+ le16 num_buffers;
+};
+
+static uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+/**
+ * Module init for virtio via PCI.
+ * Checks whether we're reponsible for the given device and set up
+ * the virtqueue configuration.
+ */
+static int virtionet_init_pci(struct virtio_net *vnet, struct virtio_device *dev)
+{
+ struct virtio_device *vdev = &vnet->vdev;
+
+ dprintf("virtionet: doing virtionet_init_pci!\n");
+
+ if (!dev)
+ return -1;
+
+ /* make a copy of the device structure */
+ memcpy(vdev, dev, sizeof(struct virtio_device));
+
+ /* Reset device */
+ virtio_reset_device(vdev);
+
+ /* Acknowledge device. */
+ virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE);
+
+ return 0;
+}
+
+/**
+ * Initialize the virtio-net device.
+ * See the Virtio Spec, chapter 2.2.1 and Appendix C "Device Initialization"
+ * for details.
+ */
+static int virtionet_init(struct virtio_net *vnet)
+{
+ int i;
+ int status = VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER;
+ struct virtio_device *vdev = &vnet->vdev;
+ net_driver_t *driver = &vnet->driver;
+ struct vqs *vq_tx, *vq_rx;
+
+ dprintf("virtionet_init(%02x:%02x:%02x:%02x:%02x:%02x)\n",
+ driver->mac_addr[0], driver->mac_addr[1],
+ driver->mac_addr[2], driver->mac_addr[3],
+ driver->mac_addr[4], driver->mac_addr[5]);
+
+ if (driver->running != 0)
+ return 0;
+
+ /* Tell HV that we know how to drive the device. */
+ virtio_set_status(vdev, status);
+
+ /* Device specific setup */
+ if (vdev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(vdev, DRIVER_FEATURE_SUPPORT))
+ goto dev_error;
+ net_hdr_size = sizeof(struct virtio_net_hdr_v1);
+ virtio_get_status(vdev, &status);
+ } else {
+ net_hdr_size = sizeof(struct virtio_net_hdr);
+ virtio_set_guest_features(vdev, 0);
+ }
+
+ /* The queue information can be retrieved via the virtio header that
+ * can be found in the I/O BAR. First queue is the receive queue,
+ * second the transmit queue, and the forth is the control queue for
+ * networking options.
+ * We are only interested in the receive and transmit queue here. */
+ vq_rx = virtio_queue_init_vq(vdev, VQ_RX);
+ vq_tx = virtio_queue_init_vq(vdev, VQ_TX);
+ if (!vq_rx || !vq_tx) {
+ virtio_set_status(vdev, VIRTIO_STAT_ACKNOWLEDGE|VIRTIO_STAT_DRIVER
+ |VIRTIO_STAT_FAILED);
+ return -1;
+ }
+
+ /* Allocate memory for one transmit an multiple receive buffers */
+ vq_rx->buf_mem = SLOF_alloc_mem((BUFFER_ENTRY_SIZE+net_hdr_size)
+ * RX_QUEUE_SIZE);
+ if (!vq_rx->buf_mem) {
+ printf("virtionet: Failed to allocate buffers!\n");
+ goto dev_error;
+ }
+
+ /* Prepare receive buffer queue */
+ for (i = 0; i < RX_QUEUE_SIZE; i++) {
+ uint64_t addr = (uint64_t)vq_rx->buf_mem
+ + i * (BUFFER_ENTRY_SIZE+net_hdr_size);
+ uint32_t id = i*2;
+ /* Descriptor for net_hdr: */
+ virtio_fill_desc(vq_rx, id, vdev->features, addr, net_hdr_size,
+ VRING_DESC_F_NEXT | VRING_DESC_F_WRITE, id + 1);
+
+ /* Descriptor for data: */
+ virtio_fill_desc(vq_rx, id + 1, vdev->features, addr + net_hdr_size,
+ BUFFER_ENTRY_SIZE, VRING_DESC_F_WRITE, 0);
+
+ vq_rx->avail->ring[i] = virtio_cpu_to_modern16(vdev, id);
+ }
+ sync();
+
+ vq_rx->avail->flags = virtio_cpu_to_modern16(vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_rx->avail->idx = virtio_cpu_to_modern16(vdev, RX_QUEUE_SIZE);
+
+ last_rx_idx = virtio_modern16_to_cpu(vdev, vq_rx->used->idx);
+
+ vq_tx->avail->flags = virtio_cpu_to_modern16(vdev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq_tx->avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(vdev, status);
+
+ /* Tell HV that RX queues are ready */
+ virtio_queue_notify(vdev, VQ_RX);
+
+ driver->running = 1;
+ for(i = 0; i < (int)sizeof(driver->mac_addr); i++) {
+ driver->mac_addr[i] = virtio_get_config(vdev, i, 1);
+ }
+ return 0;
+
+dev_error:
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(vdev, status);
+ return -1;
+}
+
+
+/**
+ * Shutdown driver.
+ * We've got to make sure that the hosts stops all transfers since the buffers
+ * in our main memory will become invalid after this module has been terminated.
+ */
+static int virtionet_term(struct virtio_net *vnet)
+{
+ struct virtio_device *vdev = &vnet->vdev;
+ net_driver_t *driver = &vnet->driver;
+ struct vqs *vq_tx = &vnet->vdev.vq[VQ_TX];
+ struct vqs *vq_rx = &vnet->vdev.vq[VQ_RX];
+
+ dprintf("virtionet_term()\n");
+
+ if (driver->running == 0)
+ return 0;
+
+ /* Quiesce device */
+ virtio_set_status(vdev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(vdev);
+
+ driver->running = 0;
+
+ SLOF_free_mem(vq_rx->buf_mem,
+ (BUFFER_ENTRY_SIZE+net_hdr_size) * RX_QUEUE_SIZE);
+ vq_rx->buf_mem = NULL;
+
+ virtio_queue_term_vq(vdev, vq_rx, VQ_RX);
+ virtio_queue_term_vq(vdev, vq_tx, VQ_TX);
+
+ return 0;
+}
+
+
+/**
+ * Transmit a packet
+ */
+static int virtionet_xmit(struct virtio_net *vnet, char *buf, int len)
+{
+ int id, idx;
+ static struct virtio_net_hdr_v1 nethdr_v1;
+ static struct virtio_net_hdr nethdr_legacy;
+ void *nethdr = &nethdr_legacy;
+ struct virtio_device *vdev = &vnet->vdev;
+ struct vqs *vq_tx = &vdev->vq[VQ_TX];
+
+ if (len > BUFFER_ENTRY_SIZE) {
+ printf("virtionet: Packet too big!\n");
+ return 0;
+ }
+
+ dprintf("\nvirtionet_xmit(packet at %p, %d bytes)\n", buf, len);
+
+ if (vdev->features & VIRTIO_F_VERSION_1)
+ nethdr = &nethdr_v1;
+
+ memset(nethdr, 0, net_hdr_size);
+
+ /* Determine descriptor index */
+ idx = virtio_modern16_to_cpu(vdev, vq_tx->avail->idx);
+ id = (idx * 2) % vq_tx->size;
+
+ virtio_free_desc(vq_tx, id, vdev->features);
+ virtio_free_desc(vq_tx, id + 1, vdev->features);
+
+ /* Set up virtqueue descriptor for header */
+ virtio_fill_desc(vq_tx, id, vdev->features, (uint64_t)nethdr,
+ net_hdr_size, VRING_DESC_F_NEXT, id + 1);
+
+ /* Set up virtqueue descriptor for data */
+ virtio_fill_desc(vq_tx, id + 1, vdev->features, (uint64_t)buf, len, 0, 0);
+
+ vq_tx->avail->ring[idx % vq_tx->size] = virtio_cpu_to_modern16(vdev, id);
+ sync();
+ vq_tx->avail->idx = virtio_cpu_to_modern16(vdev, idx + 1);
+ sync();
+
+ /* Tell HV that TX queue is ready */
+ virtio_queue_notify(vdev, VQ_TX);
+
+ return len;
+}
+
+
+/**
+ * Receive a packet
+ */
+static int virtionet_receive(struct virtio_net *vnet, char *buf, int maxlen)
+{
+ uint32_t len = 0;
+ uint32_t id, idx;
+ uint16_t avail_idx;
+ struct virtio_device *vdev = &vnet->vdev;
+ struct vqs *vq_rx = &vnet->vdev.vq[VQ_RX];
+
+ idx = virtio_modern16_to_cpu(vdev, vq_rx->used->idx);
+
+ if (last_rx_idx == idx) {
+ /* Nothing received yet */
+ return 0;
+ }
+
+ id = (virtio_modern32_to_cpu(vdev, vq_rx->used->ring[last_rx_idx % vq_rx->size].id) + 1)
+ % vq_rx->size;
+ len = virtio_modern32_to_cpu(vdev, vq_rx->used->ring[last_rx_idx % vq_rx->size].len)
+ - net_hdr_size;
+ dprintf("virtionet_receive() last_rx_idx=%i, vq_rx->used->idx=%i,"
+ " id=%i len=%i\n", last_rx_idx, vq_rx->used->idx, id, len);
+
+ if (len > (uint32_t)maxlen) {
+ printf("virtio-net: Receive buffer not big enough!\n");
+ len = maxlen;
+ }
+
+#if 0
+ /* Dump packet */
+ printf("\n");
+ int i;
+ for (i=0; i<64; i++) {
+ printf(" %02x", *(uint8_t*)(vq_rx->desc[id].addr+i));
+ if ((i%16)==15)
+ printf("\n");
+ }
+ prinfk("\n");
+#endif
+
+ /* Copy data to destination buffer */
+ memcpy(buf, virtio_desc_addr(vdev, VQ_RX, id), len);
+
+ /* Move indices to next entries */
+ last_rx_idx = last_rx_idx + 1;
+
+ avail_idx = virtio_modern16_to_cpu(vdev, vq_rx->avail->idx);
+ vq_rx->avail->ring[avail_idx % vq_rx->size] = virtio_cpu_to_modern16(vdev, id - 1);
+ sync();
+ vq_rx->avail->idx = virtio_cpu_to_modern16(vdev, avail_idx + 1);
+
+ /* Tell HV that RX queue entry is ready */
+ virtio_queue_notify(vdev, VQ_RX);
+
+ return len;
+}
+
+struct virtio_net *virtionet_open(struct virtio_device *dev)
+{
+ struct virtio_net *vnet;
+
+ vnet = SLOF_alloc_mem(sizeof(*vnet));
+ if (!vnet) {
+ printf("Unable to allocate virtio-net driver\n");
+ return NULL;
+ }
+
+ vnet->driver.running = 0;
+
+ if (virtionet_init_pci(vnet, dev))
+ goto FAIL;
+
+ if (virtionet_init(vnet))
+ goto FAIL;
+
+ return vnet;
+
+FAIL:
+ SLOF_free_mem(vnet, sizeof(*vnet));
+ return NULL;
+}
+
+void virtionet_close(struct virtio_net *vnet)
+{
+ if (vnet) {
+ virtionet_term(vnet);
+ SLOF_free_mem(vnet, sizeof(*vnet));
+ }
+}
+
+int virtionet_read(struct virtio_net *vnet, char *buf, int len)
+{
+ if (vnet && buf)
+ return virtionet_receive(vnet, buf, len);
+ return -1;
+}
+
+int virtionet_write(struct virtio_net *vnet, char *buf, int len)
+{
+ if (vnet && buf)
+ return virtionet_xmit(vnet, buf, len);
+ return -1;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-net.h b/roms/SLOF/lib/libvirtio/virtio-net.h
new file mode 100644
index 000000000..c71fbded0
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-net.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#ifndef VIRTIO_NET_H
+#define VIRTIO_NET_H
+
+#include <netdriver.h>
+#include "virtio.h"
+
+#define RX_QUEUE_SIZE 128
+#define BUFFER_ENTRY_SIZE 1514
+
+enum {
+ VQ_RX = 0, /* Receive Queue */
+ VQ_TX = 1, /* Transmit Queue */
+};
+
+struct virtio_net {
+ net_driver_t driver;
+ struct virtio_device vdev;
+};
+
+/* VIRTIO_NET Feature bits */
+#define VIRTIO_NET_F_MAC (1 << 5)
+
+extern struct virtio_net *virtionet_open(struct virtio_device *dev);
+extern void virtionet_close(struct virtio_net *vnet);
+extern int virtionet_read(struct virtio_net *vnet, char *buf, int len);
+extern int virtionet_write(struct virtio_net *vnet, char *buf, int len);
+
+#endif
diff --git a/roms/SLOF/lib/libvirtio/virtio-scsi.c b/roms/SLOF/lib/libvirtio/virtio-scsi.c
new file mode 100644
index 000000000..96285e389
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-scsi.c
@@ -0,0 +1,152 @@
+/******************************************************************************
+ * Copyright (c) 2012 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
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <cpu.h>
+#include <helpers.h>
+#include "virtio.h"
+#include "virtio-internal.h"
+#include "virtio-scsi.h"
+
+int virtioscsi_send(struct virtio_device *dev,
+ struct virtio_scsi_req_cmd *req,
+ struct virtio_scsi_resp_cmd *resp,
+ int is_read, void *buf, uint64_t buf_len)
+{
+
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ int id;
+ uint32_t time;
+ struct vqs *vq = &dev->vq[VIRTIO_SCSI_REQUEST_VQ];
+
+ 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 * 3) % vq->size;
+ virtio_fill_desc(vq, id, dev->features, (uint64_t)req, sizeof(*req), VRING_DESC_F_NEXT,
+ id + 1);
+
+ if (buf == NULL || buf_len == 0) {
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_WRITE, 0);
+ } else if (is_read) {
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_NEXT | VRING_DESC_F_WRITE,
+ id + 2);
+ /* Set up virtqueue descriptor for data from device */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)buf, buf_len, VRING_DESC_F_WRITE, 0);
+ } else {
+ /* Set up virtqueue descriptor for data to device */
+ virtio_fill_desc(vq, id + 1, dev->features,
+ (uint64_t)buf, buf_len, VRING_DESC_F_NEXT,
+ id + 2);
+ /* Set up descriptor for response information */
+ virtio_fill_desc(vq, id + 2, dev->features,
+ (uint64_t)resp, sizeof(*resp),
+ VRING_DESC_F_WRITE, 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 vq is ready */
+ virtio_queue_notify(dev, VIRTIO_SCSI_REQUEST_VQ);
+
+ /* 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())
+ break;
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+ virtio_free_desc(vq, id + 1, dev->features);
+ if (!(buf == NULL || buf_len == 0))
+ virtio_free_desc(vq, id + 2, dev->features);
+
+ return 0;
+}
+
+/**
+ * Initialize virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+int virtioscsi_init(struct virtio_device *dev)
+{
+ struct vqs *vq_ctrl, *vq_event, *vq_request;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+
+ /* Reset device */
+ // XXX That will clear the virtq base. We need to move
+ // initializing it to here anyway
+ //
+ // 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);
+
+ /* Device specific setup - we do not support special features right now */
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ if (virtio_negotiate_guest_features(dev, VIRTIO_F_VERSION_1))
+ goto dev_error;
+ virtio_get_status(dev, &status);
+ } else {
+ virtio_set_guest_features(dev, 0);
+ }
+
+ vq_ctrl = virtio_queue_init_vq(dev, VIRTIO_SCSI_CONTROL_VQ);
+ vq_event = virtio_queue_init_vq(dev, VIRTIO_SCSI_EVENT_VQ);
+ vq_request = virtio_queue_init_vq(dev, VIRTIO_SCSI_REQUEST_VQ);
+ if (!vq_ctrl || !vq_event || !vq_request)
+ goto dev_error;
+
+ /* Tell HV that setup succeeded */
+ status |= VIRTIO_STAT_DRIVER_OK;
+ virtio_set_status(dev, status);
+
+ return 0;
+dev_error:
+ printf("%s: failed\n", __func__);
+ status |= VIRTIO_STAT_FAILED;
+ virtio_set_status(dev, status);
+ return -1;
+}
+
+/**
+ * Shutdown the virtio-block device.
+ * @param dev pointer to virtio device information
+ */
+void virtioscsi_shutdown(struct virtio_device *dev)
+{
+ /* Quiesce device */
+ virtio_set_status(dev, VIRTIO_STAT_FAILED);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-scsi.h b/roms/SLOF/lib/libvirtio/virtio-scsi.h
new file mode 100644
index 000000000..d598dea43
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio-scsi.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ * Copyright (c) 2012 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 SCSI Host device definitions.
+ * See Virtio Spec, Appendix I, for details
+ */
+
+#ifndef _VIRTIO_SCSI_H
+#define _VIRTIO_SCSI_H
+
+#define VIRTIO_SCSI_CDB_SIZE 32
+#define VIRTIO_SCSI_SENSE_SIZE 96
+
+#define VIRTIO_SCSI_CONTROL_VQ 0
+#define VIRTIO_SCSI_EVENT_VQ 1
+#define VIRTIO_SCSI_REQUEST_VQ 2
+
+struct virtio_scsi_config
+{
+ uint32_t num_queues;
+ uint32_t seg_max;
+ uint32_t max_sectors;
+ uint32_t cmd_per_lun;
+ uint32_t event_info_size;
+ uint32_t sense_size;
+ uint32_t cdb_size;
+ uint16_t max_channel;
+ uint16_t max_target;
+ uint32_t max_lun;
+} __attribute__((packed));
+
+/* This is the first element of the "out" scatter-gather list. */
+struct virtio_scsi_req_cmd {
+ uint8_t lun[8];
+ uint64_t tag;
+ uint8_t task_attr;
+ uint8_t prio;
+ uint8_t crn;
+ char cdb[VIRTIO_SCSI_CDB_SIZE];
+} __attribute__((packed));
+
+/* This is the first element of the "in" scatter-gather list. */
+struct virtio_scsi_resp_cmd {
+ uint32_t sense_len;
+ uint32_t residual;
+ uint16_t status_qualifier;
+ uint8_t status;
+ uint8_t response;
+ uint8_t sense[VIRTIO_SCSI_SENSE_SIZE];
+};
+
+extern int virtioscsi_init(struct virtio_device *dev);
+extern void virtioscsi_shutdown(struct virtio_device *dev);
+extern int virtioscsi_send(struct virtio_device *dev,
+ struct virtio_scsi_req_cmd *req,
+ struct virtio_scsi_resp_cmd *resp,
+ int is_read, void *buf, uint64_t buf_len);
+
+#endif /* _VIRTIO_SCSI_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio-serial.c b/roms/SLOF/lib/libvirtio/virtio-serial.c
new file mode 100644
index 000000000..92afb0201
--- /dev/null
+++ b/roms/SLOF/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 uint16_t last_rx_idx; /* Last index in RX "used" ring */
+
+int virtio_serial_init(struct virtio_device *dev)
+{
+ struct vqs *vq_rx, *vq_tx;
+ int status = VIRTIO_STAT_ACKNOWLEDGE;
+ int i;
+
+ /* 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->features & VIRTIO_F_VERSION_1) {
+ /* 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);
+ }
+
+ vq_rx = virtio_queue_init_vq(dev, RX_Q);
+ if (!vq_rx)
+ 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 (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, i, dev->features, addr, 1, VRING_DESC_F_WRITE, 0);
+ vq_rx->avail->ring[i] = virtio_cpu_to_modern16(dev, i);
+ }
+ 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);
+
+ vq_tx = virtio_queue_init_vq(dev, TX_Q);
+ if (!vq_tx)
+ goto dev_error;
+
+
+ /* 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);
+
+ /* Stop queues */
+ virtio_queue_term_vq(dev, &dev->vq[TX_Q], TX_Q);
+ virtio_queue_term_vq(dev, &dev->vq[RX_Q], RX_Q);
+
+ /* Reset device */
+ virtio_reset_device(dev);
+}
+
+int virtio_serial_putchar(struct virtio_device *dev, char c)
+{
+ int id, ret;
+ uint32_t time;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx, avail_idx;
+ struct vqs *vq = &dev->vq[TX_Q];
+
+ if (!vq->desc)
+ return 0;
+
+ 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 */
+ virtio_fill_desc(vq, id, dev->features, (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 */
+ ret = 1;
+ 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");
+ ret = 0;
+ break;
+ }
+ }
+
+ virtio_free_desc(vq, id, dev->features);
+
+ return ret;
+}
+
+char virtio_serial_getchar(struct virtio_device *dev)
+{
+ int id, idx;
+ char buf[RX_NUM_ELEMS] = {0};
+ uint16_t avail_idx;
+ struct vqs *vq_rx = &dev->vq[RX_Q];
+
+ 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, virtio_desc_addr(dev, RX_Q, id - 1), 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)
+{
+ struct vqs *vq_rx = &dev->vq[RX_Q];
+
+ if (last_rx_idx == virtio_modern16_to_cpu(dev, vq_rx->used->idx))
+ return 0;
+ else
+ return 1;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio-serial.h b/roms/SLOF/lib/libvirtio/virtio-serial.h
new file mode 100644
index 000000000..68af46de5
--- /dev/null
+++ b/roms/SLOF/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/roms/SLOF/lib/libvirtio/virtio.c b/roms/SLOF/lib/libvirtio/virtio.c
new file mode 100644
index 000000000..9dfc93cb2
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.c
@@ -0,0 +1,645 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stddef.h>
+#include <cpu.h>
+#include <cache.h>
+#include <byteorder.h>
+#include "virtio.h"
+#include "helpers.h"
+#include "virtio-internal.h"
+
+/* PCI virtio header offsets */
+#define VIRTIOHDR_DEVICE_FEATURES 0
+#define VIRTIOHDR_GUEST_FEATURES 4
+#define VIRTIOHDR_QUEUE_ADDRESS 8
+#define VIRTIOHDR_QUEUE_SIZE 12
+#define VIRTIOHDR_QUEUE_SELECT 14
+#define VIRTIOHDR_QUEUE_NOTIFY 16
+#define VIRTIOHDR_DEVICE_STATUS 18
+#define VIRTIOHDR_ISR_STATUS 19
+#define VIRTIOHDR_DEVICE_CONFIG 20
+
+/* PCI defines */
+#define PCI_BASE_ADDR_SPACE_IO 0x01
+#define PCI_BASE_ADDR_SPACE_64BIT 0x04
+#define PCI_BASE_ADDR_MEM_MASK (~0x0fUL)
+#define PCI_BASE_ADDR_IO_MASK (~0x03UL)
+
+#define PCI_BASE_ADDR_REG_0 0x10
+#define PCI_CONFIG_CAP_REG 0x34
+
+#define PCI_CAP_ID_VNDR 0x9
+
+/* Common configuration */
+#define VIRTIO_PCI_CAP_COMMON_CFG 1
+/* Notifications */
+#define VIRTIO_PCI_CAP_NOTIFY_CFG 2
+/* ISR access */
+#define VIRTIO_PCI_CAP_ISR_CFG 3
+/* Device specific configuration */
+#define VIRTIO_PCI_CAP_DEVICE_CFG 4
+/* PCI configuration access */
+#define VIRTIO_PCI_CAP_PCI_CFG 5
+
+#define VIRTIO_PCI_CAP_VNDR 0 /* Generic PCI field: PCI_CAP_ID_VNDR */
+#define VIRTIO_PCI_CAP_NEXT 1 /* Generic PCI field: next ptr. */
+#define VIRTIO_PCI_CAP_LEN 2 /* Generic PCI field: capability length */
+#define VIRTIO_PCI_CAP_CFG_TYPE 3 /* Identifies the structure. */
+#define VIRTIO_PCI_CAP_BAR 4 /* Where to find it. */
+#define VIRTIO_PCI_CAP_OFFSET 8 /* Offset within bar. */
+#define VIRTIO_PCI_CAP_LENGTH 12 /* Length of the structure, in bytes. */
+
+struct virtio_dev_common {
+ le32 dev_features_sel;
+ le32 dev_features;
+ le32 drv_features_sel;
+ le32 drv_features;
+ le16 msix_config;
+ le16 num_queues;
+ uint8_t dev_status;
+ uint8_t cfg_generation;
+
+ le16 q_select;
+ le16 q_size;
+ le16 q_msix_vec;
+ le16 q_enable;
+ le16 q_notify_off;
+ le64 q_desc;
+ le64 q_avail;
+ le64 q_used;
+} __attribute__ ((packed));
+
+/* virtio 1.0 Spec: 4.1.3 PCI Device Layout
+ *
+ * Fields of different sizes are present in the device configuration regions.
+ * All 64-bit, 32-bit and 16-bit fields are little-endian. 64-bit fields are to
+ * be treated as two 32-bit fields, with low 32 bit part followed by the high 32
+ * bit part.
+ */
+static void virtio_pci_write64(void *addr, uint64_t val)
+{
+ uint32_t hi = (val >> 32) & 0xFFFFFFFF;
+ uint32_t lo = val & 0xFFFFFFFF;
+
+ ci_write_32(addr, cpu_to_le32(lo));
+ ci_write_32(addr + 4, cpu_to_le32(hi));
+}
+
+static void virtio_cap_set_base_addr(struct virtio_cap *cap, uint32_t offset)
+{
+ uint64_t addr;
+
+ addr = SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * cap->bar);
+ if (addr & PCI_BASE_ADDR_SPACE_IO) {
+ addr = addr & PCI_BASE_ADDR_IO_MASK;
+ cap->is_io = 1;
+ } else {
+ if (addr & PCI_BASE_ADDR_SPACE_64BIT)
+ addr |= SLOF_pci_config_read32(PCI_BASE_ADDR_REG_0 + 4 * (cap->bar + 1)) << 32;
+ addr = addr & PCI_BASE_ADDR_MEM_MASK;
+ cap->is_io = 0;
+ }
+ addr = (uint64_t)SLOF_translate_my_address((void *)addr);
+ cap->addr = (void *)addr + offset;
+}
+
+static void virtio_process_cap(struct virtio_device *dev, uint8_t cap_ptr)
+{
+ struct virtio_cap *cap;
+ uint8_t cfg_type, bar;
+ uint32_t offset;
+
+ cfg_type = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_CFG_TYPE);
+ bar = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_BAR);
+ offset = SLOF_pci_config_read32(cap_ptr + VIRTIO_PCI_CAP_OFFSET);
+
+ switch(cfg_type) {
+ case VIRTIO_PCI_CAP_COMMON_CFG:
+ cap = &dev->common;
+ break;
+ case VIRTIO_PCI_CAP_NOTIFY_CFG:
+ cap = &dev->notify;
+ dev->notify_off_mul = SLOF_pci_config_read32(cap_ptr + sizeof(struct virtio_cap));
+ break;
+ case VIRTIO_PCI_CAP_ISR_CFG:
+ cap = &dev->isr;
+ break;
+ case VIRTIO_PCI_CAP_DEVICE_CFG:
+ cap = &dev->device;
+ break;
+ default:
+ return;
+ }
+
+ cap->bar = bar;
+ virtio_cap_set_base_addr(cap, offset);
+ cap->cap_id = cfg_type;
+}
+
+/**
+ * Reads the virtio device capabilities, gets called from SLOF routines The
+ * function determines legacy or modern device and sets up driver registers
+ */
+struct virtio_device *virtio_setup_vd(void)
+{
+ uint8_t cap_ptr, cap_vndr;
+ struct virtio_device *dev;
+
+ dev = SLOF_alloc_mem(sizeof(struct virtio_device));
+ if (!dev) {
+ printf("Failed to allocate memory");
+ return NULL;
+ }
+
+ cap_ptr = SLOF_pci_config_read8(PCI_CONFIG_CAP_REG);
+ while (cap_ptr != 0) {
+ cap_vndr = SLOF_pci_config_read8(cap_ptr + VIRTIO_PCI_CAP_VNDR);
+ if (cap_vndr == PCI_CAP_ID_VNDR)
+ virtio_process_cap(dev, cap_ptr);
+ cap_ptr = SLOF_pci_config_read8(cap_ptr+VIRTIO_PCI_CAP_NEXT);
+ }
+
+ if (dev->common.cap_id && dev->notify.cap_id &&
+ dev->isr.cap_id && dev->device.cap_id) {
+ dev->features = VIRTIO_F_VERSION_1;
+ } else {
+ dev->features = 0;
+ dev->legacy.cap_id = 0;
+ dev->legacy.bar = 0;
+ virtio_cap_set_base_addr(&dev->legacy, 0);
+ }
+ return dev;
+}
+
+/**
+ * Calculate ring size according to queue size number
+ */
+unsigned long virtio_vring_size(unsigned int qsize)
+{
+ return VQ_ALIGN(sizeof(struct vring_desc) * qsize +
+ sizeof(struct vring_avail) + sizeof(uint16_t) * qsize) +
+ VQ_ALIGN(sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * qsize);
+}
+
+
+/**
+ * Get number of elements in a vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return number of elements
+ */
+unsigned int virtio_get_qsize(struct virtio_device *dev, int queue)
+{
+ unsigned int size = 0;
+
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ void *addr = dev->common.addr + offset_of(struct virtio_dev_common, q_select);
+ ci_write_16(addr, cpu_to_le16(queue));
+ eieio();
+ addr = dev->common.addr + offset_of(struct virtio_dev_common, q_size);
+ size = le16_to_cpu(ci_read_16(addr));
+ }
+ else {
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT,
+ cpu_to_le16(queue));
+ eieio();
+ size = le16_to_cpu(ci_read_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SIZE));
+ }
+
+ return size;
+}
+
+
+/**
+ * Get address of descriptor vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the descriptor ring
+ */
+struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].desc;
+}
+
+
+/**
+ * Get address of "available" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "available" ring
+ */
+struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].avail;
+}
+
+
+/**
+ * Get address of "used" vring
+ * @param dev pointer to virtio device information
+ * @param queue virtio queue number
+ * @return pointer to the "used" ring
+ */
+struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue)
+{
+ return dev->vq[queue].used;
+}
+
+/**
+ * Fill the virtio ring descriptor depending on the legacy mode or virtio 1.0
+ */
+void virtio_fill_desc(struct vqs *vq, int id, uint64_t features,
+ uint64_t addr, uint32_t len,
+ uint16_t flags, uint16_t next)
+{
+ struct vring_desc *desc;
+
+ id %= vq->size;
+ desc = &vq->desc[id];
+ next %= vq->size;
+
+ if (features & VIRTIO_F_VERSION_1) {
+ if (features & VIRTIO_F_IOMMU_PLATFORM) {
+ void *gpa = (void *) addr;
+
+ if (!vq->desc_gpas) {
+ fprintf(stderr, "IOMMU setup has not been done!\n");
+ return;
+ }
+
+ addr = SLOF_dma_map_in(gpa, len, 0);
+ vq->desc_gpas[id] = gpa;
+ }
+ desc->addr = cpu_to_le64(addr);
+ desc->len = cpu_to_le32(len);
+ desc->flags = cpu_to_le16(flags);
+ desc->next = cpu_to_le16(next);
+ } else {
+ desc->addr = addr;
+ desc->len = len;
+ desc->flags = flags;
+ desc->next = next;
+ }
+}
+
+void virtio_free_desc(struct vqs *vq, int id, uint64_t features)
+{
+ struct vring_desc *desc;
+
+ id %= vq->size;
+ desc = &vq->desc[id];
+
+ if (!(features & VIRTIO_F_VERSION_1) ||
+ !(features & VIRTIO_F_IOMMU_PLATFORM))
+ return;
+
+ if (!vq->desc_gpas[id])
+ return;
+
+ SLOF_dma_map_out(le64_to_cpu(desc->addr), 0, le32_to_cpu(desc->len));
+ vq->desc_gpas[id] = NULL;
+}
+
+void *virtio_desc_addr(struct virtio_device *vdev, int queue, int id)
+{
+ struct vqs *vq = &vdev->vq[queue];
+
+ if (vq->desc_gpas)
+ return vq->desc_gpas[id];
+
+ return (void *) virtio_modern64_to_cpu(vdev, vq->desc[id].addr);
+}
+
+/**
+ * Reset virtio device
+ */
+void virtio_reset_device(struct virtio_device *dev)
+{
+ virtio_set_status(dev, 0);
+}
+
+
+/**
+ * Notify hypervisor about queue update
+ */
+void virtio_queue_notify(struct virtio_device *dev, int queue)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ void *q_sel = dev->common.addr + offset_of(struct virtio_dev_common, q_select);
+ void *q_ntfy = dev->common.addr + offset_of(struct virtio_dev_common, q_notify_off);
+ void *addr;
+ uint16_t q_notify_off;
+
+ ci_write_16(q_sel, cpu_to_le16(queue));
+ eieio();
+ q_notify_off = le16_to_cpu(ci_read_16(q_ntfy));
+ addr = dev->notify.addr + q_notify_off * dev->notify_off_mul;
+ ci_write_16(addr, cpu_to_le16(queue));
+ } else {
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_NOTIFY, cpu_to_le16(queue));
+ }
+}
+
+/**
+ * Set queue address
+ */
+static void virtio_set_qaddr(struct virtio_device *dev, int queue, unsigned long qaddr)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint64_t q_desc = qaddr;
+ uint64_t q_avail;
+ uint64_t q_used;
+ uint32_t q_size = virtio_get_qsize(dev, queue);
+
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+ unsigned long cb;
+
+ cb = q_size * sizeof(struct vring_desc);
+ cb += sizeof(struct vring_avail) +
+ sizeof(uint16_t) * q_size;
+ cb = VQ_ALIGN(cb);
+ cb += sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * q_size;
+ cb = VQ_ALIGN(cb);
+ q_desc = SLOF_dma_map_in((void *)q_desc, cb, 0);
+
+ dev->vq[queue].bus_desc = q_desc;
+ }
+
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_desc), q_desc);
+ q_avail = q_desc + q_size * sizeof(struct vring_desc);
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_avail), q_avail);
+ q_used = VQ_ALIGN(q_avail + sizeof(struct vring_avail) + sizeof(uint16_t) * q_size);
+ virtio_pci_write64(dev->common.addr + offset_of(struct virtio_dev_common, q_used), q_used);
+ ci_write_16(dev->common.addr + offset_of(struct virtio_dev_common, q_enable), cpu_to_le16(1));
+ } else {
+ uint32_t val = qaddr;
+ val = val >> 12;
+ ci_write_16(dev->legacy.addr+VIRTIOHDR_QUEUE_SELECT,
+ cpu_to_le16(queue));
+ eieio();
+ ci_write_32(dev->legacy.addr+VIRTIOHDR_QUEUE_ADDRESS,
+ cpu_to_le32(val));
+ }
+}
+
+struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id)
+{
+ struct vqs *vq;
+
+ if (id >= sizeof(dev->vq)/sizeof(dev->vq[0])) {
+ printf("Queue index is too big!\n");
+ return NULL;
+ }
+ vq = &dev->vq[id];
+
+ memset(vq, 0, sizeof(*vq));
+
+ vq->size = virtio_get_qsize(dev, id);
+ vq->desc = SLOF_alloc_mem_aligned(virtio_vring_size(vq->size), 4096);
+ if (!vq->desc) {
+ printf("memory allocation failed!\n");
+ return NULL;
+ }
+
+ vq->avail = (void *) vq->desc + vq->size * sizeof(struct vring_desc);
+ vq->used = (void *) VQ_ALIGN((unsigned long) vq->avail +
+ sizeof(struct vring_avail) +
+ sizeof(uint16_t) * vq->size);
+
+ memset(vq->desc, 0, virtio_vring_size(vq->size));
+ virtio_set_qaddr(dev, id, (unsigned long)vq->desc);
+
+ vq->avail->flags = virtio_cpu_to_modern16(dev, VRING_AVAIL_F_NO_INTERRUPT);
+ vq->avail->idx = 0;
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM)
+ vq->desc_gpas = SLOF_alloc_mem_aligned(
+ vq->size * sizeof(vq->desc_gpas[0]), 4096);
+
+ return vq;
+}
+
+void virtio_queue_term_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id)
+{
+ if (vq->desc_gpas) {
+ unsigned i;
+
+ for (i = 0; i < vq->size; ++i)
+ virtio_free_desc(vq, i, dev->features);
+
+ SLOF_free_mem(vq->desc_gpas,
+ vq->size * sizeof(vq->desc_gpas[0]));
+ }
+ if (vq->desc) {
+ if (dev->features & VIRTIO_F_IOMMU_PLATFORM) {
+ unsigned long cb;
+ uint32_t q_size = virtio_get_qsize(dev, id);
+
+ cb = q_size * sizeof(struct vring_desc);
+ cb += sizeof(struct vring_avail) +
+ sizeof(uint16_t) * q_size;
+ cb = VQ_ALIGN(cb);
+ cb += sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * q_size;
+ cb = VQ_ALIGN(cb);
+
+ SLOF_dma_map_out(vq->bus_desc, 0, cb);
+ }
+
+ SLOF_free_mem(vq->desc, virtio_vring_size(vq->size));
+ }
+ memset(vq, 0, sizeof(*vq));
+}
+
+/**
+ * Set device status bits
+ */
+void virtio_set_status(struct virtio_device *dev, int status)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ ci_write_8(dev->common.addr +
+ offset_of(struct virtio_dev_common, dev_status), status);
+ } else {
+ ci_write_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS, status);
+ }
+}
+
+/**
+ * Get device status bits
+ */
+void virtio_get_status(struct virtio_device *dev, int *status)
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ *status = ci_read_8(dev->common.addr +
+ offset_of(struct virtio_dev_common, dev_status));
+ } else {
+ *status = ci_read_8(dev->legacy.addr+VIRTIOHDR_DEVICE_STATUS);
+ }
+}
+
+/**
+ * Set guest feature bits
+ */
+void virtio_set_guest_features(struct virtio_device *dev, uint64_t features)
+
+{
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint32_t f1 = (features >> 32) & 0xFFFFFFFF;
+ uint32_t f0 = features & 0xFFFFFFFF;
+ void *addr = dev->common.addr;
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel),
+ cpu_to_le32(1));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features),
+ cpu_to_le32(f1));
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features_sel),
+ cpu_to_le32(0));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, drv_features),
+ cpu_to_le32(f0));
+ } else {
+ ci_write_32(dev->legacy.addr+VIRTIOHDR_GUEST_FEATURES, cpu_to_le32(features));
+ }
+}
+
+/**
+ * Get host feature bits
+ */
+uint64_t virtio_get_host_features(struct virtio_device *dev)
+
+{
+ uint64_t features = 0;
+ if (dev->features & VIRTIO_F_VERSION_1) {
+ uint32_t f0 = 0, f1 = 0;
+ void *addr = dev->common.addr;
+
+ ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel),
+ cpu_to_le32(1));
+ f1 = ci_read_32(addr +
+ offset_of(struct virtio_dev_common, dev_features));
+ ci_write_32(addr + offset_of(struct virtio_dev_common, dev_features_sel),
+ cpu_to_le32(0));
+ f0 = ci_read_32(addr +
+ offset_of(struct virtio_dev_common, dev_features));
+
+ features = ((uint64_t)le32_to_cpu(f1) << 32) | le32_to_cpu(f0);
+ } else {
+ features = le32_to_cpu(ci_read_32(dev->legacy.addr+VIRTIOHDR_DEVICE_FEATURES));
+ }
+ return features;
+}
+
+int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features)
+{
+ uint64_t host_features = 0;
+ int status;
+
+ /* Negotiate features */
+ host_features = virtio_get_host_features(dev);
+ if (!(host_features & VIRTIO_F_VERSION_1)) {
+ fprintf(stderr, "Device does not support virtio 1.0 %llx\n", host_features);
+ return -1;
+ }
+
+ if (host_features & VIRTIO_F_IOMMU_PLATFORM)
+ features |= VIRTIO_F_IOMMU_PLATFORM;
+
+ virtio_set_guest_features(dev, features);
+ host_features = virtio_get_host_features(dev);
+ if ((host_features & features) != features) {
+ fprintf(stderr, "Features error %llx\n", features);
+ return -1;
+ }
+
+ virtio_get_status(dev, &status);
+ status |= VIRTIO_STAT_FEATURES_OK;
+ virtio_set_status(dev, status);
+
+ /* Read back to verify the FEATURES_OK bit */
+ virtio_get_status(dev, &status);
+ if ((status & VIRTIO_STAT_FEATURES_OK) != VIRTIO_STAT_FEATURES_OK)
+ return -1;
+
+ dev->features = features;
+
+ return 0;
+}
+
+/**
+ * Get additional config values
+ */
+uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
+{
+ uint64_t val = ~0ULL;
+ uint32_t hi, lo;
+ void *confbase;
+
+ if (dev->features & VIRTIO_F_VERSION_1)
+ confbase = dev->device.addr;
+ else
+ confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG;
+
+ switch (size) {
+ case 1:
+ val = ci_read_8(confbase+offset);
+ break;
+ case 2:
+ val = ci_read_16(confbase+offset);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = le16_to_cpu(val);
+ break;
+ case 4:
+ val = ci_read_32(confbase+offset);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = le32_to_cpu(val);
+ break;
+ case 8:
+ /* We don't support 8 bytes PIO accesses
+ * in qemu and this is all PIO
+ */
+ lo = ci_read_32(confbase+offset);
+ hi = ci_read_32(confbase+offset+4);
+ if (dev->features & VIRTIO_F_VERSION_1)
+ val = (uint64_t)le32_to_cpu(hi) << 32 | le32_to_cpu(lo);
+ else
+ val = (uint64_t)hi << 32 | lo;
+ break;
+ }
+
+ return val;
+}
+
+/**
+ * Get config blob
+ */
+int __virtio_read_config(struct virtio_device *dev, void *dst,
+ int offset, int len)
+{
+ void *confbase;
+ unsigned char *buf = dst;
+ int i;
+
+ if (dev->features & VIRTIO_F_VERSION_1)
+ confbase = dev->device.addr;
+ else
+ confbase = dev->legacy.addr+VIRTIOHDR_DEVICE_CONFIG;
+
+ for (i = 0; i < len; i++)
+ buf[i] = ci_read_8(confbase + offset + i);
+
+ return len;
+}
diff --git a/roms/SLOF/lib/libvirtio/virtio.code b/roms/SLOF/lib/libvirtio/virtio.code
new file mode 100644
index 000000000..db3ed600f
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.code
@@ -0,0 +1,203 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 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
+ *****************************************************************************/
+
+#include <virtio.h>
+#include <virtio-blk.h>
+#include <virtio-9p.h>
+#include <virtio-scsi.h>
+#include <virtio-net.h>
+#include <virtio-serial.h>
+
+/******** core virtio ********/
+
+// : virtio-setup-vd ( -- dev )
+PRIM(virtio_X2d_setup_X2d_vd)
+ PUSH; TOS.a = virtio_setup_vd();
+MIRP
+
+// : virtio-vring-size ( queuesize -- ringsize )
+PRIM(virtio_X2d_vring_X2d_size)
+ TOS.u = virtio_vring_size(TOS.u);
+MIRP
+
+// : virtio-get-qsize ( dev queue -- queuesize )
+PRIM(virtio_X2d_get_X2d_qsize)
+ int queue = TOS.u; POP;
+ TOS.u = virtio_get_qsize(TOS.a, queue);
+MIRP
+
+// : virtio-get-config ( dev offset size -- val )
+PRIM(virtio_X2d_get_X2d_config)
+ int size = TOS.u; POP;
+ int offset = TOS.u; POP;
+ TOS.u = virtio_get_config(TOS.a, offset, size);
+MIRP
+
+/******** virtio-blk ********/
+
+// : virtio-blk-init ( dev -- blk-size)
+PRIM(virtio_X2d_blk_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtioblk_init(dev);
+MIRP
+
+// : virtio-blk-shutdown ( dev -- )
+PRIM(virtio_X2d_blk_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtioblk_shutdown(dev);
+MIRP
+
+// : virtio-blk-read ( buf blkno cnt dev -- #read )
+PRIM(virtio_X2d_blk_X2d_read)
+ void *dev = TOS.a; POP;
+ long cnt = TOS.n; POP;
+ long blkno = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioblk_transfer(dev, buf, blkno, cnt, VIRTIO_BLK_T_IN);
+MIRP
+
+// : virtio-blk-write ( buf blkno cnt dev -- #written )
+PRIM(virtio_X2d_blk_X2d_write)
+ void *dev = TOS.a; POP;
+ long cnt = TOS.n; POP;
+ long blkno = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioblk_transfer(dev, buf, blkno, cnt, VIRTIO_BLK_T_OUT);
+MIRP
+
+/******** virtio-fs ********/
+
+// : virtio-fs-init ( dev tx rx size -- success )
+PRIM(virtio_X2d_fs_X2d_init)
+ int size = TOS.n; POP;
+ void *rx = TOS.a; POP;
+ void *tx = TOS.a; POP;
+ void *dev = TOS.a;
+
+ TOS.n = virtio_9p_init(dev, tx, rx, size) == 0 ? -1 : 0;
+MIRP
+
+// : virtio-fs-shutdown ( dev -- )
+PRIM(virtio_X2d_fs_X2d_shutdown)
+ void *dev = TOS.a; POP;
+
+ virtio_9p_shutdown(dev);
+MIRP
+
+// : virtio-fs-load ( dev buf str -- #read )
+PRIM(virtio_X2d_fs_X2d_load)
+ char *str = TOS.a; POP;
+ void *buf = TOS.a; POP;
+ void *dev = TOS.a;
+
+ TOS.n = virtio_9p_load(dev, str, buf);
+MIRP
+
+/******** virtio-scsi ********/
+
+// : virtio-scsi-init ( dev -- success )
+PRIM(virtio_X2d_scsi_X2d_init)
+ void *dev = TOS.a;
+ TOS.u = virtioscsi_init(dev);
+MIRP
+
+// : virtio-scsi-shutdown ( dev -- )
+PRIM(virtio_X2d_scsi_X2d_shutdown)
+ void *dev = TOS.a; POP;
+ virtioscsi_shutdown(dev);
+MIRP
+
+// : virtio-scsi-send ( buf_addr buf_len is_read req_ptr rsp_ptr dev -- success)
+PRIM(virtio_X2d_scsi_X2d_send)
+ void *dev = TOS.a; POP;
+ void *resp = TOS.a; POP;
+ void *req = TOS.a; POP;
+ int is_read = !!TOS.n; POP;
+ uint64_t blen = TOS.n; POP;
+ void *buf = TOS.a;
+ TOS.n = virtioscsi_send(dev, req, resp, is_read, buf, blen);
+MIRP
+
+/******** virtio-net ********/
+
+// : virtio-net-open ( dev -- false | [ vnet true ] )
+PRIM(virtio_X2d_net_X2d_open)
+{
+ void *dev = TOS.a;
+
+ void *vnet = virtionet_open(dev);
+
+ if (vnet) {
+ TOS.u = (unsigned long)vnet; PUSH;
+ TOS.n = -1;
+ } else
+ TOS.n = 0;
+}
+MIRP
+
+// : virtio-net-close ( vnet -- )
+PRIM(virtio_X2d_net_X2d_close)
+{
+ void *vnet = TOS.a; POP;
+ virtionet_close(vnet);
+}
+MIRP
+
+// : virtio-net-read ( addr len vnet -- actual )
+PRIM(virtio_X2d_net_X2d_read)
+{
+ void *vnet = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = virtionet_read(vnet, TOS.a, len);
+}
+MIRP
+
+// : virtio-net-write ( addr len vnet -- actual )
+PRIM(virtio_X2d_net_X2d_write)
+{
+ void *vnet = TOS.a; POP;
+ int len = TOS.u; POP;
+ TOS.n = virtionet_write(vnet, 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/roms/SLOF/lib/libvirtio/virtio.h b/roms/SLOF/lib/libvirtio/virtio.h
new file mode 100644
index 000000000..c4eafe40d
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.h
@@ -0,0 +1,132 @@
+/******************************************************************************
+ * Copyright (c) 2011 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
+ *****************************************************************************/
+
+#ifndef _LIBVIRTIO_H
+#define _LIBVIRTIO_H
+
+#include <stdint.h>
+
+/* Device status bits */
+#define VIRTIO_STAT_ACKNOWLEDGE 1
+#define VIRTIO_STAT_DRIVER 2
+#define VIRTIO_STAT_DRIVER_OK 4
+#define VIRTIO_STAT_FEATURES_OK 8
+#define VIRTIO_STAT_NEEDS_RESET 64
+#define VIRTIO_STAT_FAILED 128
+
+#define BIT(x) (1UL << (x))
+
+/* VIRTIO 1.0 Device independent feature bits */
+#define VIRTIO_F_RING_INDIRECT_DESC BIT(28)
+#define VIRTIO_F_RING_EVENT_IDX BIT(29)
+#define VIRTIO_F_VERSION_1 BIT(32)
+#define VIRTIO_F_IOMMU_PLATFORM BIT(33)
+
+#define VIRTIO_TIMEOUT 5000 /* 5 sec timeout */
+
+/* Definitions for vring_desc.flags */
+#define VRING_DESC_F_NEXT 1 /* buffer continues via the next field */
+#define VRING_DESC_F_WRITE 2 /* buffer is write-only (otherwise read-only) */
+#define VRING_DESC_F_INDIRECT 4 /* buffer contains a list of buffer descriptors */
+
+/* Descriptor table entry - see Virtio Spec chapter 2.3.2 */
+struct vring_desc {
+ uint64_t addr; /* Address (guest-physical) */
+ uint32_t len; /* Length */
+ uint16_t flags; /* The flags as indicated above */
+ uint16_t next; /* Next field if flags & NEXT */
+};
+
+/* Definitions for vring_avail.flags */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* Available ring - see Virtio Spec chapter 2.3.4 */
+struct vring_avail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[];
+};
+
+/* Definitions for vring_used.flags */
+#define VRING_USED_F_NO_NOTIFY 1
+
+struct vring_used_elem {
+ uint32_t id; /* Index of start of used descriptor chain */
+ uint32_t len; /* Total length of the descriptor chain which was used */
+};
+
+struct vring_used {
+ uint16_t flags;
+ uint16_t idx;
+ struct vring_used_elem ring[];
+};
+
+/* Structure shared with SLOF and is 16bytes */
+struct virtio_cap {
+ void *addr;
+ uint8_t bar;
+ uint8_t is_io;
+ uint8_t cap_id;
+};
+
+struct vqs {
+ uint32_t size;
+ void *buf_mem;
+ struct vring_desc *desc;
+ struct vring_avail *avail;
+ struct vring_used *used;
+ void **desc_gpas; /* to get gpa from desc->addr (which is ioba) */
+ uint64_t bus_desc;
+};
+
+struct virtio_device {
+ uint64_t features;
+ struct virtio_cap legacy;
+ struct virtio_cap common;
+ struct virtio_cap notify;
+ struct virtio_cap isr;
+ struct virtio_cap device;
+ struct virtio_cap pci;
+ uint32_t notify_off_mul;
+ struct vqs vq[3];
+};
+
+/* Parts of the virtqueue are aligned on a 4096 byte page boundary */
+#define VQ_ALIGN(addr) (((addr) + 0xfff) & ~0xfff)
+
+extern unsigned long virtio_vring_size(unsigned int qsize);
+extern unsigned int virtio_get_qsize(struct virtio_device *dev, int queue);
+extern struct vring_desc *virtio_get_vring_desc(struct virtio_device *dev, int queue);
+extern struct vring_avail *virtio_get_vring_avail(struct virtio_device *dev, int queue);
+extern struct vring_used *virtio_get_vring_used(struct virtio_device *dev, int queue);
+extern void virtio_fill_desc(struct vqs *vq, int id, uint64_t features,
+ uint64_t addr, uint32_t len,
+ uint16_t flags, uint16_t next);
+extern void virtio_free_desc(struct vqs *vq, int id, uint64_t features);
+void *virtio_desc_addr(struct virtio_device *vdev, int queue, int id);
+extern struct vqs *virtio_queue_init_vq(struct virtio_device *dev, unsigned int id);
+extern void virtio_queue_term_vq(struct virtio_device *dev, struct vqs *vq, unsigned int id);
+
+extern struct virtio_device *virtio_setup_vd(void);
+extern void virtio_reset_device(struct virtio_device *dev);
+extern void virtio_queue_notify(struct virtio_device *dev, int queue);
+extern void virtio_set_status(struct virtio_device *dev, int status);
+extern void virtio_get_status(struct virtio_device *dev, int *status);
+extern void virtio_set_guest_features(struct virtio_device *dev, uint64_t features);
+extern uint64_t virtio_get_host_features(struct virtio_device *dev);
+extern int virtio_negotiate_guest_features(struct virtio_device *dev, uint64_t features);
+extern uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size);
+extern int __virtio_read_config(struct virtio_device *dev, void *dst,
+ int offset, int len);
+
+
+#endif /* _LIBVIRTIO_H */
diff --git a/roms/SLOF/lib/libvirtio/virtio.in b/roms/SLOF/lib/libvirtio/virtio.in
new file mode 100644
index 000000000..5c96c7d0d
--- /dev/null
+++ b/roms/SLOF/lib/libvirtio/virtio.in
@@ -0,0 +1,41 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 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
+ *****************************************************************************/
+
+cod(virtio-setup-vd)
+
+cod(virtio-vring-size)
+cod(virtio-get-qsize)
+cod(virtio-get-config)
+
+cod(virtio-blk-init)
+cod(virtio-blk-shutdown)
+cod(virtio-blk-read)
+cod(virtio-blk-write)
+
+cod(virtio-scsi-init)
+cod(virtio-scsi-shutdown)
+cod(virtio-scsi-send)
+
+cod(virtio-fs-init)
+cod(virtio-fs-shutdown)
+cod(virtio-fs-load)
+
+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)