android: Update android_pipe virtual device.
WARNING: This makes the device dependent on the AndroidEmu
header android/emulation/android_pipe_device.h.
diff --git a/hw/arm/ranchu.c b/hw/arm/ranchu.c
index 4667a1a..8450a9a 100644
--- a/hw/arm/ranchu.c
+++ b/hw/arm/ranchu.c
@@ -38,7 +38,6 @@
#include "qemu/config-file.h"
#include "sysemu/char.h"
#include "monitor/monitor.h"
-#include "hw/misc/android_pipe.h"
#include "android/android.h"
diff --git a/hw/mips/mips_ranchu.c b/hw/mips/mips_ranchu.c
index 420d763..65d85c4 100644
--- a/hw/mips/mips_ranchu.c
+++ b/hw/mips/mips_ranchu.c
@@ -13,7 +13,6 @@
#include "qemu/config-file.h"
#include "sysemu/char.h"
#include "monitor/monitor.h"
-#include "hw/misc/android_pipe.h"
#include "hw/loader.h"
#include "elf.h"
#include "hw/intc/goldfish_pic.h"
diff --git a/hw/misc/android_pipe.c b/hw/misc/android_pipe.c
index 17a7a47..2e7f2a0 100644
--- a/hw/misc/android_pipe.c
+++ b/hw/misc/android_pipe.c
@@ -1,5 +1,6 @@
/* Copyright (C) 2011 The Android Open Source Project
** Copyright (C) 2014 Linaro Limited
+** Copyright (C) 2015 Intel Corporation
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
@@ -27,15 +28,15 @@
** should give some thought to if this needs re-writing to take
** advantage of that infrastructure to create the pipes.
*/
-
+#include "android-qemu2-glue/utils/stream.h"
+#include "android/emulation/android_pipe_device.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
-
-#include "hw/misc/android_pipe.h"
-#include "qemu-common.h"
-#include "qemu/timer.h"
#include "qemu/error-report.h"
+#include <assert.h>
+#include <glib.h>
+
/* Set to > 0 for debug output */
#define PIPE_DEBUG 0
@@ -71,6 +72,70 @@
exit(1); \
} while (0);
+/* The following definitions must match those in the kernel driver
+ * found at android.googlesource.com/kernel/goldfish.git
+ *
+ * drivers/platform/goldfish/goldfish_pipe.c
+ */
+
+/* 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 */
+/* read/write: parameter buffer address */
+#define PIPE_REG_PARAMS_ADDR_LOW 0x18
+#define PIPE_REG_PARAMS_ADDR_HIGH 0x1c
+/* write: access with paremeter buffer */
+#define PIPE_REG_ACCESS_PARAMS 0x20
+#define PIPE_REG_VERSION 0x24 /* read: device version */
+#define PIPE_REG_CHANNEL_HIGH 0x30 /* read/write: high 32 bit channel id */
+#define PIPE_REG_ADDRESS_HIGH 0x34 /* write: high 32 bit physical address */
+
+/* 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 */
+
+/* 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 */
+
+struct access_params_32 {
+ uint32_t channel; /* 0x00 */
+ uint32_t size; /* 0x04 */
+ uint32_t address; /* 0x08 */
+ uint32_t cmd; /* 0x0c */
+ uint32_t result; /* 0x10 */
+ /* reserved for future extension */
+ uint32_t flags; /* 0x14 */
+};
+
+struct access_params_64 {
+ uint64_t channel; /* 0x00 */
+ uint32_t size; /* 0x08 */
+ uint64_t address; /* 0x0c */
+ uint32_t cmd; /* 0x14 */
+ uint32_t result; /* 0x18 */
+ /* reserved for future extension */
+ uint32_t flags; /* 0x1c */
+};
+
+union access_params {
+ struct access_params_32 aps32;
+ struct access_params_64 aps64;
+};
+
/* Maximum length of pipe service name, in characters (excluding final 0) */
#define MAX_PIPE_SERVICE_NAME_SIZE 255
@@ -106,362 +171,89 @@
/***********************************************************************
***********************************************************************
*****
- ***** 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 *name;
- void *opaque; /* pipe specific data */
- AndroidPipeFuncs funcs;
-} PipeService;
-
-typedef struct {
- int count;
- PipeService services[MAX_PIPE_SERVICES];
-} PipeServices;
-
-static PipeServices _pipeServices[1];
-
-void
-android_pipe_add_type(const char *pipeName,
- void *pipeOpaque,
- const AndroidPipeFuncs *pipeFuncs)
-{
- PipeServices *list = _pipeServices;
- int count = list->count;
-
- if (count >= MAX_PIPE_SERVICES) {
- APANIC("Too many goldfish pipe services (%d)", count);
- }
-
- if (strlen(pipeName) > MAX_PIPE_SERVICE_NAME_SIZE) {
- APANIC("Pipe service name too long: '%s'", pipeName);
- }
-
- list->services[count].name = pipeName;
- list->services[count].opaque = pipeOpaque;
- list->services[count].funcs = pipeFuncs[0];
-
- list->count++;
-}
-
-static const PipeService* android_pipe_find_type(const char *pipeName)
-{
- PipeServices* list = _pipeServices;
- int count = list->count;
- int nn;
-
- for (nn = 0; nn < count; nn++) {
- if (!strcmp(list->services[nn].name, pipeName)) {
- return &list->services[nn];
- }
- }
- return NULL;
-}
-
-
-/***********************************************************************
- ***********************************************************************
- *****
***** P I P E C O N N E C T I O N S
*****
*****/
-typedef struct Pipe {
- struct Pipe *next;
- struct Pipe *next_waked;
+typedef struct HwPipe {
+ struct HwPipe *next;
+ struct HwPipe *wanted_next;
+ struct HwPipe *wanted_prev;
PipeDevice *device;
uint64_t channel; /* opaque kernel handle */
- void *opaque;
- const AndroidPipeFuncs *funcs;
- const PipeService *service;
- char* args;
unsigned char wanted;
char closed;
-} Pipe;
+ void *pipe;
+} HwPipe;
-/* Forward */
-static void* pipeConnector_new(Pipe* pipe);
+static HwPipe* hwpipe_new0(struct PipeDevice* device) {
+ HwPipe* hwp;
+ ANEW0(hwp);
+ hwp->device = device;
+ return hwp;
+}
-static Pipe*
+static void hwpipe_free(HwPipe* hwp) {
+ android_pipe_guest_close(hwp->pipe);
+ free(hwp);
+}
+
+static unsigned char get_and_clear_pipe_wanted(HwPipe* pipe) {
+ unsigned char val = pipe->wanted;
+ pipe->wanted = 0;
+ return val;
+}
+
+static void set_pipe_wanted_bits(HwPipe* pipe, unsigned char val) {
+ pipe->wanted |= val;
+}
+
+static HwPipe*
pipe_new0(PipeDevice* dev)
{
- Pipe* pipe;
- pipe = g_malloc0(sizeof(Pipe));
+ HwPipe* pipe;
+ pipe = g_malloc0(sizeof(HwPipe));
pipe->device = dev;
return pipe;
}
-static Pipe*
+static HwPipe*
pipe_new(uint64_t channel, PipeDevice* dev)
{
- Pipe* pipe = pipe_new0(dev);
+ HwPipe* pipe = pipe_new0(dev);
pipe->channel = channel;
- pipe->opaque = pipeConnector_new(pipe);
+ pipe->pipe = android_pipe_guest_open(pipe);
return pipe;
}
-static Pipe**
-pipe_list_findp_channel(Pipe **list, uint64_t channel)
+static void pipe_free(HwPipe* pipe)
{
- Pipe** pnode = list;
- for (;;) {
- Pipe* node = *pnode;
- if (node == NULL || node->channel == channel) {
- break;
- }
- pnode = &node->next;
- }
- return pnode;
-}
-
-static Pipe**
-pipe_list_findp_waked(Pipe **list, Pipe *pipe)
-{
- Pipe** pnode = list;
- for (;;) {
- Pipe* node = *pnode;
- if (node == NULL || node == pipe) {
- break;
- }
- pnode = &node->next_waked;
- }
- return pnode;
-}
-
-
-static void pipe_list_remove_waked(Pipe **list, Pipe *pipe)
-{
- Pipe** lookup = pipe_list_findp_waked(list, pipe);
- Pipe* node = *lookup;
-
- if (node != NULL) {
- (*lookup) = node->next_waked;
- node->next_waked = NULL;
- }
-}
-
-static void pipe_free(Pipe* pipe)
-{
- /* Call close callback */
- if (pipe->funcs->close) {
- pipe->funcs->close(pipe->opaque);
- }
- /* Free stuff */
- g_free(pipe->args);
+ android_pipe_guest_close(pipe->pipe);
g_free(pipe);
}
/***********************************************************************
***********************************************************************
*****
- ***** P I P E C O N N E C T O R S
- *****
- *****/
-
-/* 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.
- */
-typedef struct {
- Pipe* pipe;
- char buffer[128];
- int buffpos;
-} PipeConnector;
-
-static const AndroidPipeFuncs pipeConnector_funcs; // forward
-
-void*
-pipeConnector_new(Pipe* pipe)
-{
- PipeConnector* pcon;
-
- pcon = g_malloc0(sizeof(PipeConnector));
- pcon->pipe = pipe;
- pipe->funcs = &pipeConnector_funcs;
- return pcon;
-}
-
-static void
-pipeConnector_close( void* opaque )
-{
- PipeConnector* pcon = opaque;
- g_free(pcon);
-}
-
-static int
-pipeConnector_sendBuffers( void* opaque, const AndroidPipeBuffer* buffers, int numBuffers )
-{
- PipeConnector* pcon = opaque;
- const AndroidPipeBuffer* buffers_limit = buffers + numBuffers;
- int ret = 0;
-
- DD("%s: channel=0x%llx numBuffers=%d", __FUNCTION__,
- (unsigned long long)pcon->pipe->channel,
- numBuffers);
-
- while (buffers < buffers_limit) {
- int avail;
-
- DD("%s: buffer data (%3zd bytes): '%.*s'", __FUNCTION__,
- buffers[0].size, (int) buffers[0].size, buffers[0].data);
-
- if (buffers[0].size == 0) {
- buffers++;
- continue;
- }
-
- avail = sizeof(pcon->buffer) - pcon->buffpos;
- if (avail > buffers[0].size)
- avail = buffers[0].size;
-
- if (avail > 0) {
- memcpy(pcon->buffer + pcon->buffpos, buffers[0].data, avail);
- pcon->buffpos += avail;
- ret += avail;
- }
- buffers++;
- }
-
- /* 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("%s: connector: '%s'", __FUNCTION__, pcon->buffer);
-
- if (memcmp(pcon->buffer, "pipe:", 5) != 0) {
- /* Nope, we don't handle these for now. */
- qemu_log_mask(LOG_UNIMP, "%s: Unknown pipe connection: '%s'\n",
- __func__, pcon->buffer);
- return PIPE_ERROR_INVAL;
- }
-
- pipeName = pcon->buffer + 5;
- pipeArgs = strchr(pipeName, ':');
-
- /* Directly connect qemud:adb pipes to their adb backends without
- * going through the qemud multiplexer. All other uses of the ':'
- * char than an initial "qemud:" will be parsed as arguments to the
- * pipe name preceeding the colon.
- */
- if (pipeArgs && pipeArgs - pipeName == 5
- && strncmp(pipeName, "qemud", 5) == 0) {
- pipeArgs = strchr(pipeArgs + 1, ':');
- }
-
- if (pipeArgs != NULL) {
- *pipeArgs++ = '\0';
- if (!*pipeArgs)
- pipeArgs = NULL;
- }
-
- Pipe* pipe = pcon->pipe;
- const PipeService* svc = android_pipe_find_type(pipeName);
- if (svc == NULL) {
- qemu_log_mask(LOG_UNIMP, "%s: Couldn't find service: '%s'\n",
- __func__, pipeName);
- return PIPE_ERROR_INVAL;
- }
-
- void* peer = svc->funcs.init(pipe, svc->opaque, pipeArgs);
- if (peer == NULL) {
- fprintf(stderr,"%s: error initialising pipe:'%s' with args '%s'\n",
- __func__, pipeName, pipeArgs);
- return PIPE_ERROR_INVAL;
- }
-
- /* Do the evil switch now */
- pipe->opaque = peer;
- pipe->service = svc;
- pipe->funcs = &svc->funcs;
- pipe->args = g_strdup(pipeArgs);
- g_free(pcon);
- }
-
- return ret;
-}
-
-static int
-pipeConnector_recvBuffers( void* opaque, AndroidPipeBuffer* buffers, int numBuffers )
-{
- return PIPE_ERROR_IO;
-}
-
-static unsigned
-pipeConnector_poll( void* opaque )
-{
- return PIPE_POLL_OUT;
-}
-
-static void
-pipeConnector_wakeOn( void* opaque, int flags )
-{
- /* nothing, really should never happen */
-}
-
-static void
-pipeConnector_save( void* pipe, QEMUFile* file )
-{
- PipeConnector* pcon = pipe;
- qemu_put_sbe32(file, pcon->buffpos);
- qemu_put_sbuffer(file, (const int8_t*)pcon->buffer, pcon->buffpos);
-}
-
-static void*
-pipeConnector_load( void* hwpipe, void* pipeOpaque, const char* args, QEMUFile* file )
-{
- PipeConnector* pcon;
-
- int len = qemu_get_sbe32(file);
- if (len < 0 || len > sizeof(pcon->buffer)) {
- return NULL;
- }
- pcon = pipeConnector_new(hwpipe);
- pcon->buffpos = len;
- if (qemu_get_buffer(file, (uint8_t*)pcon->buffer, pcon->buffpos) != pcon->buffpos) {
- g_free(pcon);
- return NULL;
- }
- return pcon;
-}
-
-static const AndroidPipeFuncs 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 */
- pipeConnector_save,
- pipeConnector_load,
-};
-
-
-/***********************************************************************
- ***********************************************************************
- *****
***** G O L D F I S H P I P E D E V I C E
*****
*****/
struct PipeDevice {
- AndroidPipeState *ps; /* FIXME: backlink to instance state */
+ AndroidPipeState *ps; // FIXME: backlink to instance state
- /* the list of all pipes */
- Pipe* pipes;
+ // The list of all pipes.
+ HwPipe* pipes;
+ // The list of the pipes that signalled some 'wanted' state.
+ HwPipe* wanted_pipes_first;
+ // A cached wanted pipe after the guest's _CHANNEL_HIGH call. We need to
+ // return the very same pipe during the following *_CHANNEL call.
+ HwPipe* wanted_pipe_after_channel_high;
- /* the list of signalled pipes */
- Pipe* signaled_pipes;
+ // Cache of the pipes by channel for a faster lookup.
+ GHashTable* pipes_by_channel;
- /* i/o registers */
+ // i/o registers
uint64_t address;
uint32_t size;
uint32_t status;
@@ -470,29 +262,122 @@
uint64_t params_addr;
};
-/* Map the guest buffer specified by the guest vaddr 'address'.
+// hashtable-related functions
+// 64-bit emulator just casts uint64_t to gpointer to get a key for the
+// GLib's hash table. 32-bit one has to dynamically allocate an 8-byte chunk
+// of memory and copy the channel value there, storing pointer as a key.
+static uint64_t hash_channel_from_key(gconstpointer key) {
+#ifdef __x86_64__
+ // keys are just channels
+ return (uint64_t)key;
+#else
+ // keys are pointers to channels
+ return *(uint64_t*)key;
+#endif
+}
+
+static gpointer hash_create_key_from_channel(uint64_t channel) {
+#ifdef __x86_64__
+ return (gpointer)channel;
+#else
+ uint64_t* on_heap = (uint64_t*)malloc(sizeof(channel));
+ if (!on_heap) {
+ APANIC("%s: failed to allocate RAM for a channel value\n", __func__);
+ }
+ *on_heap = channel;
+ return on_heap;
+#endif
+}
+
+static gconstpointer hash_cast_key_from_channel(const uint64_t* channel) {
+#ifdef __x86_64__
+ return (gconstpointer)*channel;
+#else
+ return (gconstpointer)channel;
+#endif
+}
+
+static guint hash_channel(gconstpointer key) {
+ uint64_t channel = hash_channel_from_key(key);
+ return (guint)(channel ^ (channel >> 6));
+}
+
+static gboolean hash_channel_equal(gconstpointer a, gconstpointer b) {
+ return hash_channel_from_key(a) == hash_channel_from_key(b);
+}
+
+#ifdef __x86_64__
+// we don't need to free channels in 64-bit emulator as we store them in-place
+static void (*hash_channel_destroy)(gpointer a) = NULL;
+#else
+static void hash_channel_destroy(gpointer a) {
+ free((uint64_t*)a);
+}
+#endif
+
+// Wanted pipe linked list operations
+static HwPipe* wanted_pipes_pop_first(PipeDevice* dev) {
+ if (dev->wanted_pipe_after_channel_high) {
+ HwPipe* val = dev->wanted_pipe_after_channel_high;
+ dev->wanted_pipe_after_channel_high = NULL;
+ return val;
+ }
+ HwPipe* pipe = dev->wanted_pipes_first;
+ if (pipe) {
+ dev->wanted_pipes_first = pipe->wanted_next;
+ assert(pipe->wanted_prev == NULL);
+ pipe->wanted_next = NULL;
+ if (dev->wanted_pipes_first) {
+ dev->wanted_pipes_first->wanted_prev = NULL;
+ }
+ }
+ return pipe;
+}
+
+static void wanted_pipes_add(PipeDevice* dev, HwPipe* pipe) {
+ if (!pipe->wanted_next && !pipe->wanted_prev
+ && dev->wanted_pipes_first != pipe
+ && dev->wanted_pipe_after_channel_high != pipe) {
+ pipe->wanted_next = dev->wanted_pipes_first;
+ if (dev->wanted_pipes_first) {
+ dev->wanted_pipes_first->wanted_prev = pipe;
+ }
+ dev->wanted_pipes_first = pipe;
+ }
+}
+
+static void wanted_pipes_remove(PipeDevice* dev, HwPipe* pipe) {
+ if (dev->wanted_pipe_after_channel_high == pipe) {
+ dev->wanted_pipe_after_channel_high = NULL;
+ }
+
+ if (pipe->wanted_next) {
+ pipe->wanted_next->wanted_prev = pipe->wanted_prev;
+ }
+ if (pipe->wanted_prev) {
+ pipe->wanted_prev->wanted_next = pipe->wanted_next;
+ pipe->wanted_prev = NULL;
+ } else if (dev->wanted_pipes_first == pipe) {
+ dev->wanted_pipes_first = pipe->wanted_next;
+ }
+ pipe->wanted_next = NULL;
+}
+
+/* Update this version number if the device's interface changes. */
+#define PIPE_DEVICE_VERSION 1
+
+/* Map the guest buffer specified by the guest paddr 'phys'.
* Returns a host pointer which should be unmapped later via
* cpu_physical_memory_unmap(), or NULL if mapping failed (likely
- * because the vaddr doesn't actually point at RAM).
+ * because the paddr doesn't actually point at RAM).
* Note that for RAM the "mapping" process doesn't actually involve a
* data copy.
- *
- * TODO: using cpu_get_phys_page_debug() is a bit bogus, and we could
- * avoid it if we fixed the driver to do the sane thing and pass us
- * physical addresses rather than virtual ones.
*/
-static void *map_guest_buffer(target_ulong address, size_t size, int is_write)
+static void *map_guest_buffer(hwaddr phys, size_t size, int is_write)
{
hwaddr l = size;
void *ptr;
- /* Convert virtual address to physical address */
- hwaddr phys = cpu_get_phys_page_debug(current_cpu, address);
-
- if (phys == -1) {
- return NULL;
- }
-
ptr = cpu_physical_memory_map(phys, &l, is_write);
if (!ptr) {
/* Can't happen for RAM */
@@ -510,11 +395,9 @@
return ptr;
}
-static void
-pipeDevice_doCommand( PipeDevice* dev, uint32_t command )
-{
- Pipe** lookup = pipe_list_findp_channel(&dev->pipes, dev->channel);
- Pipe* pipe = *lookup;
+static void pipeDevice_doCommand(PipeDevice* dev, uint32_t command) {
+ HwPipe* pipe = (HwPipe*)g_hash_table_lookup(
+ dev->pipes_by_channel, hash_cast_key_from_channel(&dev->channel));
/* Check that we're referring a known pipe channel */
if (command != PIPE_CMD_OPEN && pipe == NULL) {
@@ -539,24 +422,40 @@
pipe->next = dev->pipes;
dev->pipes = pipe;
dev->status = 0;
+ g_hash_table_insert(dev->pipes_by_channel,
+ hash_create_key_from_channel(dev->channel), pipe);
break;
- case PIPE_CMD_CLOSE:
+ case PIPE_CMD_CLOSE: {
DD("%s: CMD_CLOSE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
- /* Remove from device's lists */
- *lookup = pipe->next;
+ // Remove from device's lists.
+ // This linear lookup is potentially slow, but we don't delete pipes
+ // often enough for it to become noticable.
+ HwPipe** pnode = &dev->pipes;
+ while (*pnode && *pnode != pipe) {
+ pnode = &(*pnode)->next;
+ }
+ if (!*pnode) {
+ dev->status = PIPE_ERROR_INVAL;
+ break;
+ }
+ *pnode = pipe->next;
pipe->next = NULL;
- pipe_list_remove_waked(&dev->signaled_pipes, pipe);
+ g_hash_table_remove(dev->pipes_by_channel,
+ hash_cast_key_from_channel(&pipe->channel));
+ wanted_pipes_remove(dev, pipe);
+
pipe_free(pipe);
break;
+ }
case PIPE_CMD_POLL:
- dev->status = pipe->funcs->poll(pipe->opaque);
+ dev->status = android_pipe_guest_poll(pipe->pipe);
DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
break;
case PIPE_CMD_READ_BUFFER: {
- /* Translate virtual address into physical one, into emulator memory. */
+ /* Translate guest physical address into emulator memory. */
AndroidPipeBuffer buffer;
buffer.data = map_guest_buffer(dev->address, dev->size, 1);
if (!buffer.data) {
@@ -564,7 +463,7 @@
break;
}
buffer.size = dev->size;
- dev->status = pipe->funcs->recvBuffers(pipe->opaque, &buffer, 1);
+ dev->status = android_pipe_guest_recv(pipe->pipe, &buffer, 1);
DD("%s: CMD_READ_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d",
__FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
dev->size, dev->status);
@@ -573,7 +472,7 @@
}
case PIPE_CMD_WRITE_BUFFER: {
- /* Translate virtual address into physical one, into emulator memory. */
+ /* Translate guest physical address into emulator memory. */
AndroidPipeBuffer buffer;
buffer.data = map_guest_buffer(dev->address, dev->size, 0);
if (!buffer.data) {
@@ -581,7 +480,7 @@
break;
}
buffer.size = dev->size;
- dev->status = pipe->funcs->sendBuffers(pipe->opaque, &buffer, 1);
+ dev->status = android_pipe_guest_send(pipe->pipe, &buffer, 1);
DD("%s: CMD_WRITE_BUFFER channel=0x%llx address=0x%16llx size=%d > status=%d",
__FUNCTION__, (unsigned long long)dev->channel, (unsigned long long)dev->address,
dev->size, dev->status);
@@ -593,7 +492,7 @@
DD("%s: CMD_WAKE_ON_READ channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
if ((pipe->wanted & PIPE_WAKE_READ) == 0) {
pipe->wanted |= PIPE_WAKE_READ;
- pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+ android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
}
dev->status = 0;
break;
@@ -602,7 +501,7 @@
DD("%s: CMD_WAKE_ON_WRITE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
if ((pipe->wanted & PIPE_WAKE_WRITE) == 0) {
pipe->wanted |= PIPE_WAKE_WRITE;
- pipe->funcs->wakeOn(pipe->opaque, pipe->wanted);
+ android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
}
dev->status = 0;
break;
@@ -721,34 +620,41 @@
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;
+ case PIPE_REG_CHANNEL: {
+ HwPipe* wanted_pipe = wanted_pipes_pop_first(dev);
+ if (wanted_pipe != NULL) {
+ dev->wakes = get_and_clear_pipe_wanted(wanted_pipe);
DR("%s: channel=0x%llx wanted=%d", __FUNCTION__,
- (unsigned long long)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) {
- /* android_device_set_irq(&dev->dev, 0, 0); */
- qemu_set_irq(s->irq, 0);
- DD("%s: lowering IRQ", __FUNCTION__);
- }
- return (uint32_t)(pipe->channel & 0xFFFFFFFFUL);
+ (unsigned long long)wanted_pipe->channel, dev->wakes);
+ return (uint32_t)(wanted_pipe->channel & 0xFFFFFFFFUL);
+ } else {
+ qemu_set_irq(s->irq, 0);
+ DD("%s: no signaled channels, lowering IRQ", __FUNCTION__);
+ return 0;
}
- DR("%s: no signaled channels", __FUNCTION__);
- return 0;
+ }
- case PIPE_REG_CHANNEL_HIGH:
- if (dev->signaled_pipes != NULL) {
- Pipe* pipe = dev->signaled_pipes;
+ case PIPE_REG_CHANNEL_HIGH: {
+ // TODO(zyy): this call is really dangerous; currently the device would
+ // stop the calls as soon as we return 0 here; but it means that if the
+ // channel's upper 32 bits are zeroes (that happens), we won't be able
+ // to wake either that channel or any following ones.
+ // I think we should create a new pipe protocol to deal with this
+ // issue and also reduce chattiness of the pipe communication at the
+ // same time.
+ HwPipe* wanted_pipe = wanted_pipes_pop_first(dev);
+ if (wanted_pipe != NULL) {
+ dev->wanted_pipe_after_channel_high = wanted_pipe;
DR("%s: channel_high=0x%llx wanted=%d", __FUNCTION__,
- (unsigned long long)pipe->channel, pipe->wanted);
- return (uint32_t)(pipe->channel >> 32);
+ (unsigned long long)wanted_pipe->channel, wanted_pipe->wanted);
+ assert((uint32_t)(wanted_pipe->channel >> 32) != 0);
+ return (uint32_t)(wanted_pipe->channel >> 32);
+ } else {
+ qemu_set_irq(s->irq, 0);
+ DD("%s: no signaled channels (for high), lowering IRQ", __func__);
+ return 0;
}
- DR("%s: no signaled channels", __FUNCTION__);
- return 0;
+ }
case PIPE_REG_WAKES:
DR("%s: wakes %d", __FUNCTION__, dev->wakes);
@@ -760,6 +666,9 @@
case PIPE_REG_PARAMS_ADDR_LOW:
return (uint32_t)(dev->params_addr & 0xFFFFFFFFUL);
+ case PIPE_REG_VERSION:
+ return PIPE_DEVICE_VERSION;
+
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown register %" HWADDR_PRId
" (0x%" HWADDR_PRIx ")\n", __FUNCTION__, offset, offset);
@@ -773,6 +682,196 @@
.endianness = DEVICE_NATIVE_ENDIAN
};
+static void qemu2_android_pipe_reset(void* hwpipe, void* internal_pipe);
+static void qemu2_android_pipe_host_signal_wake(void* hwpipe, unsigned flags);
+static void qemu2_android_pipe_host_close(void* hwpipe);
+
+static const AndroidPipeHwFuncs qemu2_android_pipe_hw_funcs = {
+ .resetPipe = qemu2_android_pipe_reset,
+ .closeFromHost = qemu2_android_pipe_host_close,
+ .signalWake = qemu2_android_pipe_host_signal_wake,
+};
+
+#define ANDROID_PIPE_SAVE_VERSION 1
+
+static void android_pipe_save(QEMUFile* f, void* opaque) {
+ AndroidPipeState* s = opaque;
+ PipeDevice* dev = s->dev;
+ Stream* stream = stream_from_qemufile(f);
+
+ /* Save i/o registers. */
+ stream_put_be64(stream, dev->address);
+ stream_put_be32(stream, dev->size);
+ stream_put_be32(stream, dev->status);
+ stream_put_be64(stream, dev->channel);
+ stream_put_be32(stream, dev->wakes);
+ stream_put_be64(stream, dev->params_addr);
+
+ /* Save the pipe count and state of the pipes. */
+ int pipe_count = 0;
+ HwPipe* pipe;
+ for (pipe = dev->pipes; pipe; pipe = pipe->next) {
+ ++pipe_count;
+ }
+ stream_put_be32(stream, pipe_count);
+ for (pipe = dev->pipes; pipe; pipe = pipe->next) {
+ stream_put_be64(stream, pipe->channel);
+ stream_put_byte(stream, pipe->closed);
+ stream_put_byte(stream, pipe->wanted);
+ android_pipe_guest_save(pipe->pipe, stream);
+ }
+
+ /* Save wanted pipes list. */
+ int wanted_pipes_count = 0;
+ for (pipe = dev->wanted_pipes_first; pipe; pipe = pipe->wanted_next) {
+ wanted_pipes_count++;
+ }
+ stream_put_be32(stream, wanted_pipes_count);
+ for (pipe = dev->wanted_pipes_first; pipe; pipe = pipe->wanted_next) {
+ stream_put_be64(stream, pipe->channel);
+ }
+ if (dev->wanted_pipe_after_channel_high) {
+ stream_put_byte(stream, 1);
+ stream_put_be64(stream, dev->wanted_pipe_after_channel_high->channel);
+ } else {
+ stream_put_byte(stream, 0);
+ }
+
+ stream_free(stream);
+}
+
+static int android_pipe_load(QEMUFile* f, void* opaque, int version_id) {
+ AndroidPipeState* s = opaque;
+ PipeDevice* dev = s->dev;
+ Stream* stream = stream_from_qemufile(f);
+
+ /* Load i/o registers. */
+ dev->address = stream_get_be64(stream);
+ dev->size = stream_get_be32(stream);
+ dev->status = stream_get_be32(stream);
+ dev->channel = stream_get_be64(stream);
+ dev->wakes = stream_get_be32(stream);
+ dev->params_addr = stream_get_be64(stream);
+
+ /* Clean up old pipe objects. */
+ HwPipe* pipe = dev->pipes;
+ while(pipe) {
+ HwPipe* old_pipe = pipe;
+ pipe = pipe->next;
+ hwpipe_free(old_pipe);
+ }
+
+ /* Restore pipes. */
+ int pipe_count = stream_get_be32(stream);
+ int pipe_n;
+ HwPipe** pipe_list_end = &(dev->pipes);
+ *pipe_list_end = NULL;
+ uint64_t* force_closed_pipes = malloc(sizeof(uint64_t) * pipe_count);
+ int force_closed_pipes_count = 0;
+ for (pipe_n = 0; pipe_n < pipe_count; pipe_n++) {
+ HwPipe* pipe = hwpipe_new0(dev);
+ char force_close = 0;
+ pipe->channel = stream_get_be64(stream);
+ pipe->closed = stream_get_byte(stream);
+ pipe->wanted = stream_get_byte(stream);
+ pipe->pipe = android_pipe_guest_load(stream, pipe, &force_close);
+ pipe->wanted_next = pipe->wanted_prev = NULL;
+
+ // |pipe| might be NULL in case it couldn't be saved. However,
+ // in that case |force_close| will be set by android_pipe_guest_load,
+ // so |force_close| should be checked first so that the function
+ // can continue.
+ if (force_close) {
+ pipe->closed = 1;
+ force_closed_pipes[force_closed_pipes_count++] = pipe->channel;
+ } else if (!pipe->pipe) {
+ hwpipe_free(pipe);
+ free(force_closed_pipes);
+ return -EIO;
+ }
+
+ pipe->next = NULL;
+ *pipe_list_end = pipe;
+ pipe_list_end = &(pipe->next);
+ }
+
+ /* Rebuild the pipes-by-channel table. */
+ g_hash_table_remove_all(dev->pipes_by_channel); /* Clean up old data. */
+ for(pipe = dev->pipes; pipe; pipe = pipe->next) {
+ g_hash_table_insert(dev->pipes_by_channel,
+ hash_create_key_from_channel(pipe->channel),
+ pipe);
+ }
+
+ /* Reconstruct wanted pipes list. */
+ HwPipe** wanted_pipe_list_end = &(dev->wanted_pipes_first);
+ *wanted_pipe_list_end = NULL;
+ int wanted_pipes_count = stream_get_be32(stream);
+ uint64_t channel;
+ for (pipe_n = 0; pipe_n < wanted_pipes_count; ++pipe_n) {
+ channel = stream_get_be64(stream);
+ HwPipe* pipe = g_hash_table_lookup(dev->pipes_by_channel,
+ hash_cast_key_from_channel(&channel));
+ if (pipe) {
+ pipe->wanted_prev = *wanted_pipe_list_end;
+ pipe->wanted_next = NULL;
+ *wanted_pipe_list_end = pipe;
+ wanted_pipe_list_end = &(pipe->wanted_next);
+ } else {
+ stream_free(stream);
+ free(force_closed_pipes);
+ return -EIO;
+ }
+ }
+ if (stream_get_byte(stream)) {
+ channel = stream_get_be64(stream);
+ dev->wanted_pipe_after_channel_high =
+ g_hash_table_lookup(dev->pipes_by_channel,
+ hash_cast_key_from_channel(&channel));
+ if (!dev->wanted_pipe_after_channel_high) {
+ stream_free(stream);
+ free(force_closed_pipes);
+ return -EIO;
+ }
+ } else {
+ dev->wanted_pipe_after_channel_high = NULL;
+ }
+
+ /* Add forcibly closed pipes to wanted pipes list */
+ for (pipe_n = 0; pipe_n < force_closed_pipes_count; pipe_n++) {
+ HwPipe* pipe =
+ g_hash_table_lookup(dev->pipes_by_channel,
+ hash_cast_key_from_channel(
+ &force_closed_pipes[pipe_n]));
+ set_pipe_wanted_bits(pipe, PIPE_WAKE_CLOSED);
+ pipe->closed = 1;
+ if (!pipe->wanted_next &&
+ !pipe->wanted_prev &&
+ pipe != dev->wanted_pipe_after_channel_high) {
+ pipe->wanted_prev = *wanted_pipe_list_end;
+ *wanted_pipe_list_end = pipe;
+ wanted_pipe_list_end = &(pipe->wanted_next);
+ }
+ }
+
+ stream_free(stream);
+ free(force_closed_pipes);
+ return 0;
+}
+
+static void android_pipe_post_load(void* opaque) {
+ /* This function gets invoked after all VM state has
+ * been loaded. Raising IRQ in the load handler causes
+ * problems.
+ */
+ PipeDevice* dev = ((AndroidPipeState*)opaque)->dev;
+ if (dev->wanted_pipes_first) {
+ qemu_set_irq(dev->ps->irq, 1);
+ } else {
+ qemu_set_irq(dev->ps->irq, 0);
+ }
+}
+
static void android_pipe_realize(DeviceState *dev, Error **errp)
{
SysBusDevice *sbdev = SYS_BUS_DEVICE(dev);
@@ -780,59 +879,61 @@
s->dev = (PipeDevice *) g_malloc0(sizeof(PipeDevice));
s->dev->ps = s; /* HACK: backlink */
-
+ s->dev->wanted_pipes_first = NULL;
+ s->dev->wanted_pipe_after_channel_high = NULL;
+ s->dev->pipes_by_channel = g_hash_table_new_full(
+ hash_channel, hash_channel_equal,
+ hash_channel_destroy, NULL);
+ if (!s->dev->pipes_by_channel) {
+ APANIC("%s: failed to initialize pipes hash\n", __func__);
+ }
memory_region_init_io(&s->iomem, OBJECT(s), &android_pipe_iomem_ops, s,
- "android_pipe", 0x1000 /*TODO: ?how big?*/);
+ "android_pipe", 0x2000 /*TODO: ?how big?*/);
sysbus_init_mmio(sbdev, &s->iomem);
sysbus_init_irq(sbdev, &s->irq);
- android_zero_pipe_init();
- android_pingpong_init();
- android_throttle_init();
- android_sensors_init();
+ android_pipe_set_hw_funcs(&qemu2_android_pipe_hw_funcs);
- /* TODO: This may be a complete hack and there may be beautiful QOM ways
- * to accomplish this.
- *
- * Initialize android pipe backends
- */
- android_adb_dbg_backend_init();
-
+ register_savevm_with_post_load
+ (dev,
+ "android_pipe",
+ 0,
+ ANDROID_PIPE_SAVE_VERSION,
+ android_pipe_save,
+ android_pipe_load,
+ android_pipe_post_load,
+ s);
}
-void
-android_pipe_wake( void* hwpipe, unsigned flags )
+static void qemu2_android_pipe_reset(void* hwpipe, void* internal_pipe) {
+ HwPipe* pipe = hwpipe;
+ pipe->pipe = internal_pipe;
+}
+
+static void qemu2_android_pipe_host_signal_wake( void* hwpipe, unsigned flags )
{
- Pipe* pipe = hwpipe;
- Pipe** lookup;
+ HwPipe* pipe = hwpipe;
PipeDevice* dev = pipe->device;
DD("%s: channel=0x%llx flags=%d", __FUNCTION__, (unsigned long long)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;
+ set_pipe_wanted_bits(pipe, (unsigned char)flags);
+ wanted_pipes_add(dev, pipe);
/* Raise IRQ to indicate there are items on our list ! */
- /* android_device_set_irq(&dev->dev, 0, 1);*/
qemu_set_irq(dev->ps->irq, 1);
DD("%s: raising IRQ", __FUNCTION__);
}
-void
-android_pipe_close( void* hwpipe )
+static void qemu2_android_pipe_host_close( void* hwpipe )
{
- Pipe* pipe = hwpipe;
+ HwPipe* pipe = hwpipe;
D("%s: channel=0x%llx (closed=%d)", __FUNCTION__, (unsigned long long)pipe->channel, pipe->closed);
if (!pipe->closed) {
pipe->closed = 1;
- android_pipe_wake( hwpipe, PIPE_WAKE_CLOSED );
+ qemu2_android_pipe_host_signal_wake( hwpipe, PIPE_WAKE_CLOSED );
}
}
@@ -856,3 +957,4 @@
}
type_init(android_pipe_register);
+
diff --git a/include/hw/misc/android_pipe.h b/include/hw/misc/android_pipe.h
deleted file mode 100644
index 9afabc8..0000000
--- a/include/hw/misc/android_pipe.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (C) 2011 The Android Open Source Project
-**
-** This software is licensed under the terms of the GNU General Public
-** License version 2, as published by the Free Software Foundation, and
-** may be copied, distributed, and modified under those terms.
-**
-** This program is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-** GNU General Public License for more details.
-*/
-#ifndef _HW_ANDROID_PIPE_H
-#define _HW_ANDROID_PIPE_H
-
-#include <stdbool.h>
-#include <stdint.h>
-#include "hw/hw.h"
-
-extern bool qemu2_adb_server_init(int port);
-
-#include "android/emulation/android_pipe_device.h"
-#include "android/opengles-pipe.h"
-
-extern void android_zero_pipe_init(void);
-extern void android_pingpong_init(void);
-extern void android_throttle_init(void);
-extern void android_adb_dbg_backend_init(void);
-extern void android_adb_backend_init(void);
-extern void android_sensors_init(void);
-
-#endif /* _HW_ANDROID_PIPE_H */