aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorDavid 'Digit' Turner <digit@android.com>2011-04-26 18:09:17 +0200
committerDavid 'Digit' Turner <digit@android.com>2011-04-28 16:41:50 +0200
commitd4d688e926097faba7fd3717d1c0d3f296bdb526 (patch)
treeb6e89c99abae42e5bcef26afaa2952b5a870895b /hw
parenta65e41529e4e3900372d54859f8c559cf79d953c (diff)
downloadexternal_qemu-d4d688e926097faba7fd3717d1c0d3f296bdb526.zip
external_qemu-d4d688e926097faba7fd3717d1c0d3f296bdb526.tar.gz
external_qemu-d4d688e926097faba7fd3717d1c0d3f296bdb526.tar.bz2
New goldfish_pipe virtual device.
This adds a new virtual hardware device named "goldfish_pipe" used to implement a very fast communication channel between the guest system and the emulator. IMPORTANT: This depends on a special kernel driver, see: https://review.source.android.com/#change,22496 Usage from the guest is simply the following: fd = open("/dev/qemu_pipe", O_RDWR); const char* pipename = "pipe:<name>"; ret = write(fd, pipename, strlen(pipename)+1); if (ret < 0) { /* could not connect to service named <name> */ } /* now you can read()/write()/close() as a normal * file descriptor to exchange data with the service. */ In addition, this implements the following pipe services in the emulator: tcp:<port> tcp:<hostname>:<port> unix:<path> opengles The 'tcp:' and 'unix:' services simply redirect to a TCP or Unix socket on the host with minimal The 'opengles' service simply connects to tcp:locahost:22468 for now. We may change this to be more configurable in the future, but that's the port number used by the current experimental OpenGL ES hardware emulation host libraries / programs. Benchmarking with a simple ping-pong program shows that the guest <-> emulator can achieve a roundtrip bandwidth of 192 MB/s (on a 2.7 Ghz Xeon PC). Using the tcp: service to talk to a ping-pong server listening on localhost reaches 102 MB/s on the same machine, using a Unix socket reaches 140 MB/s. By contrast, using standard sockets in the guest reaches only 3.8 MB/s on the same machine (and requires special privileges from the application anyway).
Diffstat (limited to 'hw')
-rw-r--r--hw/android_arm.c4
-rw-r--r--hw/goldfish_pipe.c1388
-rw-r--r--hw/goldfish_pipe.h203
-rw-r--r--hw/goldfish_trace.c15
4 files changed, 996 insertions, 614 deletions
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 806d9bd..3b9dc6d 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -27,7 +27,6 @@
#endif // CONFIG_MEMCHECK
#include "android/utils/debug.h"
-#include "android/hw-qemud-pipe.h"
#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
@@ -148,7 +147,6 @@ static void android_arm_init_(ram_addr_t ram_size,
#ifdef CONFIG_MEMCHECK
|| memcheck_enabled
#endif // CONFIG_MEMCHECK
- || 1 /* XXX: ALWAYS AVAILABLE FOR QEMUD PIPES */
) {
trace_dev_init();
}
@@ -159,7 +157,7 @@ static void android_arm_init_(ram_addr_t ram_size,
}
#endif
- init_qemud_pipes();
+ pipe_dev_init();
#if TEST_SWITCH
{
diff --git a/hw/goldfish_pipe.c b/hw/goldfish_pipe.c
index 6c24208..998ba49 100644
--- a/hw/goldfish_pipe.c
+++ b/hw/goldfish_pipe.c
@@ -9,14 +9,16 @@
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
*/
-#include "android/utils/intmap.h"
#include "android/utils/panic.h"
-#include "android/utils/reflist.h"
#include "android/utils/system.h"
-#include "android/hw-qemud.h"
#include "hw/goldfish_pipe.h"
+#include "hw/goldfish_device.h"
+#include "qemu-timer.h"
-#define DEBUG 0
+#define DEBUG 1
+
+/* Set to 1 to debug i/o register reads/writes */
+#define DEBUG_REGS 0
#if DEBUG >= 1
# define D(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
@@ -30,711 +32,1035 @@
# define DD(...) (void)0
#endif
+#if DEBUG_REGS >= 1
+# define DR(...) D(__VA_ARGS__)
+#else
+# define DR(...) (void)0
+#endif
+
#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
-/* Must match hw/goldfish_trace.h */
-#define SLOT_COMMAND 0
-#define SLOT_STATUS 0
-#define SLOT_ADDRESS 1
-#define SLOT_SIZE 2
-#define SLOT_CHANNEL 3
-
-#define QEMUD_PIPE_CMD_CLOSE 1
-#define QEMUD_PIPE_CMD_SEND 2
-#define QEMUD_PIPE_CMD_RECV 3
-#define QEMUD_PIPE_CMD_WAKE_ON_SEND 4
-#define QEMUD_PIPE_CMD_WAKE_ON_RECV 5
-
-#define QEMUD_PIPE_ERROR_INVAL 22 /* EINVAL */
-#define QEMUD_PIPE_ERROR_AGAIN 11 /* EAGAIN */
-#define QEMUD_PIPE_ERROR_CONNRESET 104 /* ECONNRESET */
-#define QEMUD_PIPE_ERROR_NOMEM 12 /* ENOMEM */
-
-/**********************************************************************
- **********************************************************************
- **
- ** TECHNICAL NOTE ON THE FOLLOWING IMPLEMENTATION:
- **
- ** PipeService ::
- ** The global state for the QEMUD fast pipes service.
- ** Holds a tid -> ThreadState map. Registers as a Qemud
- ** service named "fast-pipes" which creates PipeClient
- ** objects on connection.
- **
- ** PipeClient ::
- ** The state of each QEMUD pipe. This handles the initial
- ** connection, message exchanges and signalling.
- **
- ** ThreadState ::
- ** Hold the thread-specific state corresponding to each guest
- ** thread that has created a least one qemud pipe. Stores the
- ** state of our 4 I/O Registers, and a map of localId -> PipeState
- **
- **
- ** The following graphics is an example corresponding to the following
- ** situation:
- **
- ** - two guest threads have opened pipe connections
- ** - the first thread has opened two different pipes
- ** - the second thread has opened only one pipe
- **
- **
- ** QEMUD-SERVICE
- ** | |
- ** | |________________________________________
- ** | | | |
- ** | v v v
- ** | QEMUD-CLIENT#1 QEMUD-CLIENT#2 QEMUD-CLIENT#3
- ** | ^ ^ ^
- ** | | | |
- ** | | | |
- ** | v v v
- ** | PIPE-CLIENT#1 PIPE-CLIENT#2 PIPE-CLIENT#3
- ** | ^ ^ ^
- ** | | | |
- ** | |________________| |
- ** | | +
- ** | | |
- ** | THREAD-STATE#1 THREAD-STATE#2
- ** | ^ ^
- ** | ____|___________________________________|
- ** | |
- ** | |
- ** PIPE-SERVICE
- **
- ** Note that the QemudService and QemudClient objects are created by
- ** hw-qemud.c and not defined here.
- **
- **/
-
-typedef struct PipeService PipeService;
-typedef struct PipeClient PipeClient;
-
-static void pipeService_removeState(PipeService* pipeSvc, int tid);
-
-/**********************************************************************
- **********************************************************************
+/* Set to 1 to enable the 'zero' pipe type, useful for debugging */
+#define DEBUG_ZERO_PIPE 1
+
+/* Set to 1 to enable the 'pingpong' pipe type, useful for debugging */
+#define DEBUG_PINGPONG_PIPE 1
+
+/* Set to 1 to enable the 'throttle' pipe type, useful for debugging */
+#define DEBUG_THROTTLE_PIPE 1
+
+/***********************************************************************
+ ***********************************************************************
*****
- ***** REGISTRY OF SUPPORTED PIPE SERVICES
+ ***** P I P E S E R V I C E R E G I S T R A T I O N
*****
*****/
+#define MAX_PIPE_SERVICES 8
typedef struct {
- const char* pipeName;
- void* pipeOpaque;
- const QemudPipeHandlerFuncs* pipeFuncs;
-} PipeType;
+ const char* name;
+ void* opaque;
+ GoldfishPipeFuncs funcs;
+} PipeService;
-#define MAX_PIPE_TYPES 4
+typedef struct {
+ int count;
+ PipeService services[MAX_PIPE_SERVICES];
+} PipeServices;
-static PipeType sPipeTypes[MAX_PIPE_TYPES];
-static int sPipeTypeCount;
+static PipeServices _pipeServices[1];
void
-goldfish_pipe_add_type( const char* pipeName,
- void* pipeOpaque,
- const QemudPipeHandlerFuncs* pipeFuncs )
+goldfish_pipe_add_type(const char* pipeName,
+ void* pipeOpaque,
+ const GoldfishPipeFuncs* pipeFuncs )
{
- int count = sPipeTypeCount;
+ PipeServices* list = _pipeServices;
+ int count = list->count;
- if (count >= MAX_PIPE_TYPES) {
- APANIC("%s: Too many qemud pipe types!", __FUNCTION__);
+ if (count >= MAX_PIPE_SERVICES) {
+ APANIC("Too many goldfish pipe services (%d)", count);
}
- sPipeTypes[count].pipeName = pipeName;
- sPipeTypes[count].pipeOpaque = pipeOpaque;
- sPipeTypes[count].pipeFuncs = pipeFuncs;
- sPipeTypeCount = ++count;
+ list->services[count].name = pipeName;
+ list->services[count].opaque = pipeOpaque;
+ list->services[count].funcs = pipeFuncs[0];
+
+ list->count++;
}
-static const PipeType*
-goldfish_pipe_find_type( const char* pipeName )
+static const PipeService*
+goldfish_pipe_find_type(const char* pipeName)
{
- const PipeType* ptype = sPipeTypes;
- const PipeType* limit = ptype + sPipeTypeCount;
+ PipeServices* list = _pipeServices;
+ int count = list->count;
+ int nn;
- for ( ; ptype < limit; ptype++ ) {
- if (!strcmp(pipeName, ptype->pipeName)) {
- return ptype;
+ for (nn = 0; nn < count; nn++) {
+ if (!strcmp(list->services[nn].name, pipeName)) {
+ return &list->services[nn];
}
}
return NULL;
}
-/**********************************************************************
- **********************************************************************
+/***********************************************************************
+ ***********************************************************************
*****
- ***** THREAD-SPECIFIC STATE
+ ***** P I P E C O N N E C T I O N S
*****
*****/
-static void
-pipeClient_closeFromThread( PipeClient* pcl );
-
-static uint32_t
-pipeClient_doCommand( PipeClient* pcl,
- uint32_t command,
- uint32_t address,
- uint32_t* pSize );
-
-
-/* For each guest thread, we will store the following state:
- *
- * - The current state of the 'address', 'size', 'localId' and 'status'
- * I/O slots provided through the magic page by hw/goldfish_trace.c
- *
- * - A list of PipeClient objects, corresponding to all the pipes in
- * this thread, identified by localId.
- */
-typedef struct {
- uint32_t address;
- uint32_t size;
- uint32_t localId;
- uint32_t status;
- AIntMap* pipes;
-} ThreadState;
+typedef struct PipeDevice PipeDevice;
-static void
-threadState_free( ThreadState* ts )
-{
- /* Get rid of the localId -> PipeClient map */
- AINTMAP_FOREACH_VALUE(ts->pipes, pcl, pipeClient_closeFromThread(pcl));
- aintMap_free(ts->pipes);
+typedef struct Pipe {
+ struct Pipe* next;
+ struct Pipe* next_waked;
+ PipeDevice* device;
+ uint32_t channel;
+ void* opaque;
+ const GoldfishPipeFuncs* funcs;
+ unsigned char wanted;
+ char closed;
+} Pipe;
- AFREE(ts);
-}
+/* Forward */
+static void* pipeConnector_new(Pipe* pipe);
-static ThreadState*
-threadState_new( void )
+Pipe*
+pipe_new(uint32_t channel, PipeDevice* dev)
{
- ThreadState* ts;
- ANEW0(ts);
- ts->pipes = aintMap_new();
- return ts;
+ Pipe* pipe;
+ ANEW0(pipe);
+ pipe->channel = channel;
+ pipe->device = dev;
+ pipe->opaque = pipeConnector_new(pipe);
+ return pipe;
}
-static int
-threadState_addPipe( ThreadState* ts, int localId, PipeClient* pcl )
+static Pipe**
+pipe_list_findp_channel( Pipe** list, uint32_t channel )
{
- /* We shouldn't already have a pipe for this localId */
- if (aintMap_get(ts->pipes, localId) != NULL) {
- errno = EBADF;
- return -1;
+ Pipe** pnode = list;
+ for (;;) {
+ Pipe* node = *pnode;
+ if (node == NULL || node->channel == channel) {
+ break;
+ }
+ pnode = &node->next;
}
- aintMap_set(ts->pipes, localId, pcl);
- return 0;
+ return pnode;
}
-static void
-threadState_delPipe( ThreadState* ts, int localId )
+#if 0
+static Pipe**
+pipe_list_findp_opaque( Pipe** list, void* opaque )
{
- aintMap_del(ts->pipes, localId);
+ Pipe** pnode = list;
+ for (;;) {
+ Pipe* node = *pnode;
+ if (node == NULL || node->opaque == opaque) {
+ break;
+ }
+ pnode = &node->next;
+ }
+ return pnode;
}
+#endif
-static void
-threadState_write( ThreadState* ts, int offset, uint32_t value )
+static Pipe**
+pipe_list_findp_waked( Pipe** list, Pipe* pipe )
{
- PipeClient* pcl;
-
- switch (offset) {
- case SLOT_COMMAND:
- pcl = aintMap_get(ts->pipes, (int)ts->localId);
- if (pcl == NULL) {
- D("%s: Invalid localId (%d)",
- __FUNCTION__, ts->localId);
- ts->status = QEMUD_PIPE_ERROR_INVAL;
- } else {
- ts->status = pipeClient_doCommand(pcl,
- value,
- ts->address,
- &ts->size);
- }
- break;
- case SLOT_ADDRESS:
- ts->address = value;
+ Pipe** pnode = list;
+ for (;;) {
+ Pipe* node = *pnode;
+ if (node == NULL || node == pipe) {
break;
- case SLOT_SIZE:
- ts->size = value;
- break;
- case SLOT_CHANNEL:
- ts->localId = value;
- break;
- default:
- /* XXX: PRINT ERROR? */
- ;
+ }
+ pnode = &node->next_waked;
}
+ return pnode;
}
-static uint32_t
-threadState_read( ThreadState* ts, int offset )
+
+static void
+pipe_list_remove_waked( Pipe** list, Pipe* pipe )
{
- switch (offset) {
- case SLOT_STATUS: return ts->status;
- case SLOT_ADDRESS: return ts->address;
- case SLOT_SIZE: return ts->size;
- case SLOT_CHANNEL: return ts->localId;
- default: return 0;
+ Pipe** lookup = pipe_list_findp_waked(list, pipe);
+ Pipe* node = *lookup;
+
+ if (node != NULL) {
+ (*lookup) = node->next_waked;
+ node->next_waked = NULL;
}
}
-/**********************************************************************
- **********************************************************************
+/***********************************************************************
+ ***********************************************************************
*****
- ***** PIPE CLIENT STATE
+ ***** P I P E C O N N E C T O R S
*****
*****/
-/* Each client object points to a PipeState after it has received the
- * initial request from the guest, which shall look like:
- * <tid>:<localId>:<name>
+/* These are used to handle the initial connection attempt, where the
+ * client is going to write the name of the pipe service it wants to
+ * connect to, followed by a terminating zero.
*/
-struct PipeClient {
- char* pipeName;
- QemudClient* client;
- PipeService* pipeSvc;
-
- int tid;
- uint32_t localId;
- void* handler;
- const QemudPipeHandlerFuncs* handlerFuncs;
-};
+typedef struct {
+ Pipe* pipe;
+ char buffer[128];
+ int buffpos;
+} PipeConnector;
-static int pipeService_addPipe(PipeService* pipeSvc, int tid, int localId, PipeClient* pcl);
-static void pipeService_removePipe(PipeService* pipeSvc, int tid, int localId);
+static const GoldfishPipeFuncs pipeConnector_funcs; // forward
-static void
-pipeClient_closeFromThread( PipeClient* pcl )
+void*
+pipeConnector_new(Pipe* pipe)
{
- qemud_client_close(pcl->client);
+ PipeConnector* pcon;
+
+ ANEW0(pcon);
+ pcon->pipe = pipe;
+ pipe->funcs = &pipeConnector_funcs;
+ return pcon;
}
-/* This function should only be invoked through qemud_client_close().
- * Never call it explicitely. */
static void
-pipeClient_close( void* opaque )
+pipeConnector_close( void* opaque )
{
- PipeClient* pcl = opaque;
-
- if (pcl->handler && pcl->handlerFuncs && pcl->handlerFuncs->close) {
- pcl->handlerFuncs->close(pcl->handler);
- }
-
- D("qemud:pipe: closing client (%d,%x)", pcl->tid, pcl->localId);
- qemud_client_close(pcl->client);
- pcl->client = NULL;
-
- /* The guest is closing the connection, so remove it from our state */
- pipeService_removePipe(pcl->pipeSvc, pcl->tid, pcl->localId);
-
- AFREE(pcl->pipeName);
+ PipeConnector* pcon = opaque;
+ AFREE(pcon);
}
-static void
-pipeClient_recv( void* opaque, uint8_t* msg, int msgLen, QemudClient* client )
+static int
+pipeConnector_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
{
- PipeClient* pcl = opaque;
- const PipeType* ptype;
+ PipeConnector* pcon = opaque;
+ const GoldfishPipeBuffer* buffers_limit = buffers + numBuffers;
+ int ret = 0;
- const char* p;
- int failure = 1;
- char answer[64];
+ DD("%s: channel=0x%x numBuffers=%d", __FUNCTION__,
+ pcon->pipe->channel,
+ numBuffers);
- if (pcl->pipeName != NULL) {
- /* Should never happen, the only time we'll receive something
- * is when the connection is created! Simply ignore any incoming
- * message.
- */
- return;
- }
+ while (buffers < buffers_limit) {
+ int avail;
- /* The message format is <tid>:<localIdHex>:<name> */
- if (sscanf((char*)msg, "%d:%x:", &pcl->tid, &pcl->localId) != 2) {
- goto BAD_FORMAT;
- }
- p = strchr((const char*)msg, ':');
- if (p != NULL) {
- p = strchr(p+1, ':');
- if (p != NULL)
- p += 1;
- }
- if (p == NULL || *p == '\0') {
- goto BAD_FORMAT;
- }
+ DD("%s: buffer data (%3d bytes): '%.*s'", __FUNCTION__,
+ buffers[0].size, buffers[0].size, buffers[0].data);
- pcl->pipeName = ASTRDUP(p);
+ if (buffers[0].size == 0) {
+ buffers++;
+ continue;
+ }
- ptype = goldfish_pipe_find_type(pcl->pipeName);
- if (ptype == NULL) {
- goto UNKNOWN_PIPE_TYPE;
- }
+ avail = sizeof(pcon->buffer) - pcon->buffpos;
+ if (avail > buffers[0].size)
+ avail = buffers[0].size;
- pcl->handlerFuncs = ptype->pipeFuncs;
- pcl->handler = ptype->pipeFuncs->init( client, ptype->pipeOpaque );
- if (pcl->handler == NULL) {
- goto BAD_INIT;
+ if (avail > 0) {
+ memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
+ pcon->buffpos += avail;
+ ret += avail;
+ }
+ buffers++;
}
- if (pipeService_addPipe(pcl->pipeSvc, pcl->tid, pcl->localId, pcl) < 0) {
- goto DUPLICATE_REQUEST;
- }
+ /* Now check that our buffer contains a zero-terminated string */
+ if (memchr(pcon->buffer, '\0', pcon->buffpos) != NULL) {
+ /* Acceptable formats for the connection string are:
+ *
+ * pipe:<name>
+ * pipe:<name>:<arguments>
+ */
+ char* pipeName;
+ char* pipeArgs;
- D("qemud:pipe: Added new client: %s", msg);
- failure = 0;
- snprintf(answer, sizeof answer, "OK");
- goto SEND_ANSWER;
+ D("%s: connector: '%s'", __FUNCTION__, pcon->buffer);
-BAD_INIT:
- /* Initialization failed for some reason! */
- E("qemud:pipe: Could not initialize pipe: '%s'", pcl->pipeName);
- snprintf(answer, sizeof answer, "KO:%d:Could not initialize pipe",
- QEMUD_PIPE_ERROR_INVAL);
- goto SEND_ANSWER;
+ if (memcmp(pcon->buffer, "pipe:", 5) != 0) {
+ /* Nope, we don't handle these for now. */
+ D("%s: Unknown pipe connection: '%s'", __FUNCTION__, pcon->buffer);
+ return PIPE_ERROR_INVAL;
+ }
+
+ pipeName = pcon->buffer + 5;
+ pipeArgs = strchr(pipeName, ':');
-UNKNOWN_PIPE_TYPE:
- E("qemud:pipe: Unknown pipe type: '%s'", p);
- snprintf(answer, sizeof answer, "KO:%d:Unknown pipe type name",
- QEMUD_PIPE_ERROR_INVAL);
- goto SEND_ANSWER;
+ if (pipeArgs != NULL) {
+ *pipeArgs++ = '\0';
+ }
-BAD_FORMAT:
- E("qemud:pipe: Invalid connection request: '%s'", msg);
- snprintf(answer, sizeof answer, "KO:%d:Invalid connection request",
- QEMUD_PIPE_ERROR_INVAL);
- goto SEND_ANSWER;
+ Pipe* pipe = pcon->pipe;
+ const PipeService* svc = goldfish_pipe_find_type(pipeName);
+ if (svc == NULL) {
+ D("%s: Unknown server!", __FUNCTION__);
+ return PIPE_ERROR_INVAL;
+ }
-DUPLICATE_REQUEST:
- E("qemud:pipe: Duplicate connection request: '%s'", msg);
- snprintf(answer, sizeof answer, "KO:%d:Duplicate connection request",
- QEMUD_PIPE_ERROR_INVAL);
- goto SEND_ANSWER;
+ void* peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
+ if (peer == NULL) {
+ D("%s: Initialization failed!", __FUNCTION__);
+ return PIPE_ERROR_INVAL;
+ }
-SEND_ANSWER:
- qemud_client_send(client, (uint8_t*)answer, strlen(answer));
- if (failure) {
- qemud_client_close(client);
- } else {
- /* Disable framing for the rest of signalling */
- qemud_client_set_framing(client, 0);
+ /* Do the evil switch now */
+ pipe->opaque = peer;
+ pipe->funcs = &svc->funcs;
+ AFREE(pcon);
}
+
+ return ret;
}
-/* Count the number of GoldfishPipeBuffers we will need to transfer
- * memory from/to [address...address+size)
- */
static int
-_countPipeBuffers( uint32_t address, uint32_t size )
+pipeConnector_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
{
- CPUState* env = cpu_single_env;
- int count = 0;
+ return PIPE_ERROR_IO;
+}
- while (size > 0) {
- uint32_t vstart = address & TARGET_PAGE_MASK;
- uint32_t vend = vstart + TARGET_PAGE_SIZE;
- uint32_t next = address + size;
+static unsigned
+pipeConnector_poll( void* opaque )
+{
+ return PIPE_WAKE_WRITE;
+}
- DD("%s: trying to map (0x%x - 0x%0x, %d bytes) -> page=0x%x - 0x%x",
- __FUNCTION__, address, address+size, size, vstart, vend);
+static void
+pipeConnector_wakeOn( void* opaque, int flags )
+{
+ /* nothing, really should never happen */
+}
- if (next > vend) {
- next = vend;
- }
+static const GoldfishPipeFuncs pipeConnector_funcs = {
+ NULL, /* init */
+ pipeConnector_close, /* should rarely happen */
+ pipeConnector_sendBuffers, /* the interesting stuff */
+ pipeConnector_recvBuffers, /* should not happen */
+ pipeConnector_poll, /* should not happen */
+ pipeConnector_wakeOn, /* should not happen */
+};
- /* Check that the address is valid */
- if (cpu_get_phys_page_debug(env, vstart) == -1) {
- DD("%s: bad guest address!", __FUNCTION__);
- return -1;
- }
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** Z E R O P I P E S
+ *****
+ *****/
+
+/* A simple pipe service that mimics /dev/zero, you can write anything to
+ * it, and you can always read any number of zeros from it. Useful for debugging
+ * the kernel driver.
+ */
+#if DEBUG_ZERO_PIPE
+
+typedef struct {
+ void* hwpipe;
+} ZeroPipe;
+
+static void*
+zeroPipe_init( void* hwpipe, void* svcOpaque, const char* args )
+{
+ ZeroPipe* zpipe;
+
+ D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+ ANEW0(zpipe);
+ zpipe->hwpipe = hwpipe;
+ return zpipe;
+}
- count++;
+static void
+zeroPipe_close( void* opaque )
+{
+ ZeroPipe* zpipe = opaque;
- size -= (next - address);
- address = next;
+ D("%s: hwpipe=%p", __FUNCTION__, zpipe->hwpipe);
+ AFREE(zpipe);
+}
+
+static int
+zeroPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+ int ret = 0;
+ while (numBuffers > 0) {
+ ret += buffers[0].size;
+ buffers++;
+ numBuffers--;
}
- return count;
+ return ret;
+}
+
+static int
+zeroPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+ int ret = 0;
+ while (numBuffers > 0) {
+ ret += buffers[0].size;
+ memset(buffers[0].data, 0, buffers[0].size);
+ buffers++;
+ numBuffers--;
+ }
+ return ret;
+}
+
+static unsigned
+zeroPipe_poll( void* opaque )
+{
+ return PIPE_WAKE_READ | PIPE_WAKE_WRITE;
}
-/* Fill the pipe buffers to prepare memory transfer from/to
- * [address...address+size). This assumes 'buffers' points to an array
- * which size corresponds to _countPipeBuffers(address, size)
- */
static void
-_fillPipeBuffers( uint32_t address, uint32_t size, GoldfishPipeBuffer* buffers )
+zeroPipe_wakeOn( void* opaque, int flags )
{
- CPUState* env = cpu_single_env;
+ /* nothing to do here */
+}
- while (size > 0) {
- uint32_t vstart = address & TARGET_PAGE_MASK;
- uint32_t vend = vstart + TARGET_PAGE_SIZE;
- uint32_t next = address + size;
+static const GoldfishPipeFuncs zeroPipe_funcs = {
+ zeroPipe_init,
+ zeroPipe_close,
+ zeroPipe_sendBuffers,
+ zeroPipe_recvBuffers,
+ zeroPipe_poll,
+ zeroPipe_wakeOn,
+};
- if (next > vend) {
- next = vend;
- }
+#endif /* DEBUG_ZERO */
- /* Translate virtual address into physical one, into emulator
- * memory. */
- target_phys_addr_t phys = cpu_get_phys_page_debug(env, vstart);
+/***********************************************************************
+ ***********************************************************************
+ *****
+ ***** P I N G P O N G P I P E S
+ *****
+ *****/
- buffers[0].data = qemu_get_ram_ptr(phys) + (address - vstart);
- buffers[0].size = next - address;
- buffers++;
+/* Similar debug service that sends back anything it receives */
+/* All data is kept in a circular dynamic buffer */
- size -= (next - address);
- address = next;
- }
+#if DEBUG_PINGPONG_PIPE
+
+/* Initial buffer size */
+#define PINGPONG_SIZE 1024
+
+typedef struct {
+ void* hwpipe;
+ uint8_t* buffer;
+ size_t size;
+ size_t pos;
+ size_t count;
+ unsigned flags;
+} PingPongPipe;
+
+static void
+pingPongPipe_init0( PingPongPipe* pipe, void* hwpipe, void* svcOpaque )
+{
+ pipe->hwpipe = hwpipe;
+ pipe->size = PINGPONG_SIZE;
+ pipe->buffer = malloc(pipe->size);
+ pipe->pos = 0;
+ pipe->count = 0;
}
-static uint32_t
-pipeClient_doCommand( PipeClient* pcl,
- uint32_t command,
- uint32_t address,
- uint32_t* pSize )
+static void*
+pingPongPipe_init( void* hwpipe, void* svcOpaque, const char* args )
{
- uint32_t size = *pSize;
+ PingPongPipe* ppipe;
- D("%s: TID=%4d CHANNEL=%08x COMMAND=%d ADDRESS=%08x SIZE=%d\n",
- __FUNCTION__, pcl->tid, pcl->localId, command, address, size);
+ D("%s: hwpipe=%p", __FUNCTION__, hwpipe);
+ ANEW0(ppipe);
+ pingPongPipe_init0(ppipe, hwpipe, svcOpaque);
+ return ppipe;
+}
- /* XXX: TODO */
- switch (command) {
- case QEMUD_PIPE_CMD_CLOSE:
- /* The client is asking us to close the connection, so
- * just do that. */
- qemud_client_close(pcl->client);
- return 0;
-
- case QEMUD_PIPE_CMD_SEND: {
- /* First, try to allocate a buffer from the handler */
- void* opaque = pcl->handler;
- GoldfishPipeBuffer* buffers;
- int numBuffers = 0;
-
- /* Count the number of buffers we need, allocate them,
- * then fill them. */
- numBuffers = _countPipeBuffers(address, size);
- if (numBuffers < 0) {
- D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)",
- __FUNCTION__, address, address+size, size);
- return QEMUD_PIPE_ERROR_NOMEM;
- }
- buffers = alloca( sizeof(*buffers) * numBuffers );
- _fillPipeBuffers(address, size, buffers);
-
- D("%s: Sending %d bytes using %d buffers", __FUNCTION__,
- size, numBuffers);
-
- /* Send the data */
- if (pcl->handlerFuncs->sendBuffers(opaque, buffers, numBuffers) < 0) {
- /* When .sendBuffers() returns -1, it usually means
- * that the handler isn't ready to accept a new message. There
- * is however once exception: when the message is too large
- * and it wasn't possible to allocate the buffer.
- *
- * Differentiate between these two cases by looking at errno.
- */
- if (errno == ENOMEM)
- return QEMUD_PIPE_ERROR_NOMEM;
- else
- return QEMUD_PIPE_ERROR_AGAIN;
- }
- return 0;
- }
+static void
+pingPongPipe_close( void* opaque )
+{
+ PingPongPipe* ppipe = opaque;
- case QEMUD_PIPE_CMD_RECV: {
- void* opaque = pcl->handler;
- GoldfishPipeBuffer* buffers;
- int numBuffers, ret;
-
- /* Count the number of buffers we have */
- numBuffers = _countPipeBuffers(address, size);
- if (numBuffers < 0) {
- D("%s: Invalid guest address range 0x%x - 0x%x (%d bytes)",
- __FUNCTION__, address, address+size, size);
- return QEMUD_PIPE_ERROR_NOMEM;
- }
- buffers = alloca(sizeof(*buffers)*numBuffers);
- _fillPipeBuffers(address, size, buffers);
-
- /* Receive data */
- ret = pcl->handlerFuncs->recvBuffers(opaque, buffers, numBuffers);
- if (ret < 0) {
- if (errno == ENOMEM) {
- // XXXX: TODO *pSize = msgSize;
- return QEMUD_PIPE_ERROR_NOMEM;
- } else {
- return QEMUD_PIPE_ERROR_AGAIN;
- }
- }
- *pSize = ret;
- return 0;
+ D("%s: hwpipe=%p (pos=%d count=%d size=%d)", __FUNCTION__,
+ ppipe->hwpipe, ppipe->pos, ppipe->count, ppipe->size);
+ free(ppipe->buffer);
+ AFREE(ppipe);
+}
+
+static int
+pingPongPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
+{
+ PingPongPipe* pipe = opaque;
+ int ret = 0;
+ int count;
+ const GoldfishPipeBuffer* buff = buffers;
+ const GoldfishPipeBuffer* buffEnd = buff + numBuffers;
+
+ count = 0;
+ for ( ; buff < buffEnd; buff++ )
+ count += buff->size;
+
+ /* Do we need to grow the pingpong buffer? */
+ while (count > pipe->size - pipe->count) {
+ size_t newsize = pipe->size*2;
+ uint8_t* newbuff = realloc(pipe->buffer, newsize);
+ int wpos = pipe->pos + pipe->count;
+ if (newbuff == NULL) {
+ break;
}
+ if (wpos > pipe->size) {
+ wpos -= pipe->size;
+ memcpy(newbuff + pipe->size, newbuff, wpos);
+ }
+ pipe->buffer = newbuff;
+ pipe->size = newsize;
+ D("pingpong buffer is now %d bytes", newsize);
+ }
- case QEMUD_PIPE_CMD_WAKE_ON_SEND: {
- pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_SEND);
- return 0;
+ for ( buff = buffers; buff < buffEnd; buff++ ) {
+ int avail = pipe->size - pipe->count;
+ if (avail <= 0) {
+ if (ret == 0)
+ ret = PIPE_ERROR_AGAIN;
+ break;
+ }
+ if (avail > buff->size) {
+ avail = buff->size;
}
- case QEMUD_PIPE_CMD_WAKE_ON_RECV: {
- pcl->handlerFuncs->wakeOn(pcl->handler, QEMUD_PIPE_WAKE_ON_RECV);
- return 0;
+ int wpos = pipe->pos + pipe->count;
+ if (wpos >= pipe->size) {
+ wpos -= pipe->size;
+ }
+ if (wpos + avail <= pipe->size) {
+ memcpy(pipe->buffer + wpos, buff->data, avail);
+ } else {
+ int avail2 = pipe->size - wpos;
+ memcpy(pipe->buffer + wpos, buff->data, avail2);
+ memcpy(pipe->buffer, buff->data + avail2, avail - avail2);
}
+ pipe->count += avail;
+ ret += avail;
+ }
- default:
- return QEMUD_PIPE_ERROR_CONNRESET;
+ /* Wake up any waiting readers if we wrote something */
+ if (pipe->count > 0 && (pipe->flags & PIPE_WAKE_READ)) {
+ goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_READ);
}
+
+ return ret;
}
+static int
+pingPongPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
+{
+ PingPongPipe* pipe = opaque;
+ int ret = 0;
+
+ while (numBuffers > 0) {
+ int avail = pipe->count;
+ if (avail <= 0) {
+ if (ret == 0)
+ ret = PIPE_ERROR_AGAIN;
+ break;
+ }
+ if (avail > buffers[0].size) {
+ avail = buffers[0].size;
+ }
+
+ int rpos = pipe->pos;
+
+ if (rpos + avail <= pipe->size) {
+ memcpy(buffers[0].data, pipe->buffer + rpos, avail);
+ } else {
+ int avail2 = pipe->size - rpos;
+ memcpy(buffers[0].data, pipe->buffer + rpos, avail2);
+ memcpy(buffers[0].data + avail2, pipe->buffer, avail - avail2);
+ }
+ pipe->count -= avail;
+ pipe->pos += avail;
+ if (pipe->pos >= pipe->size) {
+ pipe->pos -= pipe->size;
+ }
+ ret += avail;
+ numBuffers--;
+ buffers++;
+ }
+
+ /* Wake up any waiting readers if we wrote something */
+ if (pipe->count < PINGPONG_SIZE && (pipe->flags & PIPE_WAKE_WRITE)) {
+ goldfish_pipe_wake(pipe->hwpipe, PIPE_WAKE_WRITE);
+ }
+ return ret;
+}
-QemudClient*
-pipeClient_connect( void* opaque, QemudService* svc, int channel )
+static unsigned
+pingPongPipe_poll( void* opaque )
{
- PipeClient* pcl;
+ PingPongPipe* pipe = opaque;
+ unsigned ret = 0;
+
+ if (pipe->count < pipe->size)
+ ret |= PIPE_WAKE_WRITE;
- ANEW0(pcl);
- pcl->pipeSvc = opaque;
- pcl->client = qemud_client_new( svc, channel, pcl,
- pipeClient_recv,
- pipeClient_close,
- NULL, /* TODO: NO SNAPSHOT SAVE */
- NULL ); /* TODO: NO SNAPHOT LOAD */
+ if (pipe->count > 0)
+ ret |= PIPE_WAKE_READ;
- /* Only for the initial connection message */
- qemud_client_set_framing(pcl->client, 1);
- return pcl->client;
+ return ret;
}
-/**********************************************************************
- **********************************************************************
+static void
+pingPongPipe_wakeOn( void* opaque, int flags )
+{
+ PingPongPipe* pipe = opaque;
+ pipe->flags |= (unsigned)flags;
+}
+
+static const GoldfishPipeFuncs pingPongPipe_funcs = {
+ pingPongPipe_init,
+ pingPongPipe_close,
+ pingPongPipe_sendBuffers,
+ pingPongPipe_recvBuffers,
+ pingPongPipe_poll,
+ pingPongPipe_wakeOn,
+};
+
+#endif /* DEBUG_PINGPONG_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
*****
- ***** GLOBAL PIPE STATE
+ ***** T H R O T T L E P I P E S
*****
*****/
-struct PipeService {
- AIntMap* threadMap; /* maps tid to ThreadState */
-};
+/* Similar to PingPongPipe, but will throttle the bandwidth to test
+ * blocking I/O.
+ */
-#if 0
-static void
-pipeService_done(PipeService* pipeSvc)
+#ifdef DEBUG_THROTTLE_PIPE
+
+typedef struct {
+ PingPongPipe pingpong;
+ double sendRate;
+ int64_t sendExpiration;
+ double recvRate;
+ int64_t recvExpiration;
+ QEMUTimer* timer;
+} ThrottlePipe;
+
+/* forward declaration */
+static void throttlePipe_timerFunc( void* opaque );
+
+static void*
+throttlePipe_init( void* hwpipe, void* svcOpaque, const char* args )
{
- /* Get rid of the tid -> ThreadState map */
- AINTMAP_FOREACH_VALUE(pipeSvc->threadMap, ts, threadState_free(ts));
- aintMap_free(pipeSvc->threadMap);
- pipeSvc->threadMap = NULL;
+ ThrottlePipe* pipe;
+
+ ANEW0(pipe);
+ pingPongPipe_init0(&pipe->pingpong, hwpipe, svcOpaque);
+ pipe->timer = qemu_new_timer(vm_clock, throttlePipe_timerFunc, pipe);
+ /* For now, limit to 500 KB/s in both directions */
+ pipe->sendRate = 1e9 / (500*1024*8);
+ pipe->recvRate = pipe->sendRate;
+ return pipe;
}
-#endif
static void
-pipeService_init(PipeService* pipeSvc)
+throttlePipe_close( void* opaque )
{
- pipeSvc->threadMap = aintMap_new();
+ ThrottlePipe* pipe = opaque;
- qemud_service_register( "fast-pipe", 0, pipeSvc,
- pipeClient_connect,
- NULL, /* TODO: NO SNAPSHOT SAVE SUPPORT */
- NULL /* TODO: NO SNAPSHOT LOAD SUPPORT */ );
+ qemu_del_timer(pipe->timer);
+ qemu_free_timer(pipe->timer);
+ pingPongPipe_close(&pipe->pingpong);
}
-static ThreadState*
-pipeService_getState(PipeService* pipeSvc, int tid)
+static void
+throttlePipe_rearm( ThrottlePipe* pipe )
{
- if (pipeSvc->threadMap == NULL)
- pipeSvc->threadMap = aintMap_new();
+ int64_t minExpiration = 0;
+
+ DD("%s: sendExpiration=%lld recvExpiration=%lld\n", __FUNCTION__, pipe->sendExpiration, pipe->recvExpiration);
+
+ if (pipe->sendExpiration) {
+ if (minExpiration == 0 || pipe->sendExpiration < minExpiration)
+ minExpiration = pipe->sendExpiration;
+ }
- return (ThreadState*) aintMap_get(pipeSvc->threadMap, tid);
+ if (pipe->recvExpiration) {
+ if (minExpiration == 0 || pipe->recvExpiration < minExpiration)
+ minExpiration = pipe->recvExpiration;
+ }
+
+ if (minExpiration != 0) {
+ DD("%s: Arming for %lld\n", __FUNCTION__, minExpiration);
+ qemu_mod_timer(pipe->timer, minExpiration);
+ }
}
static void
-pipeService_removeState(PipeService* pipeSvc, int tid)
+throttlePipe_timerFunc( void* opaque )
{
- ThreadState* ts = pipeService_getState(pipeSvc, tid);
+ ThrottlePipe* pipe = opaque;
+ int64_t now = qemu_get_clock_ns(vm_clock);
- if (ts == NULL)
- return;
+ DD("%s: TICK! now=%lld sendExpiration=%lld recvExpiration=%lld\n",
+ __FUNCTION__, now, pipe->sendExpiration, pipe->recvExpiration);
+
+ /* Timer has expired, signal wake up if needed */
+ int flags = 0;
+
+ if (pipe->sendExpiration && now > pipe->sendExpiration) {
+ flags |= PIPE_WAKE_WRITE;
+ pipe->sendExpiration = 0;
+ }
+ if (pipe->recvExpiration && now > pipe->recvExpiration) {
+ flags |= PIPE_WAKE_READ;
+ pipe->recvExpiration = 0;
+ }
+ flags &= pipe->pingpong.flags;
+ if (flags != 0) {
+ DD("%s: WAKE %d\n", __FUNCTION__, flags);
+ goldfish_pipe_wake(pipe->pingpong.hwpipe, flags);
+ }
- aintMap_del(pipeSvc->threadMap,tid);
- threadState_free(ts);
+ throttlePipe_rearm(pipe);
}
static int
-pipeService_addPipe(PipeService* pipeSvc, int tid, int localId, PipeClient* pcl)
+throttlePipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers )
{
- ThreadState* ts = pipeService_getState(pipeSvc, tid);
+ ThrottlePipe* pipe = opaque;
+ int ret;
- if (ts == NULL) {
- ts = threadState_new();
- aintMap_set(pipeSvc->threadMap, tid, ts);
+ if (pipe->sendExpiration > 0) {
+ return PIPE_ERROR_AGAIN;
}
- return threadState_addPipe(ts, localId, pcl);
+ ret = pingPongPipe_sendBuffers(&pipe->pingpong, buffers, numBuffers);
+ if (ret > 0) {
+ /* Compute next send expiration time */
+ pipe->sendExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->sendRate;
+ throttlePipe_rearm(pipe);
+ }
+ return ret;
}
-static void
-pipeService_removePipe(PipeService* pipeSvc, int tid, int localId)
+static int
+throttlePipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers )
{
- ThreadState* ts = pipeService_getState(pipeSvc, tid);
+ ThrottlePipe* pipe = opaque;
+ int ret;
- if (ts == NULL)
- return;
+ if (pipe->recvExpiration > 0) {
+ return PIPE_ERROR_AGAIN;
+ }
- threadState_delPipe(ts, localId);
+ ret = pingPongPipe_recvBuffers(&pipe->pingpong, buffers, numBuffers);
+ if (ret > 0) {
+ pipe->recvExpiration = qemu_get_clock_ns(vm_clock) + ret*pipe->recvRate;
+ throttlePipe_rearm(pipe);
+ }
+ return ret;
}
-/**********************************************************************
- **********************************************************************
+static unsigned
+throttlePipe_poll( void* opaque )
+{
+ ThrottlePipe* pipe = opaque;
+ unsigned ret = pingPongPipe_poll(&pipe->pingpong);
+
+ if (pipe->sendExpiration > 0)
+ ret &= ~PIPE_WAKE_WRITE;
+
+ if (pipe->recvExpiration > 0)
+ ret &= ~PIPE_WAKE_READ;
+
+ return ret;
+}
+
+static void
+throttlePipe_wakeOn( void* opaque, int flags )
+{
+ ThrottlePipe* pipe = opaque;
+ pingPongPipe_wakeOn(&pipe->pingpong, flags);
+}
+
+static const GoldfishPipeFuncs throttlePipe_funcs = {
+ throttlePipe_init,
+ throttlePipe_close,
+ throttlePipe_sendBuffers,
+ throttlePipe_recvBuffers,
+ throttlePipe_poll,
+ throttlePipe_wakeOn,
+};
+
+#endif /* DEBUG_THROTTLE_PIPE */
+
+/***********************************************************************
+ ***********************************************************************
*****
- ***** HARDWARE API - AS SEEN FROM hw/goldfish_trace.c
+ ***** G O L D F I S H P I P E D E V I C E
*****
*****/
-static PipeService _globalState[1];
+struct PipeDevice {
+ struct goldfish_device dev;
+
+ /* the list of all pipes */
+ Pipe* pipes;
-void init_qemud_pipes(void)
+ /* the list of signalled pipes */
+ Pipe* signaled_pipes;
+
+ /* i/o registers */
+ uint32_t address;
+ uint32_t size;
+ uint32_t status;
+ uint32_t channel;
+ uint32_t wakes;
+};
+
+
+static void
+pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
{
- pipeService_init(_globalState);
+ Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
+ Pipe* pipe = *lookup;
+ CPUState* env = cpu_single_env;
+
+ /* Check that we're referring a known pipe channel */
+ if (command != PIPE_CMD_OPEN && pipe == NULL) {
+ dev->status = PIPE_ERROR_INVAL;
+ return;
+ }
+
+ /* If the pipe is closed by the host, return an error */
+ if (pipe != NULL && pipe->closed && command != PIPE_CMD_CLOSE) {
+ dev->status = PIPE_ERROR_IO;
+ return;
+ }
+
+ switch (command) {
+ case PIPE_CMD_OPEN:
+ DD("%s: CMD_OPEN channel=0x%x", __FUNCTION__, dev->channel);
+ if (pipe != NULL) {
+ dev->status = PIPE_ERROR_INVAL;
+ break;
+ }
+ pipe = pipe_new(dev->channel, dev);
+ pipe->next = dev->pipes;
+ dev->pipes = pipe;
+ dev->status = 0;
+ break;
+
+ case PIPE_CMD_CLOSE:
+ DD("%s: CMD_CLOSE channel=0x%x", __FUNCTION__, dev->channel);
+ /* Remove from device's lists */
+ *lookup = pipe->next;
+ pipe->next = NULL;
+ pipe_list_remove_waked(&dev->signaled_pipes, pipe);
+ /* Call close callback */
+ if (pipe->funcs->close) {
+ pipe->funcs->close(pipe->opaque);
+ }
+ /* Free stuff */
+ AFREE(pipe);
+ break;
+
+ case PIPE_CMD_POLL:
+ dev->status = pipe->funcs->poll(pipe->opaque);
+ DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
+ break;
+
+ case PIPE_CMD_READ_BUFFER: {
+ /* Translate virtual address into physical one, into emulator memory. */
+ GoldfishPipeBuffer buffer;
+ uint32_t address = dev->address;
+ uint32_t page = address & TARGET_PAGE_MASK;
+ target_phys_addr_t phys = cpu_get_phys_page_debug(env, page);
+ buffer.data = qemu_get_ram_ptr(phys) + (address - page);
+ buffer.size = dev->size;
+ dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
+ DD("%s: CMD_READ_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
+ __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
+ break;
+ }
+
+ case PIPE_CMD_WRITE_BUFFER: {
+ /* Translate virtual address into physical one, into emulator memory. */
+ GoldfishPipeBuffer buffer;
+ uint32_t address = dev->address;
+ uint32_t page = address & TARGET_PAGE_MASK;
+ target_phys_addr_t phys = cpu_get_phys_page_debug(env, page);
+ buffer.data = qemu_get_ram_ptr(phys) + (address - page);
+ buffer.size = dev->size;
+ dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
+ DD("%s: CMD_WRITE_BUFFER channel=0x%x address=0x%08x size=%d > status=%d",
+ __FUNCTION__, dev->channel, dev->address, dev->size, dev->status);
+ break;
+ }
+
+ case PIPE_CMD_WAKE_ON_READ:
+ DD("%s: CMD_WAKE_ON_READ channel=0x%x", __FUNCTION__, dev->channel);
+ if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
+ pipe->wanted |= PIPE_WAKE_READ;
+ pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+ }
+ dev->status = 0;
+ break;
+
+ case PIPE_CMD_WAKE_ON_WRITE:
+ DD("%s: CMD_WAKE_ON_WRITE channel=0x%x", __FUNCTION__, dev->channel);
+ if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
+ pipe->wanted |= PIPE_WAKE_WRITE;
+ pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+ }
+ dev->status = 0;
+ break;
+
+ default:
+ D("%s: command=%d (0x%x)\n", __FUNCTION__, command, command);
+ }
}
-void
-goldfish_pipe_thread_death(int tid)
+static void pipe_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
{
- PipeService* pipeSvc = _globalState;
- pipeService_removeState(pipeSvc, tid);
+ PipeDevice *s = (PipeDevice *)opaque;
+
+ switch (offset) {
+ case PIPE_REG_COMMAND:
+ DR("%s: command=%d (0x%x)", __FUNCTION__, value, value);
+ pipeDevice_doCommand(s, value);
+ break;
+
+ case PIPE_REG_SIZE:
+ DR("%s: size=%d (0x%x)", __FUNCTION__, value, value);
+ s->size = value;
+ break;
+
+ case PIPE_REG_ADDRESS:
+ DR("%s: address=%d (0x%x)", __FUNCTION__, value, value);
+ s->address = value;
+ break;
+
+ case PIPE_REG_CHANNEL:
+ DR("%s: channel=%d (0x%x)", __FUNCTION__, value, value);
+ s->channel = value;
+ break;
+
+ default:
+ D("%s: offset=%d (0x%x) value=%d (0x%x)\n", __FUNCTION__, offset,
+ offset, value, value);
+ break;
+ }
}
-void
-goldfish_pipe_write(int tid, int offset, uint32_t value)
+/* I/O read */
+static uint32_t pipe_dev_read(void *opaque, target_phys_addr_t offset)
{
- PipeService* pipeSvc = _globalState;
- DD("%s: tid=%d offset=%d value=%d (0x%x)", __FUNCTION__, tid,
- offset, value, value);
+ PipeDevice *dev = (PipeDevice *)opaque;
- ThreadState* ts = pipeService_getState(pipeSvc, tid);
+ switch (offset) {
+ case PIPE_REG_STATUS:
+ DR("%s: REG_STATUS status=%d (0x%x)", __FUNCTION__, dev->status, dev->status);
+ return dev->status;
+
+ case PIPE_REG_CHANNEL:
+ if (dev->signaled_pipes != NULL) {
+ Pipe* pipe = dev->signaled_pipes;
+ DR("%s: channel=0x%x wanted=%d", __FUNCTION__,
+ pipe->channel, pipe->wanted);
+ dev->wakes = pipe->wanted;
+ pipe->wanted = 0;
+ dev->signaled_pipes = pipe->next_waked;
+ pipe->next_waked = NULL;
+ if (dev->signaled_pipes == NULL) {
+ goldfish_device_set_irq(&dev->dev, 0, 0);
+ DD("%s: lowering IRQ", __FUNCTION__);
+ }
+ return pipe->channel;
+ }
+ DR("%s: no signaled channels", __FUNCTION__);
+ return 0;
- if (ts == NULL) {
- D("%s: no thread state for tid=%d", __FUNCTION__, tid);
- return;
+ case PIPE_REG_WAKES:
+ DR("%s: wakes %d", __FUNCTION__, dev->wakes);
+ return dev->wakes;
+
+ default:
+ D("%s: offset=%d (0x%x)\n", __FUNCTION__, offset, offset);
}
- threadState_write(ts, offset, value);
+ return 0;
}
-uint32_t
-goldfish_pipe_read(int tid, int offset)
+static CPUReadMemoryFunc *pipe_dev_readfn[] = {
+ pipe_dev_read,
+ pipe_dev_read,
+ pipe_dev_read
+};
+
+static CPUWriteMemoryFunc *pipe_dev_writefn[] = {
+ pipe_dev_write,
+ pipe_dev_write,
+ pipe_dev_write
+};
+
+/* initialize the trace device */
+void pipe_dev_init()
{
- PipeService* pipeSvc = _globalState;
- uint32_t ret;
+ PipeDevice *s;
- DD("%s: tid=%d offset=%d", __FUNCTION__, tid, offset);
+ s = (PipeDevice *) qemu_mallocz(sizeof(*s));
- ThreadState* ts = pipeService_getState(pipeSvc, tid);
+ s->dev.name = "qemu_pipe";
+ s->dev.id = -1;
+ s->dev.base = 0; // will be allocated dynamically
+ s->dev.size = 0x2000;
+ s->dev.irq = 0;
+ s->dev.irq_count = 1;
- if (ts == NULL) {
- D("%s: no thread state for tid=%d", __FUNCTION__, tid);
- return 0;
+ goldfish_device_add(&s->dev, pipe_dev_readfn, pipe_dev_writefn, s);
+
+#if DEBUG_ZERO_PIPE
+ goldfish_pipe_add_type("zero", NULL, &zeroPipe_funcs);
+#endif
+#if DEBUG_PINGPONG_PIPE
+ goldfish_pipe_add_type("pingpong", NULL, &pingPongPipe_funcs);
+#endif
+#if DEBUG_THROTTLE_PIPE
+ goldfish_pipe_add_type("throttle", NULL, &throttlePipe_funcs);
+#endif
+}
+
+void
+goldfish_pipe_wake( void* hwpipe, unsigned flags )
+{
+ Pipe* pipe = hwpipe;
+ Pipe** lookup;
+ PipeDevice* dev = pipe->device;
+
+ DD("%s: channel=0x%x flags=%d", __FUNCTION__, pipe->channel, flags);
+
+ /* If not already there, add to the list of signaled pipes */
+ lookup = pipe_list_findp_waked(&dev->signaled_pipes, pipe);
+ if (!*lookup) {
+ pipe->next_waked = dev->signaled_pipes;
+ dev->signaled_pipes = pipe;
+ }
+ pipe->wanted |= (unsigned)flags;
+
+ /* Raise IRQ to indicate there are items on our list ! */
+ goldfish_device_set_irq(&dev->dev, 0, 1);
+ DD("%s: raising IRQ", __FUNCTION__);
+}
+
+void
+goldfish_pipe_close( void* hwpipe )
+{
+ Pipe* pipe = hwpipe;
+
+ D("%s: channel=0x%x (closed=%d)", __FUNCTION__, pipe->channel, pipe->closed);
+
+ if (!pipe->closed) {
+ pipe->closed = 1;
+ goldfish_pipe_wake( hwpipe, PIPE_WAKE_CLOSED );
}
- ret = threadState_read(ts, offset);
- DD("%s: result=%d (0x%d)", __FUNCTION__, ret, ret);
- return ret;
}
diff --git a/hw/goldfish_pipe.h b/hw/goldfish_pipe.h
index 03bc443..be5c449 100644
--- a/hw/goldfish_pipe.h
+++ b/hw/goldfish_pipe.h
@@ -13,66 +13,45 @@
#define _HW_GOLDFISH_PIPE_H
#include <stdint.h>
-#include "android/hw-qemud.h"
+#include "hw/hw.h"
-/* The following functions should called from hw/goldfish_trace.c and are
- * used to implement QEMUD 'fast-pipes' in the Android emulator.
- */
-extern void goldfish_pipe_thread_death(int tid);
-extern void goldfish_pipe_write(int tid, int offset, uint32_t value);
-extern uint32_t goldfish_pipe_read(int tid, int offset);
-
-/* The following definitions are used to define a "pipe handler" type.
- * Each pipe handler manages a given, named pipe type, and must provide
- * a few callbacks that will be used at appropriate times:
- *
- * - init ::
- * is called when a guest client has connected to a given
- * pipe. The function should return an opaque pointer that
- * will be passed as the first parameter to other callbacks.
- *
- * Note: pipeOpaque is the value that was passed to
- * goldfish_pipe_add_type() to register the pipe handler.
- *
- * - close ::
- * is called when the pipe is closed.(either from the guest, or
- * when the handler itself calls qemud_client_close() on the
- * corresponding QemudClient object passed to init()).
- *
- * - sendBuffers ::
- * is called when the guest is sending data through the pipe. This
- * callback receives a list of buffer descriptors that indicate
- * where the data is located in memory.
- *
- * Must return 0 on success, or -1 on failure, with errno of:
- *
- * ENOMEM -> indicates that the message is too large
- * EAGAIN -> indicates that the handler is not ready
- * to accept the message yet.
- *
- * - recvBuffers ::
- * Is called when the guest wants to receive data from the pipe.
- * The caller provides a list of memory buffers to put the data into.
- *
- * Must return the size of the incoming data on success, or -1
- * on error, with errno of:
- *
- * ENOMEM -> buffer too small to receive the message
- * EAGAIN -> no incoming data yet
- *
- * - wakeOn ::
- * is called to indicate that the guest wants to be waked when the
- * pipe becomes able to either receive data from the guest, or send it
- * new incoming data. It is the responsability of the pipe handler to
- * signal the corresponding events by sending a single byte containing
- * QEMUD_PIPE_WAKE_XXX bit flags through qemud_client_send() to do so.
+/* TECHNICAL NOTE:
+ *
+ * A goldfish pipe is a very fast communication channel between the guest
+ * system and the emulator program.
+ *
+ * To open a new pipe to the emulator, a guest client will do the following:
+ *
+ * fd = open("/dev/qemu_pipe", O_RDWR);
+ * char invite[64];
+ * snprintf(invite, sizeof invite, "%s", pipeName);
+ * ret = write(fd, invite, strlen(invite));
+ *
+ * if (ret < 0) {
+ * // something bad happened, see errno
+ * }
+ *
+ * now read()/write() to communicate with <pipeName> service in the
+ * emulator.
+ *
+ * This header provides the interface used by pipe services in the emulator
+ * to receive new client connection and deal with them.
+ *
+ *
+ * 1/ Call goldfish_pipe_add_type() to register a new pipe service by name.
+ * This must provide a pointer to a series of functions that will be called
+ * during normal pipe operations.
+ *
+ * 2/ When a client connects to the service, the 'init' callback will be called
+ * to create a new service-specific client identifier (which must returned
+ * by the function).
+ *
+ * 3/ Call goldfish_pipe_close() to force the closure of a given pipe.
+ *
+ * 4/ Call goldfish_pipe_signal() to signal a change of state to the pipe.
+ *
*/
-enum {
- QEMUD_PIPE_WAKE_ON_SEND = (1 << 0),
- QEMUD_PIPE_WAKE_ON_RECV = (1 << 1),
-};
-
/* Buffer descriptor for sendBuffers() and recvBuffers() callbacks */
typedef struct GoldfishPipeBuffer {
uint8_t* data;
@@ -81,18 +60,112 @@ typedef struct GoldfishPipeBuffer {
/* Pipe handler funcs */
typedef struct {
- void* (*init)( QemudClient* client, void* pipeOpaque );
- void (*close)( void* opaque );
- int (*sendBuffers)( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers );
- int (*recvBuffers)( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers );
+ /* Create new client connection, 'hwpipe' must be passed to other
+ * goldfish_pipe_xxx functions, while the returned value will be passed
+ * to other callbacks (e.g. close). 'pipeOpaque' is the value passed
+ * to goldfish_pipe_add_type() when registering a given pipe service.
+ */
+ void* (*init)( void* hwpipe, void* pipeOpaque, const char* args );
+
+ /* Called when the guest kernel has finally closed a pipe connection.
+ * This is the only place where you can release/free the client connection.
+ * You should never invoke this callback directly. Call goldfish_pipe_close()
+ * instead.
+ */
+ void (*close)( void* pipe );
+
+ /* Called when the guest is write()-ing to the pipe. Should return the
+ * number of bytes transfered, 0 for EOF status, or a negative error
+ * value otherwise, including PIPE_ERROR_AGAIN to indicate that the
+ * emulator is not ready to receive data yet.
+ */
+ int (*sendBuffers)( void* pipe, const GoldfishPipeBuffer* buffers, int numBuffers );
+
+ /* Same as sendBuffers when the guest is read()-ing from the pipe. */
+ int (*recvBuffers)( void* pipe, GoldfishPipeBuffer* buffers, int numBuffers );
+
+ /* Called when guest wants to poll the read/write status for the pipe.
+ * Should return a combination of PIPE_WAKE_XXX flags.
+ */
+ unsigned (*poll)( void* pipe );
+
+ /* Called to signal that the guest wants to be woken when the set of
+ * PIPE_WAKE_XXX bit-flags in 'flags' occur. When the condition occurs,
+ * then the pipe implementation shall call goldfish_pipe_wake().
+ */
void (*wakeOn)( void* opaque, int flags );
-} QemudPipeHandlerFuncs;
+} GoldfishPipeFuncs;
/* Register a new pipe handler type. 'pipeOpaque' is passed directly
* to 'init() when a new pipe is connected to.
*/
-extern void goldfish_pipe_add_type(const char* pipeName,
- void* pipeOpaque,
- const QemudPipeHandlerFuncs* pipeFuncs );
+extern void goldfish_pipe_add_type(const char* pipeName,
+ void* pipeOpaque,
+ const GoldfishPipeFuncs* pipeFuncs );
+
+/* This tells the guest system that we want to close the pipe and that
+ * further attempts to read or write to it will fail. This will not
+ * necessarily call the 'close' callback immediately though.
+ *
+ * This will also wake-up any blocked guest threads waiting for i/o.
+ */
+extern void goldfish_pipe_close( void* hwpipe );
+
+/* Signal that the pipe can be woken up. 'flags' must be a combination of
+ * PIPE_WAKE_READ and PIPE_WAKE_WRITE.
+ */
+extern void goldfish_pipe_wake( void* hwpipe, unsigned flags );
+
+/* The following definitions must match those under:
+ *
+ * $KERNEL/drivers/misc/qemupipe/qemu_pipe.c
+ *
+ * Where $KERNEL points to the android-goldfish-2.6.xx branch on:
+ *
+ * android.git.kernel.org/kernel/qemu.git.
+ */
+
+/* pipe device registers */
+#define PIPE_REG_COMMAND 0x00 /* write: value = command */
+#define PIPE_REG_STATUS 0x04 /* read */
+#define PIPE_REG_CHANNEL 0x08 /* read/write: channel id */
+#define PIPE_REG_SIZE 0x0c /* read/write: buffer size */
+#define PIPE_REG_ADDRESS 0x10 /* write: physical address */
+#define PIPE_REG_WAKES 0x14 /* read: wake flags */
+
+/* list of commands for PIPE_REG_COMMAND */
+#define PIPE_CMD_OPEN 1 /* open new channel */
+#define PIPE_CMD_CLOSE 2 /* close channel (from guest) */
+#define PIPE_CMD_POLL 3 /* poll read/write status */
+
+/* List of bitflags returned in status of CMD_POLL command */
+#define PIPE_POLL_IN (1 << 0)
+#define PIPE_POLL_OUT (1 << 1)
+#define PIPE_POLL_HUP (1 << 2)
+
+/* The following commands are related to write operations */
+#define PIPE_CMD_WRITE_BUFFER 4 /* send a user buffer to the emulator */
+#define PIPE_CMD_WAKE_ON_WRITE 5 /* tell the emulator to wake us when writing is possible */
+
+/* The following commands are related to read operations, they must be
+ * listed in the same order than the corresponding write ones, since we
+ * will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset
+ * in qemu_pipe_read_write() below.
+ */
+#define PIPE_CMD_READ_BUFFER 6 /* receive a page-contained buffer from the emulator */
+#define PIPE_CMD_WAKE_ON_READ 7 /* tell the emulator to wake us when reading is possible */
+
+/* Possible status values used to signal errors - see qemu_pipe_error_convert */
+#define PIPE_ERROR_INVAL -1
+#define PIPE_ERROR_AGAIN -2
+#define PIPE_ERROR_NOMEM -3
+#define PIPE_ERROR_IO -4
+
+/* Bit-flags used to signal events from the emulator */
+#define PIPE_WAKE_CLOSED (1 << 0) /* emulator closed pipe */
+#define PIPE_WAKE_READ (1 << 1) /* pipe can now be read from */
+#define PIPE_WAKE_WRITE (1 << 2) /* pipe can now be written to */
+
+void pipe_dev_init(void);
#endif /* _HW_GOLDFISH_PIPE_H */
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
index ee51a8b..02f9b8d 100644
--- a/hw/goldfish_trace.c
+++ b/hw/goldfish_trace.c
@@ -15,7 +15,6 @@
*/
#include "qemu_file.h"
#include "goldfish_trace.h"
-#include "goldfish_pipe.h"
#include "sysemu.h"
#include "trace.h"
#ifdef CONFIG_MEMCHECK
@@ -177,7 +176,6 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va
memcheck_exit(value);
}
#endif // CONFIG_MEMCHECK
- goldfish_pipe_thread_death((int)tid);
break;
case TRACE_DEV_REG_NAME: // record thread name
vstrcpy(value, exec_path, CLIENT_PAGE_SIZE);
@@ -350,13 +348,6 @@ static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t va
break;
#endif // CONFIG_MEMCHECK
- case TRACE_DEV_PIPE_COMMAND:
- case TRACE_DEV_PIPE_ADDRESS:
- case TRACE_DEV_PIPE_SIZE:
- case TRACE_DEV_PIPE_CHANNEL:
- goldfish_pipe_write(tid, ((offset >> 2) - TRACE_DEV_PIPE_BASE), value);
- break;
-
default:
if (offset < 4096) {
cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
@@ -379,12 +370,6 @@ static uint32_t trace_dev_read(void *opaque, target_phys_addr_t offset)
case TRACE_DEV_REG_ENABLE: // tracing enable
return tracing;
- case TRACE_DEV_PIPE_COMMAND:
- case TRACE_DEV_PIPE_ADDRESS:
- case TRACE_DEV_PIPE_SIZE:
- case TRACE_DEV_PIPE_CHANNEL:
- return goldfish_pipe_read(tid, (offset >> 2) - TRACE_DEV_PIPE_BASE);
-
default:
if (offset < 4096) {
cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);