diff options
author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-01-13 17:25:00 +1100 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2012-01-19 17:17:50 +1100 |
commit | 44e8d9a57d2e67e5e05d6ae1902eec90db77f752 (patch) | |
tree | fe7c574147648842e89e60cf2cc0e9f61d350a41 /lib/libvirtio | |
parent | ca610c931e2b0004c44f748cb813580b09c60489 (diff) | |
download | SLOF-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/Makefile | 2 | ||||
-rw-r--r-- | lib/libvirtio/p9.c | 575 | ||||
-rw-r--r-- | lib/libvirtio/p9.h | 68 | ||||
-rw-r--r-- | lib/libvirtio/virtio-9p.c | 336 | ||||
-rw-r--r-- | lib/libvirtio/virtio-9p.h | 31 | ||||
-rw-r--r-- | lib/libvirtio/virtio-blk.c | 6 | ||||
-rw-r--r-- | lib/libvirtio/virtio.c | 22 | ||||
-rw-r--r-- | lib/libvirtio/virtio.code | 33 | ||||
-rw-r--r-- | lib/libvirtio/virtio.h | 2 | ||||
-rw-r--r-- | lib/libvirtio/virtio.in | 5 |
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) |