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 */