blob: 2e7f2a024f3c246976f863d9a583850556591ba0 [file] [log] [blame]
/* 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
** 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.
**
** Description
**
** This device provides a virtual pipe device (originally called
** goldfish_pipe and latterly qemu_pipe). This allows the android
** running under the emulator to open a fast connection to the host
** for various purposes including the adb debug bridge and
** (eventually) the opengles pass-through. This file contains only the
** basic pipe infrastructure and a couple of test pipes. Additional
** pipes are registered with the android_pipe_add_type() call.
**
** Open Questions
**
** Since this was originally written there have been a number of other
** virtual devices added to QEMU using the virtio infrastructure. We
** 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 "qemu/error-report.h"
#include <assert.h>
#include <glib.h>
/* Set to > 0 for debug output */
#define PIPE_DEBUG 0
/* Set to 1 to debug i/o register reads/writes */
#define PIPE_DEBUG_REGS 0
#if PIPE_DEBUG >= 1
#define D(fmt, ...) \
do { fprintf(stdout, "android_pipe: " fmt "\n", ## __VA_ARGS__); } while (0)
#else
#define D(fmt, ...) do { /* nothing */ } while (0)
#endif
#if PIPE_DEBUG >= 2
#define DD(fmt, ...) \
do { fprintf(stdout, "android_pipe: " fmt "\n", ## __VA_ARGS__); } while (0)
#else
#define DD(fmt, ...) do { /* nothing */ } while (0)
#endif
#if PIPE_DEBUG_REGS >= 1
# define DR(...) D(__VA_ARGS__)
#else
# define DR(...) do { /* nothing */ } while (0)
#endif
#define E(fmt, ...) \
do { fprintf(stdout, "ERROR:" fmt "\n", ## __VA_ARGS__); } while (0)
#define APANIC(...) \
do { \
error_report(__VA_ARGS__); \
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
/* from AOSP version include/hw/android/goldfish/device.h
* FIXME?: needs to use proper qemu abstractions
*/
static inline void uint64_set_low(uint64_t *addr, uint32 value)
{
*addr = (*addr & ~(0xFFFFFFFFULL)) | value;
}
static inline void uint64_set_high(uint64_t *addr, uint32 value)
{
*addr = (*addr & 0xFFFFFFFFULL) | ((uint64_t)value << 32);
}
#define TYPE_ANDROID_PIPE "android_pipe"
#define ANDROID_PIPE(obj) \
OBJECT_CHECK(AndroidPipeState, (obj), TYPE_ANDROID_PIPE)
typedef struct PipeDevice PipeDevice;
typedef struct {
SysBusDevice parent;
MemoryRegion iomem;
qemu_irq irq;
/* TODO: roll into shared state */
PipeDevice *dev;
} AndroidPipeState;
/***********************************************************************
***********************************************************************
*****
***** P I P E C O N N E C T I O N S
*****
*****/
typedef struct HwPipe {
struct HwPipe *next;
struct HwPipe *wanted_next;
struct HwPipe *wanted_prev;
PipeDevice *device;
uint64_t channel; /* opaque kernel handle */
unsigned char wanted;
char closed;
void *pipe;
} HwPipe;
static HwPipe* hwpipe_new0(struct PipeDevice* device) {
HwPipe* hwp;
ANEW0(hwp);
hwp->device = device;
return hwp;
}
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)
{
HwPipe* pipe;
pipe = g_malloc0(sizeof(HwPipe));
pipe->device = dev;
return pipe;
}
static HwPipe*
pipe_new(uint64_t channel, PipeDevice* dev)
{
HwPipe* pipe = pipe_new0(dev);
pipe->channel = channel;
pipe->pipe = android_pipe_guest_open(pipe);
return pipe;
}
static void pipe_free(HwPipe* pipe)
{
android_pipe_guest_close(pipe->pipe);
g_free(pipe);
}
/***********************************************************************
***********************************************************************
*****
***** 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
// 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;
// Cache of the pipes by channel for a faster lookup.
GHashTable* pipes_by_channel;
// i/o registers
uint64_t address;
uint32_t size;
uint32_t status;
uint64_t channel;
uint32_t wakes;
uint64_t params_addr;
};
// 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 paddr doesn't actually point at RAM).
* Note that for RAM the "mapping" process doesn't actually involve a
* data copy.
*/
static void *map_guest_buffer(hwaddr phys, size_t size, int is_write)
{
hwaddr l = size;
void *ptr;
ptr = cpu_physical_memory_map(phys, &l, is_write);
if (!ptr) {
/* Can't happen for RAM */
return NULL;
}
if (l != size) {
/* This will only happen if the address pointed at non-RAM,
* or if the size means the buffer end is beyond the end of
* the RAM block.
*/
cpu_physical_memory_unmap(ptr, l, 0, 0);
return NULL;
}
return ptr;
}
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) {
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%llx", __FUNCTION__, (unsigned long long)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;
g_hash_table_insert(dev->pipes_by_channel,
hash_create_key_from_channel(dev->channel), pipe);
break;
case PIPE_CMD_CLOSE: {
DD("%s: CMD_CLOSE channel=0x%llx", __FUNCTION__, (unsigned long long)dev->channel);
// 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;
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 = android_pipe_guest_poll(pipe->pipe);
DD("%s: CMD_POLL > status=%d", __FUNCTION__, dev->status);
break;
case PIPE_CMD_READ_BUFFER: {
/* Translate guest physical address into emulator memory. */
AndroidPipeBuffer buffer;
buffer.data = map_guest_buffer(dev->address, dev->size, 1);
if (!buffer.data) {
dev->status = PIPE_ERROR_INVAL;
break;
}
buffer.size = dev->size;
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);
cpu_physical_memory_unmap(buffer.data, dev->size, 1, dev->size);
break;
}
case PIPE_CMD_WRITE_BUFFER: {
/* Translate guest physical address into emulator memory. */
AndroidPipeBuffer buffer;
buffer.data = map_guest_buffer(dev->address, dev->size, 0);
if (!buffer.data) {
dev->status = PIPE_ERROR_INVAL;
break;
}
buffer.size = dev->size;
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);
cpu_physical_memory_unmap(buffer.data, dev->size, 0, dev->size);
break;
}
case PIPE_CMD_WAKE_ON_READ:
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;
android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
}
dev->status = 0;
break;
case PIPE_CMD_WAKE_ON_WRITE:
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;
android_pipe_guest_wake_on(pipe->pipe, pipe->wanted);
}
dev->status = 0;
break;
default:
D("%s: command=%d (0x%x)\n", __FUNCTION__, command, command);
}
}
static void pipe_dev_write(void *opaque, hwaddr offset, uint64_t value, unsigned size)
{
AndroidPipeState *state = (AndroidPipeState *) opaque;
PipeDevice *s = state->dev;
DR("%s: offset = 0x%" HWADDR_PRIx " value=%" PRIu64 "/0x%" PRIx64,
__func__, offset, value, value);
switch (offset) {
case PIPE_REG_COMMAND:
pipeDevice_doCommand(s, value);
break;
case PIPE_REG_SIZE:
s->size = value;
break;
case PIPE_REG_ADDRESS:
uint64_set_low(&s->address, value);
break;
case PIPE_REG_ADDRESS_HIGH:
uint64_set_high(&s->address, value);
break;
case PIPE_REG_CHANNEL:
uint64_set_low(&s->channel, value);
break;
case PIPE_REG_CHANNEL_HIGH:
uint64_set_high(&s->channel, value);
break;
case PIPE_REG_PARAMS_ADDR_HIGH:
uint64_set_high(&s->params_addr, value);
break;
case PIPE_REG_PARAMS_ADDR_LOW:
uint64_set_low(&s->params_addr, value);
break;
case PIPE_REG_ACCESS_PARAMS:
{
union access_params aps;
uint32_t cmd;
bool is_64bit = true;
/* Don't touch aps.result if anything wrong */
if (s->params_addr == 0)
break;
cpu_physical_memory_read(s->params_addr, (void*)&aps, sizeof(aps.aps32));
/* This auto-detection of 32bit/64bit ness relies on the
* currently unused flags parameter. As the 32 bit flags
* overlaps with the 64 bit cmd parameter. As cmd != 0 if we
* find it as 0 it's 32bit
*/
if (aps.aps32.flags == 0) {
is_64bit = false;
} else {
cpu_physical_memory_read(s->params_addr, (void*)&aps, sizeof(aps.aps64));
}
if (is_64bit) {
s->channel = aps.aps64.channel;
s->size = aps.aps64.size;
s->address = aps.aps64.address;
cmd = aps.aps64.cmd;
} else {
s->channel = aps.aps32.channel;
s->size = aps.aps32.size;
s->address = aps.aps32.address;
cmd = aps.aps32.cmd;
}
if ((cmd != PIPE_CMD_READ_BUFFER) && (cmd != PIPE_CMD_WRITE_BUFFER))
break;
pipeDevice_doCommand(s, cmd);
if (is_64bit) {
aps.aps64.result = s->status;
cpu_physical_memory_write(s->params_addr, (void*)&aps, sizeof(aps.aps64));
} else {
aps.aps32.result = s->status;
cpu_physical_memory_write(s->params_addr, (void*)&aps, sizeof(aps.aps32));
}
}
break;
default:
qemu_log_mask(LOG_GUEST_ERROR, "%s: unknown register offset = 0x%"
HWADDR_PRIx " value=%" PRIu64 "/0x%" PRIx64 "\n",
__func__, offset, value, value);
break;
}
}
/* I/O read */
static uint64_t pipe_dev_read(void *opaque, hwaddr offset, unsigned size)
{
AndroidPipeState *s = (AndroidPipeState *)opaque;
PipeDevice *dev = s->dev;
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: {
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)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;
}
}
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)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;
}
}
case PIPE_REG_WAKES:
DR("%s: wakes %d", __FUNCTION__, dev->wakes);
return dev->wakes;
case PIPE_REG_PARAMS_ADDR_HIGH:
return (uint32_t)(dev->params_addr >> 32);
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);
}
return 0;
}
static const MemoryRegionOps android_pipe_iomem_ops = {
.read = pipe_dev_read,
.write = pipe_dev_write,
.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);
AndroidPipeState *s = ANDROID_PIPE(dev);
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", 0x2000 /*TODO: ?how big?*/);
sysbus_init_mmio(sbdev, &s->iomem);
sysbus_init_irq(sbdev, &s->irq);
android_pipe_set_hw_funcs(&qemu2_android_pipe_hw_funcs);
register_savevm_with_post_load
(dev,
"android_pipe",
0,
ANDROID_PIPE_SAVE_VERSION,
android_pipe_save,
android_pipe_load,
android_pipe_post_load,
s);
}
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 )
{
HwPipe* pipe = hwpipe;
PipeDevice* dev = pipe->device;
DD("%s: channel=0x%llx flags=%d", __FUNCTION__, (unsigned long long)pipe->channel, flags);
set_pipe_wanted_bits(pipe, (unsigned char)flags);
wanted_pipes_add(dev, pipe);
/* Raise IRQ to indicate there are items on our list ! */
qemu_set_irq(dev->ps->irq, 1);
DD("%s: raising IRQ", __FUNCTION__);
}
static void qemu2_android_pipe_host_close( void* 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;
qemu2_android_pipe_host_signal_wake( hwpipe, PIPE_WAKE_CLOSED );
}
}
static void android_pipe_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = android_pipe_realize;
dc->desc = "android pipe";
}
static const TypeInfo android_pipe_info = {
.name = TYPE_ANDROID_PIPE,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AndroidPipeState),
.class_init = android_pipe_class_init
};
static void android_pipe_register(void)
{
type_register_static(&android_pipe_info);
}
type_init(android_pipe_register);