blob: 0f9e3983b0b4e0ec7732017862b2df2d50d659cb [file] [log] [blame]
/* Copyright 2015 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.
*/
#include "android-qemu2-glue/emulation/android_pipe_device.h"
#include "android/base/Compiler.h"
#include "android/base/Log.h"
#include "android/emulation/AndroidPipe.h"
#include "android/emulation/android_pipe_common.h"
#include "android/emulation/android_pipe_device.h"
#include "android/emulation/GoldfishDma.h"
#include "android/emulation/VmLock.h"
#include "android/utils/stream.h"
#include "android-qemu2-glue/base/files/QemuFileStream.h"
#include <assert.h>
#include <memory>
extern "C" {
#include "hw/misc/goldfish_pipe.h"
} // extern "C"
// Technical note: This file contains the glue code used between the
// generic AndroidPipe service implementation, and the QEMU2-specific
// goldfish_pipe virtual device. More specifically:
//
// The host service pipe expects a device implementation that will
// implement the callbacks in AndroidHwPipeFuncs. These are injected
// into the service by calling android_pipe_set_hw_funcs().
//
// The virtual device expects a service implementation that will
// implement the callbacks in GoldfishPipeServiceOps. These are injected
// into the device through goldfish_pipe_set_service_ops().
//
// qemu_android_pipe_init() is used to inject all callbacks at setup time
// and initialize the threading mode of AndroidPipe (see below).
//
using android::qemu::QemuFileStream;
static ::Stream* asCStream(android::base::Stream* stream) {
return reinterpret_cast<::Stream*>(stream);
}
// These callbacks are called from the virtual device into the pipe service.
static const GoldfishPipeServiceOps goldfish_pipe_service_ops = {
// guest_open()
[](GoldfishHwPipe* hwPipe) -> GoldfishHostPipe* {
return static_cast<GoldfishHostPipe*>(
android_pipe_guest_open(hwPipe));
},
// guest_close()
[](GoldfishHostPipe* hostPipe, GoldfishPipeCloseReason reason) {
static_assert((int)GOLDFISH_PIPE_CLOSE_GRACEFUL ==
(int)PIPE_CLOSE_GRACEFUL,
"Invalid PIPE_CLOSE_GRACEFUL value");
static_assert(
(int)GOLDFISH_PIPE_CLOSE_REBOOT == (int)PIPE_CLOSE_REBOOT,
"Invalid PIPE_CLOSE_REBOOT value");
static_assert((int)GOLDFISH_PIPE_CLOSE_LOAD_SNAPSHOT ==
(int)PIPE_CLOSE_LOAD_SNAPSHOT,
"Invalid PIPE_CLOSE_LOAD_SNAPSHOT value");
static_assert(
(int)GOLDFISH_PIPE_CLOSE_ERROR == (int)PIPE_CLOSE_ERROR,
"Invalid PIPE_CLOSE_ERROR value");
android_pipe_guest_close(hostPipe,
static_cast<PipeCloseReason>(reason));
},
// guest_pre_load()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_pipe_guest_pre_load(asCStream(&stream));
},
// guest_post_load()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_pipe_guest_post_load(asCStream(&stream));
},
// guest_pre_save()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_pipe_guest_pre_save(asCStream(&stream));
},
// guest_post_save()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_pipe_guest_post_save(asCStream(&stream));
},
// guest_load()
[](QEMUFile* file,
GoldfishHwPipe* hwPipe,
char* force_close) -> GoldfishHostPipe* {
QemuFileStream stream(file);
return static_cast<GoldfishHostPipe*>(android_pipe_guest_load(
asCStream(&stream), hwPipe, force_close));
},
// guest_save()
[](GoldfishHostPipe* hostPipe, QEMUFile* file) {
QemuFileStream stream(file);
android_pipe_guest_save(hostPipe, asCStream(&stream));
},
// guest_poll()
[](GoldfishHostPipe* hostPipe) {
static_assert((int)GOLDFISH_PIPE_POLL_IN == (int)PIPE_POLL_IN,
"invalid POLL_IN values");
static_assert((int)GOLDFISH_PIPE_POLL_OUT == (int)PIPE_POLL_OUT,
"invalid POLL_OUT values");
static_assert((int)GOLDFISH_PIPE_POLL_HUP == (int)PIPE_POLL_HUP,
"invalid POLL_HUP values");
return static_cast<GoldfishPipePollFlags>(
android_pipe_guest_poll(hostPipe));
},
// guest_recv()
[](GoldfishHostPipe* hostPipe,
GoldfishPipeBuffer* buffers,
int numBuffers) -> int {
// NOTE: Assumes that AndroidPipeBuffer and GoldfishPipeBuffer
// have exactly the same layout.
static_assert(
sizeof(AndroidPipeBuffer) == sizeof(GoldfishPipeBuffer),
"Invalid PipeBuffer sizes");
// We can't use a static_assert with offsetof() because in msvc, it uses
// reinterpret_cast.
// TODO: Add runtime assertion instead?
// https://developercommunity.visualstudio.com/content/problem/22196/static-assert-cannot-compile-constexprs-method-tha.html
#ifndef _MSC_VER
static_assert(offsetof(AndroidPipeBuffer, data) ==
offsetof(GoldfishPipeBuffer, data),
"Invalid PipeBuffer::data offsets");
static_assert(offsetof(AndroidPipeBuffer, size) ==
offsetof(GoldfishPipeBuffer, size),
"Invalid PipeBuffer::size offsets");
#endif
return android_pipe_guest_recv(
hostPipe, reinterpret_cast<AndroidPipeBuffer*>(buffers),
numBuffers);
},
// guest_send()
[](GoldfishHostPipe* hostPipe,
const GoldfishPipeBuffer* buffers,
int numBuffers) -> int {
return android_pipe_guest_send(
hostPipe,
reinterpret_cast<const AndroidPipeBuffer*>(buffers),
numBuffers);
},
// guest_wake_on()
[](GoldfishHostPipe* hostPipe, GoldfishPipeWakeFlags wakeFlags) {
android_pipe_guest_wake_on(hostPipe, static_cast<int>(wakeFlags));
},
// dma_add_buffer()
[](void* pipe, uint64_t paddr, uint64_t sz) {
android_goldfish_dma_ops.add_buffer(pipe, paddr, sz);
},
// dma_remove_buffer()
[](uint64_t paddr) { android_goldfish_dma_ops.remove_buffer(paddr); },
// dma_invalidate_host_mappings()
[]() { android_goldfish_dma_ops.invalidate_host_mappings(); },
// dma_reset_host_mappings()
[]() { android_goldfish_dma_ops.reset_host_mappings(); },
// dma_save_mappings()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_goldfish_dma_ops.save_mappings(&stream);
},
// dma_load_mappings()
[](QEMUFile* file) {
QemuFileStream stream(file);
android_goldfish_dma_ops.load_mappings(&stream);
},
};
// These callbacks are called from the pipe service into the virtual device.
static const AndroidPipeHwFuncs android_pipe_hw_funcs = {
// resetPipe()
[](void* hwPipe, void* hostPipe) {
goldfish_pipe_reset(static_cast<GoldfishHwPipe*>(hwPipe),
static_cast<GoldfishHostPipe*>(hostPipe));
},
// closeFromHost()
[](void* hwPipe) {
goldfish_pipe_close_from_host(static_cast<GoldfishHwPipe*>(hwPipe));
},
// signalWake()
[](void* hwPipe, unsigned flags) {
static_assert(
(int)GOLDFISH_PIPE_WAKE_CLOSED == (int)PIPE_WAKE_CLOSED,
"Invalid PIPE_WAKE_CLOSED values");
static_assert((int)GOLDFISH_PIPE_WAKE_READ == (int)PIPE_WAKE_READ,
"Invalid PIPE_WAKE_READ values");
static_assert((int)GOLDFISH_PIPE_WAKE_WRITE == (int)PIPE_WAKE_WRITE,
"Invalid PIPE_WAKE_WRITE values");
static_assert((int)GOLDFISH_PIPE_WAKE_UNLOCK_DMA ==
(int)PIPE_WAKE_UNLOCK_DMA,
"Invalid PIPE_WAKE_WRITE values");
goldfish_pipe_signal_wake(
static_cast<GoldfishHwPipe*>(hwPipe),
static_cast<GoldfishPipeWakeFlags>(flags));
},
// getPipeId()
[](void* hwPipe) {
return goldfish_pipe_get_id(static_cast<GoldfishHwPipe*>(hwPipe));
},
// lookupPipeById()
[](int id) {
return (void*)goldfish_pipe_lookup_by_id(id);
}
};
bool qemu_android_pipe_init(android::VmLock* vmLock) {
goldfish_pipe_set_service_ops(&goldfish_pipe_service_ops);
android_pipe_set_hw_funcs(&android_pipe_hw_funcs);
android::AndroidPipe::initThreading(vmLock);
return true;
}