aboutsummaryrefslogtreecommitdiff
path: root/lib/libvirtio
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-01-13 17:25:00 +1100
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2012-01-19 17:17:50 +1100
commit44e8d9a57d2e67e5e05d6ae1902eec90db77f752 (patch)
treefe7c574147648842e89e60cf2cc0e9f61d350a41 /lib/libvirtio
parentca610c931e2b0004c44f748cb813580b09c60489 (diff)
downloadSLOF-44e8d9a57d2e67e5e05d6ae1902eec90db77f752.zip
SLOF-44e8d9a57d2e67e5e05d6ae1902eec90db77f752.tar.gz
SLOF-44e8d9a57d2e67e5e05d6ae1902eec90db77f752.tar.bz2
Add virtfs support
Code originally written by Timothy Rule and reworked & bug fixed by myself Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'lib/libvirtio')
-rw-r--r--lib/libvirtio/Makefile2
-rw-r--r--lib/libvirtio/p9.c575
-rw-r--r--lib/libvirtio/p9.h68
-rw-r--r--lib/libvirtio/virtio-9p.c336
-rw-r--r--lib/libvirtio/virtio-9p.h31
-rw-r--r--lib/libvirtio/virtio-blk.c6
-rw-r--r--lib/libvirtio/virtio.c22
-rw-r--r--lib/libvirtio/virtio.code33
-rw-r--r--lib/libvirtio/virtio.h2
-rw-r--r--lib/libvirtio/virtio.in5
10 files changed, 1078 insertions, 2 deletions
diff --git a/lib/libvirtio/Makefile b/lib/libvirtio/Makefile
index e4fb34e..994a885 100644
--- a/lib/libvirtio/Makefile
+++ b/lib/libvirtio/Makefile
@@ -22,7 +22,7 @@ TARGET = ../libvirtio.a
all: $(TARGET)
-SRCS = virtio.c virtio-blk.c
+SRCS = virtio.c virtio-blk.c p9.c virtio-9p.c
OBJS = $(SRCS:%.c=%.o)
diff --git a/lib/libvirtio/p9.c b/lib/libvirtio/p9.c
new file mode 100644
index 0000000..a556629
--- /dev/null
+++ b/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;
+ int tx_size = GET_SIZE;
+ int 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;
+ int 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;
+ }
+ int tx_size = (e_tok - s_tok + 2 + GET_SIZE);
+ int 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/lib/libvirtio/p9.h b/lib/libvirtio/p9.h
new file mode 100644
index 0000000..7df9ef4
--- /dev/null
+++ b/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, int *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/lib/libvirtio/virtio-9p.c b/lib/libvirtio/virtio-9p.c
new file mode 100644
index 0000000..129a2b4
--- /dev/null
+++ b/lib/libvirtio/virtio-9p.c
@@ -0,0 +1,336 @@
+/******************************************************************************
+ * 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 "virtio-9p.h"
+#include "p9.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 sync() asm volatile (" sync \n" ::: "memory")
+#define MIN(a,b) ((a)>(b)?(b):(a))
+
+
+#undef DEBUG
+//#define DEBUG
+#ifdef DEBUG
+#define dprintf(_x ...) printf(_x)
+#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,
+ int *rx_size)
+{
+ struct virtio_device *dev = opaque;
+ struct vring_desc *desc;
+ int id, i;
+ uint32_t vq_size;
+ struct vring_desc *vq_desc;
+ struct vring_avail *vq_avail;
+ struct vring_used *vq_used;
+ volatile uint16_t *current_used_idx;
+ uint16_t last_used_idx;
+
+
+ /* Virt IO queues. */
+ vq_size = virtio_get_qsize(dev, 0);
+ vq_desc = virtio_get_vring_desc(dev, 0);
+ vq_avail = virtio_get_vring_avail(dev, 0);
+ vq_used = virtio_get_vring_used(dev, 0);
+
+ last_used_idx = vq_used->idx;
+ current_used_idx = &vq_used->idx;
+
+ /* Determine descriptor index */
+ id = (vq_avail->idx * 3) % vq_size;
+
+ /* TX in first queue item. */
+ dprint_buffer("TX", tx, tx_size);
+
+ desc = &vq_desc[id];
+ desc->addr = (uint64_t)tx;
+ desc->len = tx_size;
+ desc->flags = VRING_DESC_F_NEXT;
+ desc->next = (id + 1) % vq_size;
+
+ /* RX in the second queue item. */
+ desc = &vq_desc[(id + 1) % vq_size];
+ desc->addr = (uint64_t)rx;
+ desc->len = *rx_size;
+ desc->flags = VRING_DESC_F_WRITE;
+ desc->next = 0;
+
+ /* Tell HV that the queue is ready */
+ vq_avail->ring[vq_avail->idx % vq_size] = id;
+ sync();
+ vq_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
+ sync();
+ }
+ 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 vring_avail *vq_avail;
+
+ /* 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);
+
+ /* 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, VIRTIO_STAT_ACKNOWLEDGE);
+
+ /* Tell HV that we know how to drive the device. */
+ virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER);
+
+ /* Device specific setup - we do not support special features */
+ virtio_set_guest_features(dev, 0);
+
+ vq_avail = virtio_get_vring_avail(dev, 0);
+ vq_avail->flags = VRING_AVAIL_F_NO_INTERRUPT;
+ vq_avail->idx = 0;
+
+ /* Tell HV that setup succeeded */
+ virtio_set_status(dev, VIRTIO_STAT_ACKNOWLEDGE | VIRTIO_STAT_DRIVER
+ |VIRTIO_STAT_DRIVER_OK);
+
+ /* 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;
+}
+
+/**
+ * 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.
+ */
+int 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 ? offset : rc;
+}
diff --git a/lib/libvirtio/virtio-9p.h b/lib/libvirtio/virtio-9p.h
new file mode 100644
index 0000000..7513d98
--- /dev/null
+++ b/lib/libvirtio/virtio-9p.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+ * 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"
+
+typedef struct {
+ uint16_t tag_lenth;
+ char tag[0];
+} virtio_9p_config_t;
+
+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);
+int virtio_9p_load(struct virtio_device *dev, const char *file_name, uint8_t *buffer);
+
+
+#endif /* VIRTIO_9P_H_ */
diff --git a/lib/libvirtio/virtio-blk.c b/lib/libvirtio/virtio-blk.c
index 7b1e795..e470968 100644
--- a/lib/libvirtio/virtio-blk.c
+++ b/lib/libvirtio/virtio-blk.c
@@ -27,7 +27,10 @@ virtioblk_init(struct virtio_device *dev)
struct vring_avail *vq_avail;
/* Reset device */
- // virtio_reset_device(dev);
+ // 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, VIRTIO_STAT_ACKNOWLEDGE);
@@ -146,6 +149,7 @@ virtioblk_read(struct virtio_device *dev, char *buf, long blocknum, long cnt)
/* Wait for host to consume the descriptor */
i = 10000000;
while (*current_used_idx == last_used_idx && i-- > 0) {
+ // do something better
sync();
}
diff --git a/lib/libvirtio/virtio.c b/lib/libvirtio/virtio.c
index 4e70053..29d5dd3 100644
--- a/lib/libvirtio/virtio.c
+++ b/lib/libvirtio/virtio.c
@@ -189,3 +189,25 @@ uint64_t virtio_get_config(struct virtio_device *dev, int offset, int size)
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;
+
+ switch (dev->type) {
+ case VIRTIO_TYPE_PCI:
+ confbase = dev->base+VIRTIOHDR_DEVICE_CONFIG;
+ break;
+ default:
+ return 0;
+ }
+ for (i = 0; i < len; i++)
+ buf[i] = ci_read_8(confbase + offset + i);
+ return len;
+}
diff --git a/lib/libvirtio/virtio.code b/lib/libvirtio/virtio.code
index 0ed3dc7..65e1d8a 100644
--- a/lib/libvirtio/virtio.code
+++ b/lib/libvirtio/virtio.code
@@ -12,6 +12,9 @@
#include <virtio.h>
#include <virtio-blk.h>
+#include <virtio-9p.h>
+
+/******** core virtio ********/
// : virtio-vring-size ( queuesize -- ringsize )
PRIM(virtio_X2d_vring_X2d_size)
@@ -31,6 +34,8 @@ PRIM(virtio_X2d_get_X2d_config)
TOS.u = virtio_get_config(TOS.a, offset, size);
MIRP
+/******** virtio-blk ********/
+
// : virtio-blk-init ( dev -- )
PRIM(virtio_X2d_blk_X2d_init)
void *dev = TOS.a; POP;
@@ -51,3 +56,31 @@ PRIM(virtio_X2d_blk_X2d_read)
void *buf = TOS.a;
TOS.n = virtioblk_read(dev, buf, blkno, cnt);
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
diff --git a/lib/libvirtio/virtio.h b/lib/libvirtio/virtio.h
index 7355043..37d4eb3 100644
--- a/lib/libvirtio/virtio.h
+++ b/lib/libvirtio/virtio.h
@@ -79,6 +79,8 @@ 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_set_guest_features(struct virtio_device *dev, int 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/lib/libvirtio/virtio.in b/lib/libvirtio/virtio.in
index 1f2cfd1..205bb13 100644
--- a/lib/libvirtio/virtio.in
+++ b/lib/libvirtio/virtio.in
@@ -13,6 +13,11 @@
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-fs-init)
+cod(virtio-fs-shutdown)
+cod(virtio-fs-load)