diff options
author | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
---|---|---|
committer | Angelos Mouzakitis <a.mouzakitis@virtualopensystems.com> | 2023-10-10 14:33:42 +0000 |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/SLOF/lib/libvirtio | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/SLOF/lib/libvirtio')
-rw-r--r-- | roms/SLOF/lib/libvirtio/Makefile | 55 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/p9.c | 575 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/p9.h | 68 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-9p.c | 340 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-9p.h | 32 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-blk.c | 209 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-blk.h | 61 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-internal.h | 48 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-net.c | 384 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-net.h | 40 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-scsi.c | 152 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-scsi.h | 69 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-serial.c | 202 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio-serial.h | 27 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio.c | 645 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio.code | 203 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio.h | 132 | ||||
-rw-r--r-- | roms/SLOF/lib/libvirtio/virtio.in | 41 |
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) |