diff options
author | David 'Digit' Turner <digit@android.com> | 2011-04-26 18:09:17 +0200 |
---|---|---|
committer | David 'Digit' Turner <digit@android.com> | 2011-04-28 16:41:50 +0200 |
commit | d4d688e926097faba7fd3717d1c0d3f296bdb526 (patch) | |
tree | b6e89c99abae42e5bcef26afaa2952b5a870895b | |
parent | a65e41529e4e3900372d54859f8c559cf79d953c (diff) | |
download | external_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).
-rw-r--r-- | Makefile.common | 2 | ||||
-rw-r--r-- | android/hw-qemud-pipe-net.c | 788 | ||||
-rw-r--r-- | android/pipe-net.c | 520 | ||||
-rw-r--r-- | android/pipe-net.h (renamed from android/hw-qemud-pipe.h) | 10 | ||||
-rw-r--r-- | hw/android_arm.c | 4 | ||||
-rw-r--r-- | hw/goldfish_pipe.c | 1388 | ||||
-rw-r--r-- | hw/goldfish_pipe.h | 203 | ||||
-rw-r--r-- | hw/goldfish_trace.c | 15 | ||||
-rw-r--r-- | vl-android.c | 5 |
9 files changed, 1525 insertions, 1410 deletions
diff --git a/Makefile.common b/Makefile.common index fb3118e..4bdc8ec 100644 --- a/Makefile.common +++ b/Makefile.common @@ -375,8 +375,8 @@ CORE_MISC_SOURCES = \ android/hw-control.c \ android/hw-sensors.c \ android/hw-qemud.c \ - android/hw-qemud-pipe-net.c \ android/looper-qemu.c \ + android/pipe-net.c \ android/qemu-setup.c \ android/snapshot.c \ android/utils/timezone.c \ diff --git a/android/hw-qemud-pipe-net.c b/android/hw-qemud-pipe-net.c deleted file mode 100644 index 874bc69..0000000 --- a/android/hw-qemud-pipe-net.c +++ /dev/null @@ -1,788 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "sockets.h" -#include "android/utils/assert.h" -#include "android/utils/panic.h" -#include "android/utils/system.h" -#include "android/async-utils.h" -#include "android/looper.h" -#include "android/hw-qemud-pipe.h" -#include "hw/goldfish_pipe.h" - -/* Implement the OpenGL fast-pipe */ - -/* Set to 1 or 2 for debug traces */ -#define DEBUG 0 - -#if DEBUG >= 1 -# define D(...) printf(__VA_ARGS__), printf("\n") -#else -# define D(...) ((void)0) -#endif - -#if DEBUG >= 2 -# define DD(...) printf(__VA_ARGS__), printf("\n") -# define DDASSERT(cond) _ANDROID_ASSERT(cond, "Assertion failure: ", #cond) -# define DDASSERT_INT_OP(cond,val,op) _ANDROID_ASSERT_INT_OP(cond,val,op) -#else -# define DD(...) ((void)0) -# define DDASSERT(cond) ((void)0) -# define DDASSERT_INT_OP(cond,val,op) ((void)0) -#endif - -#define DDASSERT_INT_LT(cond,val) DDASSERT_INT_OP(cond,val,<) -#define DDASSERT_INT_LTE(cond,val) DDASSERT_INT_OP(cond,val,<=) -#define DDASSERT_INT_GT(cond,val) DDASSERT_INT_OP(cond,val,>) -#define DDASSERT_INT_GTE(cond,val) DDASSERT_INT_OP(cond,val,>=) -#define DDASSERT_INT_EQ(cond,val) DDASSERT_INT_OP(cond,val,==) -#define DDASSERT_INT_NEQ(cond,val) DDASSERT_INT_OP(cond,val,!=) - - -/* Forward declarations */ - -typedef struct NetPipeInit { - Looper* looper; - SockAddress serverAddress[1]; -} NetPipeInit; - -/********************************************************************** - ********************************************************************** - ***** - ***** P I P E M E S S A G E S - ***** - *****/ - -typedef struct PipeMsg { - struct PipeMsg* next; - size_t size; - uint8_t data[1]; -} PipeMsg; - -static PipeMsg* -pipeMsg_alloc( size_t size ) -{ - PipeMsg* msg = android_alloc(sizeof(*msg) + size); - - msg->next = NULL; - msg->size = size; - return msg; -} - -static void -pipeMsg_free( PipeMsg* msg ) -{ - AFREE(msg); -} - -/********************************************************************** - ********************************************************************** - ***** - ***** M E S S A G E L I S T - ***** - *****/ - -typedef struct { - PipeMsg* firstMsg; /* first message in list */ - PipeMsg* lastMsg; /* last message in list */ - size_t firstBytes; /* bytes in firstMsg that were already sent */ - size_t lastBytes; /* bytes in lastMsg that were already received */ - size_t totalBytes; /* total bytes in list */ -} MsgList; - -/* Default receiver buffer size to accept incoming data */ -#define DEFAULT_RECEIVER_SIZE 8180 - -/* Initialize a message list - appropriate for sending them out */ -static void -msgList_initSend( MsgList* list ) -{ - list->firstMsg = NULL; - list->lastMsg = NULL; - list->firstBytes = 0; - list->lastBytes = 0; - list->totalBytes = 0; -} - -/* Initialize a message list for receiving data */ -static void -msgList_initReceive( MsgList* list ) -{ - msgList_initSend(list); - list->firstMsg = list->lastMsg = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE ); -} - -/* Finalize a message list */ -static void -msgList_done( MsgList* list ) -{ - PipeMsg* msg; - - while ((msg = list->firstMsg) != NULL) { - list->firstMsg = msg->next; - pipeMsg_free(msg); - } - list->lastMsg = NULL; - - list->firstBytes = 0; - list->lastBytes = 0; - list->totalBytes = 0; -} - -static int -msgList_hasData( MsgList* list ) -{ - return list->totalBytes > 0; -} - -/* Append a list of buffers to a message list. - * - * This is a very simple implementation that simply mallocs a single - * new message containing all of the buffer's data, and append it to - * our link list. This also makes the implementation of msgList_send() - * quite simple, since there is no need to deal with the 'lastBytes' - * pointer (it is always assumed to be 'lastMsg->size'). - */ -static int -msgList_sendBuffers( MsgList* list, - const GoldfishPipeBuffer* buffers, - int numBuffers ) -{ - const GoldfishPipeBuffer* buff = buffers; - const GoldfishPipeBuffer* buffEnd = buff + numBuffers; - PipeMsg* msg; - size_t msgSize = 0; - size_t pos; - - /* Count the total number of bytes */ - for ( ; buff < buffEnd; buff++ ) { - msgSize += buff[0].size; - } - - /* Allocate a new message */ - msg = pipeMsg_alloc(msgSize); - if (msg == NULL) { - errno = ENOMEM; - return -1; - } - - /* Copy data from buffers to message */ - for ( pos = 0, buff = buffers; buff < buffEnd; buff++ ) { - memcpy(msg->data + pos, buff->data, buff->size); - pos += buff->size; - } - - /* Append message to current list */ - if (list->lastMsg != NULL) { - list->lastMsg->next = msg; - } else { - list->firstMsg = msg; - list->firstBytes = 0; - } - list->lastMsg = msg; - - list->totalBytes += msgSize; - - /* We are done */ - return 0; -} - -/* Try to send outgoing messages in the list through non-blocking socket 'fd'. - * Return 0 on success, and -1 on failure, where errno will be: - * - * ECONNRESET - connection reset by peer - * - * Note that 0 will be returned if socket_send() returns EAGAIN/EWOULDBLOCK. - */ -static int -msgList_send( MsgList* list, int fd ) -{ - int ret = 0; - - for (;;) { - PipeMsg* msg = list->firstMsg; - size_t sentBytes = list->firstBytes; - size_t availBytes; - - if (msg == NULL) { - /* We sent everything */ - return 0; - } - DDASSERT(sentBytes < msg->size); - availBytes = msg->size - sentBytes; - - ret = socket_send(fd, msg->data + sentBytes, availBytes); - if (ret <= 0) { - goto ERROR; - } - - list->totalBytes -= ret; - list->firstBytes += ret; - - if (list->firstBytes < msg->size) { - continue; - } - - /* We sent the full first packet - remove it from the head */ - list->firstBytes = 0; - list->firstMsg = msg->next; - if (list->firstMsg == NULL) { - list->lastMsg = NULL; - } - pipeMsg_free(msg); - } - -ERROR: - if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ret = 0; /* clear error - this is normal */ - } else { - DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str); - errno = ECONNRESET; - } - } else { -#if DEBUG >= 2 - int err = socket_get_error(fd); - DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__, - err, strerror(err)); -#endif - errno = ECONNRESET; - ret = -1; - } - return ret; -} - - -/* Try to receive data into caller-provided buffers, and return the total - * size of bytes that were read. Returns -1 on error, with errno: - * - * ECONNRESET: Connection reset by peer - * EAGAIN: No incoming data, wait for it to arrive. - */ -static int -msgList_recvBuffers( MsgList* list, - GoldfishPipeBuffer* buffers, - int numBuffers ) -{ - GoldfishPipeBuffer* buff = buffers; - GoldfishPipeBuffer* buffEnd = buff + numBuffers; - size_t buffStart = 0; - PipeMsg* msg = list->firstMsg; - size_t msgStart = list->firstBytes; - size_t totalSize = 0; - - DDASSERT(msg != NULL); - - D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - /* If there is no incoming data, return EAGAIN */ - if (list->totalBytes == 0) { - errno = EAGAIN; - return -1; - } - - /* Now try to transfer as much from the list of incoming messages - * into the buffers. - */ - while (msg != NULL && buff < buffEnd) { - DDASSERT(msgStart < msg->size); - DDASSERT(buffStart < buff->size); - - /* Copy data from current message into next buffer. - * For a given message, first determine the start and end - * of available data. Then try to see how much of these - * we can copy to the current buffer. - */ - size_t msgEnd = msg->size; - - if (msg == list->lastMsg) { - msgEnd = list->lastBytes; - } - - size_t msgAvail = msgEnd - msgStart; - size_t buffAvail = buff->size - buffStart; - - if (msgAvail > buffAvail) { - msgAvail = buffAvail; - } - - DDASSERT(msgAvail > 0); - - D("%s: transfer %d bytes (msgStart=%d msgSize=%d buffStart=%d buffSize=%d)", - __FUNCTION__, msgAvail, msgStart, msg->size, buffStart, buff->size); - memcpy(buff->data + buffStart, msg->data + msgStart, msgAvail); - - /* Advance cursors */ - msgStart += msgAvail; - buffStart += msgAvail; - totalSize += msgAvail; - - /* Did we fill up the current buffer? */ - if (buffStart >= buff->size) { - buffStart = 0; - buff++; - } - - /* Did we empty the current message? */ - if (msgStart >= msgEnd) { - msgStart = 0; - /* If this is the last message, reset the 'first' and 'last' - * pointers to reuse it for the next recv(). */ - if (msg == list->lastMsg) { - list->lastBytes = 0; - msg = NULL; - } else { - /* Otherwise, delete the message, and jump to the next one */ - list->firstMsg = msg->next; - pipeMsg_free(msg); - msg = list->firstMsg; - } - } - - } - list->firstBytes = msgStart; - list->totalBytes -= totalSize; - - D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - return (int)totalSize; -} - - -/* Try to receive data from non-blocking socket 'fd'. - * Return 0 on success, or -1 on error, where errno can be: - * - * ECONNRESET - connection reset by peer - * ENOMEM - full message list, no room to receive more data - */ -static int -msgList_recv( MsgList* list, int fd ) -{ - int ret = 0; - - D("%s: ENTER list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - for (;;) { - PipeMsg* last = list->lastMsg; - size_t lastBytes = list->lastBytes; - size_t availBytes; - - /* Compute how many bytes we can receive in the last buffer*/ - DDASSERT(last != NULL); - DDASSERT(last->size > 0); - DDASSERT(lastBytes < last->size); - - availBytes = last->size - lastBytes; - - /* Try to receive the data, act on errors */ - ret = socket_recv(fd, last->data + lastBytes, availBytes); - if (ret <= 0) { - goto ERROR; - } - - /* Acknowledge received data */ - list->lastBytes += ret; - list->totalBytes += ret; - - if (list->lastBytes < last->size) { - continue; - } - - /* We filled-up the last message buffer, allocate a new one */ - last = pipeMsg_alloc( DEFAULT_RECEIVER_SIZE ); - list->lastMsg->next = last; - list->lastMsg = last; - list->lastBytes = 0; - } - -ERROR: - if (ret < 0) { /* EAGAIN/EWOULDBLOCK or disconnection */ - if (errno == EAGAIN || errno == EWOULDBLOCK) { - ret = 0; /* clear error - this is normal */ - } else { - DD("%s: socket_send() returned %d: %s\n", __FUNCTION__, errno, errno_str); - errno = ECONNRESET; - } - } else /* ret == 0 */ { -#if DEBUG >= 2 - int err = socket_get_error(fd); - DD("%s: socket_send() returned 0 (error=%d: %s)", __FUNCTION__, - err, strerror(err)); -#endif - errno = ECONNRESET; - ret = -1; - } - D("%s: EXIT list.firstBytes=%d list.lastBytes=%d list.totalBytes=%d list.firstSize=%d list.lastSize=%d list.firstEqualLast=%d", - __FUNCTION__, list->firstBytes, list->lastBytes, list->totalBytes, - list->firstMsg->size, list->lastMsg->size, list->firstMsg == list->lastMsg); - - return ret; -} - - - -/********************************************************************** - ********************************************************************** - ***** - ***** P I P E H A N D L E R S - ***** - *****/ - -/* Technical Note: - * - * Each NetPipe object is connected to the following: - * - * - a remote rendering process through a normal TCP socket. - * - a Goldfish pipe (see hw/goldfish_pipe.h) to exchange messages with the guest. - * - a Qemud client (see android/hw-qemud.h) to signal state changes to the guest. - * - * REMOTE <---socket---> PIPE <------> GOLDFISH PIPE - * PROCESS <--+ - * | - * +---> QEMUD CHANNEL (android/hw-qemud.h) - * - */ -enum { - STATE_INIT, - STATE_CONNECTING, - STATE_CONNECTED, - STATE_CLOSING_GUEST, - STATE_CLOSING_SOCKET -}; - -#define DEFAULT_INCOMING_SIZE 4000 - -#define MAX_IN_BUFFERS 4 - -typedef struct { - QemudClient* client; - int state; - int wakeWanted; - - MsgList outList[1]; - MsgList inList[1]; - - LoopIo io[1]; - AsyncConnector connector[1]; - - GoldfishPipeBuffer outBuffer[1]; - GoldfishPipeBuffer inBuffers[MAX_IN_BUFFERS]; - int inBufferCount; - -} NetPipe; - -static void -netPipe_free( NetPipe* pipe ) -{ - int fd; - - /* Removing any pending incoming packet */ - msgList_done(pipe->outList); - msgList_done(pipe->inList); - - /* Close the socket */ - fd = pipe->io->fd; - loopIo_done(pipe->io); - socket_close(fd); - - /* Release the pipe object */ - AFREE(pipe); -} - - -static void -netPipe_resetState( NetPipe* pipe ) -{ - /* If there is a pending outgoing packet, open the valve */ - if (msgList_hasData(pipe->outList)) { - loopIo_wantWrite(pipe->io); - } else { - loopIo_dontWantWrite(pipe->io); - } - - /* Accept incoming data if we are not closing, and our input list isn't full */ - if (pipe->state == STATE_CONNECTED) { - loopIo_wantRead(pipe->io); - } else { - loopIo_dontWantRead(pipe->io); - } -} - - -/* This function is only called when the socket is disconnected. - * See netPipe_closeFromGuest() for the case when the guest requires - * the disconnection. */ -static void -netPipe_closeFromSocket( void* opaque ) -{ - NetPipe* pipe = opaque; - - /* If the guest already ordered the pipe to be closed, delete immediately */ - if (pipe->state == STATE_CLOSING_GUEST) { - netPipe_free(pipe); - return; - } - - /* Force the closure of the QEMUD channel - if a guest is blocked - * waiting for a wake signal, it will receive an error. */ - if (pipe->client != NULL) { - qemud_client_close(pipe->client); - pipe->client = NULL; - } - - /* Remove any outgoing packets - they won't go anywhere */ - msgList_done(pipe->outList); - - pipe->state = STATE_CLOSING_SOCKET; - netPipe_resetState(pipe); -} - - -/* This is the function that gets called each time there is an asynchronous - * event on the network pipe. - */ -static void -netPipe_io_func( void* opaque, int fd, unsigned events ) -{ - NetPipe* pipe = opaque; - int wakeFlags = 0; - - /* Run the connector if we are in the CONNECTING state */ - /* TODO: Add some sort of time-out, to deal with the case */ - /* where the renderer process is wedged. */ - if (pipe->state == STATE_CONNECTING) { - AsyncStatus status = asyncConnector_run(pipe->connector); - if (status == ASYNC_NEED_MORE) { - return; - } - else if (status == ASYNC_ERROR) { - /* Could not connect, tell our client by closing the channel. */ - - netPipe_closeFromSocket(pipe); - return; - } - pipe->state = STATE_CONNECTED; - netPipe_resetState(pipe); - return; - } - - /* Otherwise, accept incoming data */ - if ((events & LOOP_IO_READ) != 0) { - int ret; - - if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_RECV) != 0) { - wakeFlags |= QEMUD_PIPE_WAKE_ON_RECV; - } - - ret = msgList_recv(pipe->inList, fd); - if (ret < 0) { - wakeFlags &= ~QEMUD_PIPE_WAKE_ON_RECV; - - if (errno == ENOMEM) { /* shouldn't happen */ - DD("%s: msgList_recv() return ENOMEM!?\n", __FUNCTION__); - } else { - /* errno == ECONNRESET */ - DD("%s: msgList_recv() error, closing pipe\n", __FUNCTION__); - netPipe_closeFromSocket(pipe); - return; - } - } - } - - if ((events & LOOP_IO_WRITE) != 0) { - int ret; - - DDASSERT(msgList_hasData(pipe->outList)); - - ret = msgList_send(pipe->outList, fd); - if (ret < 0) { - DD("%s: msgList_send() error, closing pipe\n", __FUNCTION__); - netPipe_closeFromSocket(pipe); - return; - } - - if ((pipe->wakeWanted & QEMUD_PIPE_WAKE_ON_SEND) != 0) { - wakeFlags |= QEMUD_PIPE_WAKE_ON_SEND; - } - } - - /* Send wake signal to the guest if needed */ - if (wakeFlags != 0) { - uint8_t byte = (uint8_t) wakeFlags; - DD("%s: Sending wake flags %d (wanted=%d)", __FUNCTION__, byte, pipe->wakeWanted); - qemud_client_send(pipe->client, &byte, 1); - pipe->wakeWanted &= ~wakeFlags; - } - - /* Reset state */ - netPipe_resetState(pipe); -} - - -void* -netPipe_init( QemudClient* qcl, void* pipeOpaque ) -{ - NetPipe* pipe; - NetPipeInit* pipeSvc = pipeOpaque; - - ANEW0(pipe); - - pipe->client = qcl; - pipe->state = STATE_INIT; - - msgList_initSend(pipe->outList); - msgList_initReceive(pipe->inList); - -#define DEFAULT_OPENGLES_PORT 22468 - - { - AsyncStatus status; - - int fd = socket_create_inet( SOCKET_STREAM ); - if (fd < 0) { - netPipe_free(pipe); - return NULL; - } - - loopIo_init(pipe->io, pipeSvc->looper, fd, netPipe_io_func, pipe); - asyncConnector_init(pipe->connector, pipeSvc->serverAddress, pipe->io); - pipe->state = STATE_CONNECTING; - - status = asyncConnector_run(pipe->connector); - if (status == ASYNC_ERROR) { - D("%s: Could not create to renderer process: %s", - __FUNCTION__, errno_str); - netPipe_free(pipe); - return NULL; - } - if (status == ASYNC_COMPLETE) { - pipe->state = STATE_CONNECTED; - netPipe_resetState(pipe); - } - } - - return pipe; -} - -/* Called when the guest wants to close the channel. This is different - * from netPipe_closeFromSocket() which is called when the socket is - * disconnected. */ -static void -netPipe_closeFromGuest( void* opaque ) -{ - NetPipe* pipe = opaque; - - /* The qemud client is gone when we reach this code */ - pipe->client = NULL; - - /* Remove input messages */ - msgList_done(pipe->inList); - - /* If the socket is already closed, or if there are no - * outgoing messages, delete immediately */ - if (pipe->state == STATE_CLOSING_SOCKET || - !msgList_hasData(pipe->outList)) { - netPipe_free(pipe); - return; - } - - /* Otherwise, mark our pipe as closing, and wait until everything is - * sent before deleting the object. */ - pipe->state = STATE_CLOSING_GUEST; - netPipe_resetState(pipe); -} - - -static int -netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) -{ - NetPipe* pipe = opaque; - int ret; - - ret = msgList_sendBuffers(pipe->outList, buffers, numBuffers); - netPipe_resetState(pipe); - return ret; -} - -static int -netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) -{ - NetPipe* pipe = opaque; - int ret; - - ret = msgList_recvBuffers(pipe->inList, buffers, numBuffers); - netPipe_resetState(pipe); - return ret; -} - -static void -netPipe_wakeOn( void* opaque, int flags ) -{ - NetPipe* pipe = opaque; - - pipe->wakeWanted |= flags; -} - -/********************************************************************** - ********************************************************************** - ***** - ***** N E T W O R K P I P E M E S S A G E S - ***** - *****/ - -static const QemudPipeHandlerFuncs net_pipe_handler_funcs = { - netPipe_init, - netPipe_closeFromGuest, - netPipe_sendBuffers, - netPipe_recvBuffers, - netPipe_wakeOn, -}; - - -static NetPipeInit _netPipeService[1]; - -/********************************************************************** - ********************************************************************** - ***** - ***** O P E N G L E S P I P E S E R V I C E - ***** - *****/ - -void -android_hw_opengles_init(void) -{ - NetPipeInit* svc = _netPipeService; - int ret; - - DD("%s: Registering service\n", __FUNCTION__); - - svc->looper = looper_newCore(); - - ret = sock_address_init_resolve(svc->serverAddress, - "127.0.0.1", - DEFAULT_OPENGLES_PORT, - 0); - if (ret < 0) { - APANIC("Could not resolve renderer process address!"); - } - - goldfish_pipe_add_type( "opengles", svc, &net_pipe_handler_funcs ); -} diff --git a/android/pipe-net.c b/android/pipe-net.c new file mode 100644 index 0000000..d83d8b1 --- /dev/null +++ b/android/pipe-net.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* This file implements the 'tcp:' goldfish pipe type which allows + * guest clients to directly connect to a TCP port through /dev/qemu_pipe. + */ + +#include "sockets.h" +#include "android/utils/assert.h" +#include "android/utils/panic.h" +#include "android/utils/system.h" +#include "android/async-utils.h" +#include "android/looper.h" +#include "hw/goldfish_pipe.h" + +/* Implement the OpenGL fast-pipe */ + +/* Set to 1 or 2 for debug traces */ +#define DEBUG 1 + +#if DEBUG >= 1 +# define D(...) printf(__VA_ARGS__), printf("\n") +#else +# define D(...) ((void)0) +#endif + +#if DEBUG >= 2 +# define DD(...) printf(__VA_ARGS__), printf("\n") +# define DDASSERT(cond) _ANDROID_ASSERT(cond, "Assertion failure: ", #cond) +# define DDASSERT_INT_OP(cond,val,op) _ANDROID_ASSERT_INT_OP(cond,val,op) +#else +# define DD(...) ((void)0) +# define DDASSERT(cond) ((void)0) +# define DDASSERT_INT_OP(cond,val,op) ((void)0) +#endif + +#define DDASSERT_INT_LT(cond,val) DDASSERT_INT_OP(cond,val,<) +#define DDASSERT_INT_LTE(cond,val) DDASSERT_INT_OP(cond,val,<=) +#define DDASSERT_INT_GT(cond,val) DDASSERT_INT_OP(cond,val,>) +#define DDASSERT_INT_GTE(cond,val) DDASSERT_INT_OP(cond,val,>=) +#define DDASSERT_INT_EQ(cond,val) DDASSERT_INT_OP(cond,val,==) +#define DDASSERT_INT_NEQ(cond,val) DDASSERT_INT_OP(cond,val,!=) + +enum { + STATE_INIT, + STATE_CONNECTING, + STATE_CONNECTED, + STATE_CLOSING_GUEST, + STATE_CLOSING_SOCKET +}; + +typedef struct { + void* hwpipe; + int state; + int wakeWanted; + LoopIo io[1]; + AsyncConnector connector[1]; + +} NetPipe; + +static void +netPipe_free( NetPipe* pipe ) +{ + int fd; + + /* Close the socket */ + fd = pipe->io->fd; + loopIo_done(pipe->io); + socket_close(fd); + + /* Release the pipe object */ + AFREE(pipe); +} + + +static void +netPipe_resetState( NetPipe* pipe ) +{ + if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { + loopIo_wantWrite(pipe->io); + } else { + loopIo_dontWantWrite(pipe->io); + } + + if (pipe->state == STATE_CONNECTED && (pipe->wakeWanted & PIPE_WAKE_READ) != 0) { + loopIo_wantRead(pipe->io); + } else { + loopIo_dontWantRead(pipe->io); + } +} + + +/* This function is only called when the socket is disconnected. + * See netPipe_closeFromGuest() for the case when the guest requires + * the disconnection. */ +static void +netPipe_closeFromSocket( void* opaque ) +{ + NetPipe* pipe = opaque; + + D("%s", __FUNCTION__); + + /* If the guest already ordered the pipe to be closed, delete immediately */ + if (pipe->state == STATE_CLOSING_GUEST) { + netPipe_free(pipe); + return; + } + + /* Force the closure of the QEMUD channel - if a guest is blocked + * waiting for a wake signal, it will receive an error. */ + if (pipe->hwpipe != NULL) { + goldfish_pipe_close(pipe->hwpipe); + pipe->hwpipe = NULL; + } + + pipe->state = STATE_CLOSING_SOCKET; + netPipe_resetState(pipe); +} + + +/* This is the function that gets called each time there is an asynchronous + * event on the network pipe. + */ +static void +netPipe_io_func( void* opaque, int fd, unsigned events ) +{ + NetPipe* pipe = opaque; + int wakeFlags = 0; + + /* Run the connector if we are in the CONNECTING state */ + /* TODO: Add some sort of time-out, to deal with the case */ + /* when the server is wedged. */ + if (pipe->state == STATE_CONNECTING) { + AsyncStatus status = asyncConnector_run(pipe->connector); + if (status == ASYNC_NEED_MORE) { + return; + } + else if (status == ASYNC_ERROR) { + /* Could not connect, tell our client by closing the channel. */ + + netPipe_closeFromSocket(pipe); + return; + } + pipe->state = STATE_CONNECTED; + netPipe_resetState(pipe); + return; + } + + /* Otherwise, accept incoming data */ + if ((events & LOOP_IO_READ) != 0) { + if ((pipe->wakeWanted & PIPE_WAKE_READ) != 0) { + wakeFlags |= PIPE_WAKE_READ; + } + } + + if ((events & LOOP_IO_WRITE) != 0) { + if ((pipe->wakeWanted & PIPE_WAKE_WRITE) != 0) { + wakeFlags |= PIPE_WAKE_WRITE; + } + } + + /* Send wake signal to the guest if needed */ + if (wakeFlags != 0) { + goldfish_pipe_wake(pipe->hwpipe, wakeFlags); + pipe->wakeWanted &= ~wakeFlags; + } + + /* Reset state */ + netPipe_resetState(pipe); +} + + +void* +netPipe_initFromAddress( void* hwpipe, const SockAddress* address, Looper* looper ) +{ + NetPipe* pipe; + + ANEW0(pipe); + + pipe->hwpipe = hwpipe; + pipe->state = STATE_INIT; + + { + AsyncStatus status; + + int fd = socket_create( sock_address_get_family(address), SOCKET_STREAM ); + if (fd < 0) { + D("%s: Could create socket from address family!", __FUNCTION__); + netPipe_free(pipe); + return NULL; + } + + loopIo_init(pipe->io, looper, fd, netPipe_io_func, pipe); + asyncConnector_init(pipe->connector, address, pipe->io); + pipe->state = STATE_CONNECTING; + + status = asyncConnector_run(pipe->connector); + if (status == ASYNC_ERROR) { + D("%s: Could not connect to socket: %s", + __FUNCTION__, errno_str); + netPipe_free(pipe); + return NULL; + } + if (status == ASYNC_COMPLETE) { + pipe->state = STATE_CONNECTED; + netPipe_resetState(pipe); + } + } + + return pipe; +} + + +/* Called when the guest wants to close the channel. This is different + * from netPipe_closeFromSocket() which is called when the socket is + * disconnected. */ +static void +netPipe_closeFromGuest( void* opaque ) +{ + NetPipe* pipe = opaque; + netPipe_free(pipe); +} + + +static int +netPipe_sendBuffers( void* opaque, const GoldfishPipeBuffer* buffers, int numBuffers ) +{ + NetPipe* pipe = opaque; + int count = 0; + int ret = 0; + int buffStart = 0; + const GoldfishPipeBuffer* buff = buffers; + const GoldfishPipeBuffer* buffEnd = buff + numBuffers; + + for (; buff < buffEnd; buff++) + count += buff->size; + + buff = buffers; + while (count > 0) { + int avail = buff->size - buffStart; + int len = write(pipe->io->fd, buff->data + buffStart, avail); + + /* the write succeeded */ + if (len > 0) { + buffStart += len; + if (buffStart >= buff->size) { + buff++; + buffStart = 0; + } + count -= len; + ret += len; + continue; + } + + /* we reached the end of stream? */ + if (len == 0) { + if (ret == 0) + ret = PIPE_ERROR_IO; + break; + } + + /* loop on EINTR */ + if (errno == EINTR) + continue; + + /* if we already wrote some stuff, simply return */ + if (ret > 0) { + break; + } + + /* need to return an appropriate error code */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = PIPE_ERROR_AGAIN; + } else { + ret = PIPE_ERROR_IO; + } + break; + } + + return ret; +} + +static int +netPipe_recvBuffers( void* opaque, GoldfishPipeBuffer* buffers, int numBuffers ) +{ + NetPipe* pipe = opaque; + int count = 0; + int ret = 0; + int buffStart = 0; + GoldfishPipeBuffer* buff = buffers; + GoldfishPipeBuffer* buffEnd = buff + numBuffers; + + for (; buff < buffEnd; buff++) + count += buff->size; + + buff = buffers; + while (count > 0) { + int avail = buff->size - buffStart; + int len = read(pipe->io->fd, buff->data + buffStart, avail); + + /* the read succeeded */ + if (len > 0) { + buffStart += len; + if (buffStart >= buff->size) { + buff++; + buffStart = 0; + } + count -= len; + ret += len; + continue; + } + + /* we reached the end of stream? */ + if (len == 0) { + if (ret == 0) + ret = PIPE_ERROR_IO; + break; + } + + /* loop on EINTR */ + if (errno == EINTR) + continue; + + /* if we already read some stuff, simply return */ + if (ret > 0) { + break; + } + + /* need to return an appropriate error code */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + ret = PIPE_ERROR_AGAIN; + } else { + ret = PIPE_ERROR_IO; + } + break; + } + return ret; +} + +static unsigned +netPipe_poll( void* opaque ) +{ + NetPipe* pipe = opaque; + unsigned mask = loopIo_poll(pipe->io); + unsigned ret = 0; + + if (mask & LOOP_IO_READ) + ret |= PIPE_WAKE_READ; + if (mask & LOOP_IO_WRITE) + ret |= PIPE_WAKE_WRITE; + + return ret; +} + +static void +netPipe_wakeOn( void* opaque, int flags ) +{ + NetPipe* pipe = opaque; + + DD("%s: flags=%d", __FUNCTION__, flags); + + pipe->wakeWanted |= flags; + netPipe_resetState(pipe); +} + + +void* +netPipe_initTcp( void* hwpipe, void* _looper, const char* args ) +{ + /* Build SockAddress from arguments. Acceptable formats are: + * + * <port> + * <host>:<port> + */ + SockAddress address; + void* ret; + + if (args == NULL) { + D("%s: Missing address!", __FUNCTION__); + return NULL; + } + D("%s: Address is '%s'", __FUNCTION__, args); + + char host[256]; /* max size of regular FDQN+1 */ + int hostlen = 0; + int port; + const char* p; + + /* Assume that anything after the last ':' is a port number + * And that what is before it is a port number. Should handle IPv6 + * notation. */ + p = strrchr(args, ':'); + if (p != NULL) { + hostlen = p - args; + if (hostlen >= sizeof(host)) { + D("%s: Address too long!", __FUNCTION__); + return NULL; + } + memcpy(host, args, hostlen); + host[hostlen] = '\0'; + args = p + 1; + } else { + snprintf(host, sizeof host, "127.0.0.1"); + } + + /* Now, look at the port number */ + { + char* end; + long val = strtol(args, &end, 10); + if (end == NULL || *end != '\0' || val <= 0 || val > 65535) { + D("%s: Invalid port number: '%s'", __FUNCTION__, args); + } + port = (int)val; + } + if (sock_address_init_resolve(&address, host, port, 0) < 0) { + D("%s: Could not resolve address", __FUNCTION__); + return NULL; + } + + ret = netPipe_initFromAddress(hwpipe, &address, _looper); + + sock_address_done(&address); + return ret; +} + +void* +netPipe_initUnix( void* hwpipe, void* _looper, const char* args ) +{ + /* Build SockAddress from arguments. Acceptable formats are: + * + * <path> + */ + SockAddress address; + void* ret; + + if (args == NULL || args[0] == '\0') { + D("%s: Missing address!", __FUNCTION__); + return NULL; + } + D("%s: Address is '%s'", __FUNCTION__, args); + + sock_address_init_unix(&address, args); + + ret = netPipe_initFromAddress(hwpipe, &address, _looper); + + sock_address_done(&address); + return ret; +} + + +/********************************************************************** + ********************************************************************** + ***** + ***** N E T W O R K P I P E M E S S A G E S + ***** + *****/ + +static const GoldfishPipeFuncs netPipeTcp_funcs = { + netPipe_initTcp, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + +static const GoldfishPipeFuncs netPipeUnix_funcs = { + netPipe_initUnix, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + + +#define DEFAULT_OPENGLES_PORT 22468 + +static void* +openglesPipe_init( void* hwpipe, void* _looper, const char* args ) +{ + char temp[32]; + + /* For now, simply connect through tcp */ + snprintf(temp, sizeof temp, "%d", DEFAULT_OPENGLES_PORT); + return netPipe_initTcp(hwpipe, _looper, temp); +} + +static const GoldfishPipeFuncs openglesPipe_funcs = { + openglesPipe_init, + netPipe_closeFromGuest, + netPipe_sendBuffers, + netPipe_recvBuffers, + netPipe_poll, + netPipe_wakeOn, +}; + + +void +android_net_pipes_init(void) +{ + Looper* looper = looper_newCore(); + + goldfish_pipe_add_type( "tcp", looper, &netPipeTcp_funcs ); + goldfish_pipe_add_type( "unix", looper, &netPipeUnix_funcs ); + goldfish_pipe_add_type( "opengles", looper, &openglesPipe_funcs ); +} diff --git a/android/hw-qemud-pipe.h b/android/pipe-net.h index 540e02b..08db031 100644 --- a/android/hw-qemud-pipe.h +++ b/android/pipe-net.h @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef _ANDROID_HW_QEMUD_PIPE_H -#define _ANDROID_HW_QEMUD_PIPE_H -void init_qemud_pipes(void); -void android_hw_opengles_init(void); +#ifndef ANDROID_PIPE_NET_H +#define ANDROID_PIPE_NET_H -#endif /* _ANDROID_HW_QEMUD_PIPE_H */ +void android_net_pipes_init(void); + +#endif /* ANDROID_PIPE_NET_H */ 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); diff --git a/vl-android.c b/vl-android.c index 1d730f5..6653286 100644 --- a/vl-android.c +++ b/vl-android.c @@ -52,7 +52,6 @@ #include "modem_driver.h" #include "android/gps.h" #include "android/hw-qemud.h" -#include "android/hw-qemud-pipe.h" #include "android/hw-kmsg.h" #include "android/charmap.h" #include "android/globals.h" @@ -64,6 +63,7 @@ #include "android/utils/tempfile.h" #include "android/display-core.h" #include "android/utils/timezone.h" +#include "android/pipe-net.h" #include "android/snapshot.h" #include "targphys.h" #include "tcpdump.h" @@ -4045,6 +4045,7 @@ int main(int argc, char **argv, char **envp) /* Initialize boot properties. */ boot_property_init_service(); android_hw_control_init(); + android_net_pipes_init(); optind = 1; for(;;) { @@ -5175,7 +5176,7 @@ int main(int argc, char **argv, char **envp) } /* Initialize OpenGLES emulation */ - android_hw_opengles_init(); + //android_hw_opengles_init(); if (android_op_cpu_delay) { char* end; |