blob: 443086b08468e0b2b8f84cf4545f4e384f0d0c64 [file] [log] [blame]
/* Copyright (C) 2016 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 "hw/misc/goldfish_sync.h"
#include "android/utils/debug.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "qemu-common.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "trace.h"
#define DEBUG_LOG_ALL_DEVICE_OPS 0
#if DEBUG_LOG_ALL_DEVICE_OPS
#define DPRINT(...) do { \
if (!VERBOSE_CHECK(goldfishsync)) VERBOSE_ENABLE(goldfishsync); \
VERBOSE_TID_FUNCTION_DPRINT(goldfishsync, __VA_ARGS__); } while(0)
#else
#define DPRINT(...)
#endif
// Goldfish sync commands=======================================================
// Batch command formats:
// Host->guest:
struct goldfish_sync_batch_cmd {
// sorted by size for alignment
uint64_t handle;
uint64_t hostcmd_handle;
uint32_t cmd;
uint32_t time_arg;
};
// guest->host:
struct goldfish_sync_batch_guestcmd {
uint64_t host_command; // uint64_t for alignment
uint64_t glsync_handle;
uint64_t thread_handle;
uint64_t guest_timeline_handle;
};
// This is a suffix of batch cmd.
// The purpose of the "pending_cmd" variant
// is so we can queue up several batch commands
// in the same IRQ level.
struct goldfish_sync_pending_cmd {
uint64_t handle;
uint64_t hostcmd_handle;
uint32_t cmd;
uint32_t time_arg;
struct goldfish_sync_pending_cmd* next;
};
// goldfish_sync_ops contains all the host-side operations that we want
// triggered along with certain goldfish sync device commands.
// For example, trigger_wait will run a eglClientWaitSyncKHR
// on the host in a particular sync thread.
struct goldfish_sync_ops {
// Wait for the specified glsync object glsync_ptr
// in thread thread_ptr.
void (*trigger_wait)(uint64_t glsync_ptr,
uint64_t thread_ptr,
uint64_t timeline);
};
// Command numbers that can only be sent from the guest to the device.
#define SYNC_GUEST_CMD_READY 0
#define SYNC_GUEST_CMD_TRIGGER_HOST_WAIT 5
// The register layout is:
#define SYNC_REG_BATCH_COMMAND 0x00 // host->guest batch commands
#define SYNC_REG_BATCH_GUESTCOMMAND 0x04 // guest->host batch commands
#define SYNC_REG_BATCH_COMMAND_ADDR 0x08 // communicate physical address of host->guest batch commands
#define SYNC_REG_BATCH_COMMAND_ADDR_HIGH 0x0c // 64-bit part
#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR 0x10 // communicate physical address of guest->host commands
#define SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH 0x14 // 64-bit part
#define SYNC_REG_INIT 0x18 // to signal that the device has been detected by the kernel
// The goldfish sync device is represented by the goldfish_sync_state struct.
// Besides the machinery necessary to do I/O with the guest:
// |pending|: a linked list of |goldfish_sync_pending_cmd|'s
// that need to be processed by the kernel.
// |first_pending_cmd|: maintains a pointer to the earliest pending command
// that needs to be sent. We plan to process pending commands in the same
// order in which they were received.
// |current|: Intermediate value that represents a popped-off pending
// command.
// |batch_cmd_addr|: Physical memory address where we write the details
// of each command that comes in from the host. We also read replies
// from the same address (if applicable)
// |batch_guestcmd_addr|: Physical memory address where we read the
// details of each command that comes in from the guest.
// |ops|: Callbacks to other areas of the emulator that need to be
// triggered upon reading a guest->host command.
struct goldfish_sync_state {
SysBusDevice parent;
MemoryRegion iomem;
qemu_irq irq;
struct goldfish_sync_pending_cmd* pending;
struct goldfish_sync_pending_cmd* first_pending_cmd;
struct goldfish_sync_pending_cmd* current;
uint64_t batch_cmd_addr;
uint64_t batch_guestcmd_addr;
struct goldfish_sync_ops* ops;
};
static struct goldfish_sync_state* s_goldfish_sync_dev;
static const GoldfishSyncServiceOps* service_ops = NULL;
void goldfish_sync_set_service_ops(const GoldfishSyncServiceOps *ops) {
service_ops = ops;
}
// Command list operations======================================================
static struct goldfish_sync_pending_cmd*
goldfish_sync_new_cmd() {
struct goldfish_sync_pending_cmd* res;
res = g_malloc0(sizeof(struct goldfish_sync_pending_cmd));
return res;
}
static void
goldfish_sync_free_cmd(struct goldfish_sync_pending_cmd* cmd) {
g_free(cmd);
}
static void
goldfish_sync_push_cmd(struct goldfish_sync_state* state,
struct goldfish_sync_pending_cmd* cmd) {
cmd->next = NULL;
if (state->pending) {
state->pending->next = cmd;
}
// We need to reset first_pending_cmd if
// the list was empty to start with.
if (!state->pending) {
state->first_pending_cmd = cmd;
}
state->pending = cmd;
}
static struct goldfish_sync_pending_cmd*
goldfish_sync_pop_first_cmd(struct goldfish_sync_state* state) {
struct goldfish_sync_pending_cmd* popped =
state->first_pending_cmd;
// Update list structure as follows:
// 1. Don't do anything if its already empty.
if (!popped) return NULL;
// 2. Update first_pending_cmd and pending fields,
// changing list structure to reflect the change.
state->first_pending_cmd = popped->next;
if (state->pending == popped) state->pending = NULL;
return popped;
}
static void goldfish_sync_clear_pending(struct goldfish_sync_state* state) {
struct goldfish_sync_pending_cmd* curr =
state->pending;
while (curr) {
struct goldfish_sync_pending_cmd* tmp = curr;
curr = curr->next;
goldfish_sync_free_cmd(tmp);
}
state->pending = NULL;
state->first_pending_cmd = NULL;
}
static void goldfish_sync_reset_device(struct goldfish_sync_state* state) {
goldfish_sync_clear_pending(state);
qemu_set_irq(state->irq, 0);
}
// Callbacks and hw_funcs struct================================================
// goldfish_sync_send_command does the actual work of setting up
// the sync device registers, causing IRQ blip,
// and waiting for + reading back results from the guest
// (if appplicable)
void goldfish_sync_send_command(uint32_t cmd,
uint64_t handle,
uint32_t time_arg,
uint64_t hostcmd_handle) {
struct goldfish_sync_state* s;
struct goldfish_sync_pending_cmd* to_send;
s = s_goldfish_sync_dev;
DPRINT("incoming cmd. "
"cmd=%u "
"handle=0x%llx "
"time_arg=%u "
"hostcmd_handle=0x%llx",
cmd, handle, time_arg, hostcmd_handle);
to_send = goldfish_sync_new_cmd();
to_send->cmd = cmd;
to_send->handle = handle;
to_send->time_arg = time_arg;
to_send->hostcmd_handle = hostcmd_handle;
goldfish_sync_push_cmd(s, to_send);
qemu_set_irq(s->irq, 1);
DPRINT("Exit");
}
#define TYPE_GOLDFISH_SYNC "goldfish_sync"
#define GOLDFISH_SYNC(obj) \
OBJECT_CHECK(struct goldfish_sync_state, (obj), TYPE_GOLDFISH_SYNC)
static uint64_t
goldfish_sync_read(void *opaque,
hwaddr offset,
unsigned size) {
DPRINT("opaque=%p offset=0x%lx sz=0x%x", opaque, offset, size);
struct goldfish_sync_state* s = opaque;
struct goldfish_sync_batch_cmd* curr_batch_cmd;
switch (offset) {
// SYNC_REG_BATCH_COMMAND read:
// This is triggered by the kernel whenever we are processing
// any host->guest sync command, such as timeline increment.
// The role of this part is to pass command info (in the struct
// |goldfish_sync_batch_cmd|'s to the guest where the commands
// are actually run.
//
// SYNC_REG_BATCH_COMMAND is the first thing
// to happen on a IRQ level raise, and will continue re-issuing
// throughout IRQ active high, until all pending commands on the
// host (the linked list) are processed. At that point, IRQ
// is lowered.
case SYNC_REG_BATCH_COMMAND:
s->current = goldfish_sync_pop_first_cmd(s);
if (!s->current) {
DPRINT("Out of pending commands. Lower IRQ.");
struct goldfish_sync_batch_cmd quit_cmd = {
.cmd = 0, .handle = 0, .time_arg = 0, .hostcmd_handle = 0
};
cpu_physical_memory_write(s->batch_cmd_addr,
(void*)&quit_cmd,
sizeof(struct goldfish_sync_batch_cmd));
qemu_set_irq(s->irq, 0);
return 0;
} else {
// goldfish_sync_batch_cmd is a prefix of
// goldfish_sync_pending_cmd, so we can avoid a copy.
curr_batch_cmd = (struct goldfish_sync_batch_cmd*)(s->current);
DPRINT("read SYNC_REG_BATCH_COMMAND. writing to batch addr: "
"cmd=%u handle=0x%llx time_arg=%u hostcmd_handle=0x%llx",
curr_batch_cmd->cmd,
curr_batch_cmd->handle,
curr_batch_cmd->time_arg,
curr_batch_cmd->hostcmd_handle);
cpu_physical_memory_write(s->batch_cmd_addr,
(void*)curr_batch_cmd,
sizeof(struct goldfish_sync_batch_cmd));
goldfish_sync_free_cmd(s->current);
s->current = NULL;
}
DPRINT("done SYNC_REG_BATCH_COMMAND. returning result val?");
return 0;
// SYNC_REG_BATCH_(GUEST)COMMAND_ADDR(_HIGH) read:
// Used solely to verify that the |s->batch_cmd_addr| on the host
// is the same physical address as the corresponding one
// on the guest.
case SYNC_REG_BATCH_COMMAND_ADDR:
return (uint64_t)(uint32_t)(s->batch_cmd_addr);
case SYNC_REG_BATCH_COMMAND_ADDR_HIGH:
return (uint64_t)(uint32_t)(s->batch_cmd_addr >> 32);
case SYNC_REG_BATCH_GUESTCOMMAND_ADDR:
return (uint64_t)(uint32_t)(s->batch_guestcmd_addr);
case SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH:
return (uint64_t)(uint32_t)(s->batch_guestcmd_addr >> 32);
default:
DPRINT("INVALID sync_read! reg=%u", offset);
assert(false);
return 0;
}
return 0;
}
// The guest kernel's goldfish_sync driver triggers goldfish_sync_read
// and goldfish_sync_write. The only purpose of those two functions is to
// pass the right value to and from the special |regs| field of the
// goldfish_sync_state.
static void
goldfish_sync_write(void *opaque,
hwaddr offset,
uint64_t val,
unsigned size) {
DPRINT("opaque=%p "
"offset=0x%lx "
"sz=0x%x "
"val=%" PRIu64,
opaque, offset, size, val);
struct goldfish_sync_state* s = opaque;
switch (offset) {
// SYNC_REG_BATCH_COMMAND write: Used to send acknowledgements
// to host->guest commands. Some commands that can be issued from
// the host, such as |goldfish_sync_create_timeline|, require a
// return value of the timeline pointer itself. Not all commands
// use this; for example, we never acknowledge any
// |goldfish_sync_timeline_inc| commands because that would
// decrease performance.
case SYNC_REG_BATCH_COMMAND:
DPRINT("write SYNC_REG_BATCH_COMMAND. obtaining batch cmd vals.");
struct goldfish_sync_batch_cmd incoming = {
.cmd = 0,
.handle = 0,
.time_arg = 0,
.hostcmd_handle = 0,
};
cpu_physical_memory_read(s->batch_cmd_addr,
(void*)&incoming,
sizeof(struct goldfish_sync_batch_cmd));
DPRINT("got: cmd=%u handle=0x%llx time_arg=%u hostcmd_handle=0x%llx",
incoming.cmd,
incoming.handle,
incoming.time_arg,
incoming.hostcmd_handle);
service_ops->receive_hostcmd_result(
incoming.cmd,
incoming.handle,
incoming.time_arg,
incoming.hostcmd_handle);
break;
// SYNC_REG_BATCH_GUESTCOMMAND write: Used to send
// guest->host commands. Currently, the only guest->host command
// that matters is SYNC_GUEST_CMD_TRIGGER_HOST_WAIT, which is used
// to cause a OpenGL client wait on the host GPU/CPU.
case SYNC_REG_BATCH_GUESTCOMMAND:
DPRINT("write SYNC_REG_BATCH_GUESTCOMMAND. obtaining batch cmd vals.");
struct goldfish_sync_batch_guestcmd guest_incoming = {
.host_command = 0,
.glsync_handle = 0,
.thread_handle = 0,
.guest_timeline_handle = 0,
};
cpu_physical_memory_read(s->batch_guestcmd_addr,
(void*)&guest_incoming,
sizeof(struct goldfish_sync_batch_guestcmd));
DPRINT("got: cmd=%llu glsync=0x%llx thread=0x%llx timeline=0x%llx",
guest_incoming.host_command,
guest_incoming.glsync_handle,
guest_incoming.thread_handle,
guest_incoming.guest_timeline_handle);
switch (guest_incoming.host_command) {
case SYNC_GUEST_CMD_TRIGGER_HOST_WAIT:
DPRINT("exec SYNC_GUEST_CMD_TRIGGER_HOST_WAIT");
service_ops->trigger_host_wait(guest_incoming.glsync_handle,
guest_incoming.thread_handle,
guest_incoming.guest_timeline_handle);
break;
case SYNC_GUEST_CMD_READY:
DPRINT("exec SYNC_GUEST_CMD_READY");
break;
}
break;
// SYNC_REG_BATCH_(GUEST)COMMAND_ADDR(_HIGH) write:
// Used to communicate the physical address on the guest where
// we write all the info for commands.
case SYNC_REG_BATCH_COMMAND_ADDR:
s->batch_cmd_addr = val;
break;
case SYNC_REG_BATCH_COMMAND_ADDR_HIGH:
s->batch_cmd_addr |= (uint64_t)(val << 32);
DPRINT("Got batch command address. addr=0x%llx",
s->batch_cmd_addr);
break;
case SYNC_REG_BATCH_GUESTCOMMAND_ADDR:
s->batch_guestcmd_addr = val;
break;
case SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH:
s->batch_guestcmd_addr |= (uint64_t)(val << 32);
DPRINT("Got batch guestcommand address. addr=0x%llx",
s->batch_guestcmd_addr);
break;
case SYNC_REG_INIT:
goldfish_sync_reset_device(s);
break;
default:
DPRINT("INVALID sync_write! reg=%u", offset);
assert(false);
}
}
static const MemoryRegionOps goldfish_sync_iomem_ops = {
.read = goldfish_sync_read,
.write = goldfish_sync_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.impl.min_access_size = 4,
.impl.max_access_size = 4
};
// goldfish_sync_realize is called on startup if the sync device is enabled.
// It does the work of setting up the I/O, command queue, and function pointers.
static void goldfish_sync_realize(DeviceState *dev, Error **errp) {
DPRINT("enter");
SysBusDevice* sbdev = SYS_BUS_DEVICE(dev);
struct goldfish_sync_state* s = GOLDFISH_SYNC(dev);
s_goldfish_sync_dev = s;
s->pending = NULL;
s->current = NULL;
memory_region_init_io(&s->iomem, OBJECT(s),
&goldfish_sync_iomem_ops,
s,
"goldfish_sync",
0x2000);
sysbus_init_mmio(sbdev, &s->iomem);
sysbus_init_irq(sbdev, &s->irq);
}
static Property goldfish_sync_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
static void goldfish_sync_class_init(ObjectClass *klass, void *data)
{
DPRINT("enter");
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = goldfish_sync_realize;
dc->desc = "goldfish sync";
dc->props = goldfish_sync_properties;
}
static const TypeInfo goldfish_sync_info = {
.name = TYPE_GOLDFISH_SYNC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct goldfish_sync_state),
.class_init = goldfish_sync_class_init,
};
static void goldfish_sync_register(void)
{
DPRINT("enter");
type_register_static(&goldfish_sync_info);
}
type_init(goldfish_sync_register);