Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / SLOF / lib / libvirtio / p9.c
diff --git a/qemu/roms/SLOF/lib/libvirtio/p9.c b/qemu/roms/SLOF/lib/libvirtio/p9.c
new file mode 100644 (file)
index 0000000..a556629
--- /dev/null
@@ -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;
+}