| /* |
| * Copyright (C) 2016 Google, Inc. |
| * |
| * 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 <linux/fdtable.h> |
| #include <linux/file.h> |
| #include <linux/init.h> |
| #include <linux/miscdevice.h> |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/platform_device.h> |
| |
| #include <linux/interrupt.h> |
| #include <linux/kref.h> |
| #include <linux/spinlock.h> |
| #include <linux/types.h> |
| |
| #include <linux/io.h> |
| #include <linux/mm.h> |
| #include <linux/acpi.h> |
| |
| #include <linux/string.h> |
| |
| #include <linux/fs.h> |
| #include <linux/syscalls.h> |
| #include <linux/sync_file.h> |
| #include <linux/fence.h> |
| |
| #include "goldfish_sync_timeline_fence.h" |
| |
| #define ERR(...) printk(KERN_ERR __VA_ARGS__); |
| |
| #define INFO(...) printk(KERN_INFO __VA_ARGS__); |
| |
| #define DPRINT(...) pr_debug(__VA_ARGS__); |
| |
| #define DTRACE() DPRINT("%s: enter", __func__) |
| |
| /* The Goldfish sync driver is designed to provide a interface |
| * between the underlying host's sync device and the kernel's |
| * fence sync framework.. |
| * The purpose of the device/driver is to enable lightweight |
| * creation and signaling of timelines and fences |
| * in order to synchronize the guest with host-side graphics events. |
| * |
| * Each time the interrupt trips, the driver |
| * may perform a sync operation. |
| */ |
| |
| /* The operations are: */ |
| |
| /* Ready signal - used to mark when irq should lower */ |
| #define CMD_SYNC_READY 0 |
| |
| /* Create a new timeline. writes timeline handle */ |
| #define CMD_CREATE_SYNC_TIMELINE 1 |
| |
| /* Create a fence object. reads timeline handle and time argument. |
| * Writes fence fd to the SYNC_REG_HANDLE register. */ |
| #define CMD_CREATE_SYNC_FENCE 2 |
| |
| /* Increments timeline. reads timeline handle and time argument */ |
| #define CMD_SYNC_TIMELINE_INC 3 |
| |
| /* Destroys a timeline. reads timeline handle */ |
| #define CMD_DESTROY_SYNC_TIMELINE 4 |
| |
| /* Starts a wait on the host with |
| * the given glsync object and sync thread handle. */ |
| #define 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 /* signals that the device has been probed */ |
| |
| /* There is an ioctl associated with goldfish sync driver. |
| * Make it conflict with ioctls that are not likely to be used |
| * in the emulator. |
| * |
| * '@' 00-0F linux/radeonfb.h conflict! |
| * '@' 00-0F drivers/video/aty/aty128fb.c conflict! |
| */ |
| #define GOLDFISH_SYNC_IOC_MAGIC '@' |
| |
| #define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info) |
| |
| /* The above definitions (command codes, register layout, ioctl definitions) |
| * need to be in sync with the following files: |
| * |
| * Host-side (emulator): |
| * external/qemu/android/emulation/goldfish_sync.h |
| * external/qemu-android/hw/misc/goldfish_sync.c |
| * |
| * Guest-side (system image): |
| * device/generic/goldfish-opengl/system/egl/goldfish_sync.h |
| * device/generic/goldfish/ueventd.ranchu.rc |
| * platform/build/target/board/generic/sepolicy/file_contexts |
| */ |
| struct goldfish_sync_hostcmd { |
| /* sorted for alignment */ |
| uint64_t handle; |
| uint64_t hostcmd_handle; |
| uint32_t cmd; |
| uint32_t time_arg; |
| }; |
| |
| struct goldfish_sync_guestcmd { |
| uint64_t host_command; /* uint64_t for alignment */ |
| uint64_t glsync_handle; |
| uint64_t thread_handle; |
| uint64_t guest_timeline_handle; |
| }; |
| |
| #define GOLDFISH_SYNC_MAX_CMDS 32 |
| |
| struct goldfish_sync_state { |
| char __iomem *reg_base; |
| int irq; |
| |
| /* Spinlock protects |to_do| / |to_do_end|. */ |
| spinlock_t lock; |
| /* |mutex_lock| protects all concurrent access |
| * to timelines for both kernel and user space. */ |
| struct mutex mutex_lock; |
| |
| /* Buffer holding commands issued from host. */ |
| struct goldfish_sync_hostcmd to_do[GOLDFISH_SYNC_MAX_CMDS]; |
| uint32_t to_do_end; |
| |
| /* Addresses for the reading or writing |
| * of individual commands. The host can directly write |
| * to |batch_hostcmd| (and then this driver immediately |
| * copies contents to |to_do|). This driver either replies |
| * through |batch_hostcmd| or simply issues a |
| * guest->host command through |batch_guestcmd|. |
| */ |
| struct goldfish_sync_hostcmd *batch_hostcmd; |
| struct goldfish_sync_guestcmd *batch_guestcmd; |
| |
| /* Used to give this struct itself to a work queue |
| * function for executing actual sync commands. */ |
| struct work_struct work_item; |
| }; |
| |
| static struct goldfish_sync_state global_sync_state[1]; |
| |
| struct goldfish_sync_timeline_obj { |
| struct goldfish_sync_timeline *sync_tl; |
| uint32_t current_time; |
| /* We need to be careful about when we deallocate |
| * this |goldfish_sync_timeline_obj| struct. |
| * In order to ensure proper cleanup, we need to |
| * consider the triggered host-side wait that may |
| * still be in flight when the guest close()'s a |
| * goldfish_sync device's sync context fd (and |
| * destroys the |sync_tl| field above). |
| * The host-side wait may raise IRQ |
| * and tell the kernel to increment the timeline _after_ |
| * the |sync_tl| has already been set to null. |
| * |
| * From observations on OpenGL apps and CTS tests, this |
| * happens at some very low probability upon context |
| * destruction or process close, but it does happen |
| * and it needs to be handled properly. Otherwise, |
| * if we clean up the surrounding |goldfish_sync_timeline_obj| |
| * too early, any |handle| field of any host->guest command |
| * might not even point to a null |sync_tl| field, |
| * but to garbage memory or even a reclaimed |sync_tl|. |
| * If we do not count such "pending waits" and kfree the object |
| * immediately upon |goldfish_sync_timeline_destroy|, |
| * we might get mysterous RCU stalls after running a long |
| * time because the garbage memory that is being read |
| * happens to be interpretable as a |spinlock_t| struct |
| * that is currently in the locked state. |
| * |
| * To track when to free the |goldfish_sync_timeline_obj| |
| * itself, we maintain a kref. |
| * The kref essentially counts the timeline itself plus |
| * the number of waits in flight. kref_init/kref_put |
| * are issued on |
| * |goldfish_sync_timeline_create|/|goldfish_sync_timeline_destroy| |
| * and kref_get/kref_put are issued on |
| * |goldfish_sync_fence_create|/|goldfish_sync_timeline_inc|. |
| * |
| * The timeline is destroyed after reference count |
| * reaches zero, which would happen after |
| * |goldfish_sync_timeline_destroy| and all pending |
| * |goldfish_sync_timeline_inc|'s are fulfilled. |
| * |
| * NOTE (1): We assume that |fence_create| and |
| * |timeline_inc| calls are 1:1, otherwise the kref scheme |
| * will not work. This is a valid assumption as long |
| * as the host-side virtual device implementation |
| * does not insert any timeline increments |
| * that we did not trigger from here. |
| * |
| * NOTE (2): The use of kref by itself requires no locks, |
| * but this does not mean everything works without locks. |
| * Related timeline operations do require a lock of some sort, |
| * or at least are not proven to work without it. |
| * In particualr, we assume that all the operations |
| * done on the |kref| field above are done in contexts where |
| * |global_sync_state->mutex_lock| is held. Do not |
| * remove that lock until everything is proven to work |
| * without it!!! */ |
| struct kref kref; |
| }; |
| |
| /* We will call |delete_timeline_obj| when the last reference count |
| * of the kref is decremented. This deletes the sync |
| * timeline object along with the wrapper itself. */ |
| static void delete_timeline_obj(struct kref* kref) { |
| struct goldfish_sync_timeline_obj* obj = |
| container_of(kref, struct goldfish_sync_timeline_obj, kref); |
| |
| goldfish_sync_timeline_put_internal(obj->sync_tl); |
| obj->sync_tl = NULL; |
| kfree(obj); |
| } |
| |
| static uint64_t gensym_ctr; |
| static void gensym(char *dst) |
| { |
| sprintf(dst, "goldfish_sync:gensym:%llu", gensym_ctr); |
| gensym_ctr++; |
| } |
| |
| /* |goldfish_sync_timeline_create| assumes that |global_sync_state->mutex_lock| |
| * is held. */ |
| static struct goldfish_sync_timeline_obj* |
| goldfish_sync_timeline_create(void) |
| { |
| |
| char timeline_name[256]; |
| struct goldfish_sync_timeline *res_sync_tl = NULL; |
| struct goldfish_sync_timeline_obj *res; |
| |
| DTRACE(); |
| |
| gensym(timeline_name); |
| |
| res_sync_tl = goldfish_sync_timeline_create_internal(timeline_name); |
| if (!res_sync_tl) { |
| ERR("Failed to create goldfish_sw_sync timeline."); |
| return NULL; |
| } |
| |
| res = kzalloc(sizeof(struct goldfish_sync_timeline_obj), GFP_KERNEL); |
| res->sync_tl = res_sync_tl; |
| res->current_time = 0; |
| kref_init(&res->kref); |
| |
| DPRINT("new timeline_obj=0x%p", res); |
| return res; |
| } |
| |
| /* |goldfish_sync_fence_create| assumes that |global_sync_state->mutex_lock| |
| * is held. */ |
| static int |
| goldfish_sync_fence_create(struct goldfish_sync_timeline_obj *obj, |
| uint32_t val) |
| { |
| |
| int fd; |
| char fence_name[256]; |
| struct sync_pt *syncpt = NULL; |
| struct sync_file *sync_file_obj = NULL; |
| struct goldfish_sync_timeline *tl; |
| |
| DTRACE(); |
| |
| if (!obj) return -1; |
| |
| tl = obj->sync_tl; |
| |
| syncpt = goldfish_sync_pt_create_internal( |
| tl, sizeof(struct sync_pt) + 4, val); |
| if (!syncpt) { |
| ERR("could not create sync point! " |
| "goldfish_sync_timeline=0x%p val=%d", |
| tl, val); |
| return -1; |
| } |
| |
| fd = get_unused_fd_flags(O_CLOEXEC); |
| if (fd < 0) { |
| ERR("could not get unused fd for sync fence. " |
| "errno=%d", fd); |
| goto err_cleanup_pt; |
| } |
| |
| gensym(fence_name); |
| |
| sync_file_obj = sync_file_create(&syncpt->base); |
| if (!sync_file_obj) { |
| ERR("could not create sync fence! " |
| "goldfish_sync_timeline=0x%p val=%d sync_pt=0x%p", |
| tl, val, syncpt); |
| goto err_cleanup_fd_pt; |
| } |
| |
| DPRINT("installing sync fence into fd %d sync_file_obj=0x%p", |
| fd, sync_file_obj); |
| fd_install(fd, sync_file_obj->file); |
| kref_get(&obj->kref); |
| |
| return fd; |
| |
| err_cleanup_fd_pt: |
| put_unused_fd(fd); |
| err_cleanup_pt: |
| fence_put(&syncpt->base); |
| return -1; |
| } |
| |
| /* |goldfish_sync_timeline_inc| assumes that |global_sync_state->mutex_lock| |
| * is held. */ |
| static void |
| goldfish_sync_timeline_inc(struct goldfish_sync_timeline_obj *obj, uint32_t inc) |
| { |
| DTRACE(); |
| /* Just give up if someone else nuked the timeline. |
| * Whoever it was won't care that it doesn't get signaled. */ |
| if (!obj) return; |
| |
| DPRINT("timeline_obj=0x%p", obj); |
| goldfish_sync_timeline_signal_internal(obj->sync_tl, inc); |
| DPRINT("incremented timeline. increment max_time"); |
| obj->current_time += inc; |
| |
| /* Here, we will end up deleting the timeline object if it |
| * turns out that this call was a pending increment after |
| * |goldfish_sync_timeline_destroy| was called. */ |
| kref_put(&obj->kref, delete_timeline_obj); |
| DPRINT("done"); |
| } |
| |
| /* |goldfish_sync_timeline_destroy| assumes |
| * that |global_sync_state->mutex_lock| is held. */ |
| static void |
| goldfish_sync_timeline_destroy(struct goldfish_sync_timeline_obj *obj) |
| { |
| DTRACE(); |
| /* See description of |goldfish_sync_timeline_obj| for why we |
| * should not immediately destroy |obj| */ |
| kref_put(&obj->kref, delete_timeline_obj); |
| } |
| |
| static inline void |
| goldfish_sync_cmd_queue(struct goldfish_sync_state *sync_state, |
| uint32_t cmd, |
| uint64_t handle, |
| uint32_t time_arg, |
| uint64_t hostcmd_handle) |
| { |
| struct goldfish_sync_hostcmd *to_add; |
| |
| DTRACE(); |
| |
| BUG_ON(sync_state->to_do_end == GOLDFISH_SYNC_MAX_CMDS); |
| |
| to_add = &sync_state->to_do[sync_state->to_do_end]; |
| |
| to_add->cmd = cmd; |
| to_add->handle = handle; |
| to_add->time_arg = time_arg; |
| to_add->hostcmd_handle = hostcmd_handle; |
| |
| sync_state->to_do_end += 1; |
| } |
| |
| static inline void |
| goldfish_sync_hostcmd_reply(struct goldfish_sync_state *sync_state, |
| uint32_t cmd, |
| uint64_t handle, |
| uint32_t time_arg, |
| uint64_t hostcmd_handle) |
| { |
| unsigned long irq_flags; |
| struct goldfish_sync_hostcmd *batch_hostcmd = |
| sync_state->batch_hostcmd; |
| |
| DTRACE(); |
| |
| spin_lock_irqsave(&sync_state->lock, irq_flags); |
| |
| batch_hostcmd->cmd = cmd; |
| batch_hostcmd->handle = handle; |
| batch_hostcmd->time_arg = time_arg; |
| batch_hostcmd->hostcmd_handle = hostcmd_handle; |
| writel(0, sync_state->reg_base + SYNC_REG_BATCH_COMMAND); |
| |
| spin_unlock_irqrestore(&sync_state->lock, irq_flags); |
| } |
| |
| static inline void |
| goldfish_sync_send_guestcmd(struct goldfish_sync_state *sync_state, |
| uint32_t cmd, |
| uint64_t glsync_handle, |
| uint64_t thread_handle, |
| uint64_t timeline_handle) |
| { |
| unsigned long irq_flags; |
| struct goldfish_sync_guestcmd *batch_guestcmd = |
| sync_state->batch_guestcmd; |
| |
| DTRACE(); |
| |
| spin_lock_irqsave(&sync_state->lock, irq_flags); |
| |
| batch_guestcmd->host_command = (uint64_t)cmd; |
| batch_guestcmd->glsync_handle = (uint64_t)glsync_handle; |
| batch_guestcmd->thread_handle = (uint64_t)thread_handle; |
| batch_guestcmd->guest_timeline_handle = (uint64_t)timeline_handle; |
| writel(0, sync_state->reg_base + SYNC_REG_BATCH_GUESTCOMMAND); |
| |
| spin_unlock_irqrestore(&sync_state->lock, irq_flags); |
| } |
| |
| /* |goldfish_sync_interrupt| handles IRQ raises from the virtual device. |
| * In the context of OpenGL, this interrupt will fire whenever we need |
| * to signal a fence fd in the guest, with the command |
| * |CMD_SYNC_TIMELINE_INC|. |
| * However, because this function will be called in an interrupt context, |
| * it is necessary to do the actual work of signaling off of interrupt context. |
| * The shared work queue is used for this purpose. At the end when |
| * all pending commands are intercepted by the interrupt handler, |
| * we call |schedule_work|, which will later run the actual |
| * desired sync command in |goldfish_sync_work_item_fn|. |
| */ |
| static irqreturn_t goldfish_sync_interrupt(int irq, void *dev_id) |
| { |
| |
| struct goldfish_sync_state *sync_state = dev_id; |
| |
| uint32_t nextcmd; |
| uint32_t command_r; |
| uint64_t handle_rw; |
| uint32_t time_r; |
| uint64_t hostcmd_handle_rw; |
| |
| int count = 0; |
| |
| DTRACE(); |
| |
| sync_state = dev_id; |
| |
| spin_lock(&sync_state->lock); |
| |
| for (;;) { |
| |
| readl(sync_state->reg_base + SYNC_REG_BATCH_COMMAND); |
| nextcmd = sync_state->batch_hostcmd->cmd; |
| |
| if (nextcmd == 0) |
| break; |
| |
| command_r = nextcmd; |
| handle_rw = sync_state->batch_hostcmd->handle; |
| time_r = sync_state->batch_hostcmd->time_arg; |
| hostcmd_handle_rw = sync_state->batch_hostcmd->hostcmd_handle; |
| |
| goldfish_sync_cmd_queue( |
| sync_state, |
| command_r, |
| handle_rw, |
| time_r, |
| hostcmd_handle_rw); |
| |
| count++; |
| } |
| |
| spin_unlock(&sync_state->lock); |
| |
| schedule_work(&sync_state->work_item); |
| |
| return (count == 0) ? IRQ_NONE : IRQ_HANDLED; |
| } |
| |
| /* |goldfish_sync_work_item_fn| does the actual work of servicing |
| * host->guest sync commands. This function is triggered whenever |
| * the IRQ for the goldfish sync device is raised. Once it starts |
| * running, it grabs the contents of the buffer containing the |
| * commands it needs to execute (there may be multiple, because |
| * our IRQ is active high and not edge triggered), and then |
| * runs all of them one after the other. |
| */ |
| static void goldfish_sync_work_item_fn(struct work_struct *input) |
| { |
| |
| struct goldfish_sync_state *sync_state; |
| int sync_fence_fd; |
| |
| struct goldfish_sync_timeline_obj *timeline; |
| uint64_t timeline_ptr; |
| |
| uint64_t hostcmd_handle; |
| |
| uint32_t cmd; |
| uint64_t handle; |
| uint32_t time_arg; |
| |
| struct goldfish_sync_hostcmd *todo; |
| uint32_t todo_end; |
| |
| unsigned long irq_flags; |
| |
| struct goldfish_sync_hostcmd to_run[GOLDFISH_SYNC_MAX_CMDS]; |
| uint32_t i = 0; |
| |
| sync_state = container_of(input, struct goldfish_sync_state, work_item); |
| |
| mutex_lock(&sync_state->mutex_lock); |
| |
| spin_lock_irqsave(&sync_state->lock, irq_flags); { |
| |
| todo_end = sync_state->to_do_end; |
| |
| DPRINT("num sync todos: %u", sync_state->to_do_end); |
| |
| for (i = 0; i < todo_end; i++) |
| to_run[i] = sync_state->to_do[i]; |
| |
| /* We expect that commands will come in at a slow enough rate |
| * so that incoming items will not be more than |
| * GOLDFISH_SYNC_MAX_CMDS. |
| * |
| * This is because the way the sync device is used, |
| * it's only for managing buffer data transfers per frame, |
| * with a sequential dependency between putting things in |
| * to_do and taking them out. Once a set of commands is |
| * queued up in to_do, the user of the device waits for |
| * them to be processed before queuing additional commands, |
| * which limits the rate at which commands come in |
| * to the rate at which we take them out here. |
| * |
| * We also don't expect more than MAX_CMDS to be issued |
| * at once; there is a correspondence between |
| * which buffers need swapping to the (display / buffer queue) |
| * to particular commands, and we don't expect there to be |
| * enough display or buffer queues in operation at once |
| * to overrun GOLDFISH_SYNC_MAX_CMDS. |
| */ |
| sync_state->to_do_end = 0; |
| |
| } spin_unlock_irqrestore(&sync_state->lock, irq_flags); |
| |
| for (i = 0; i < todo_end; i++) { |
| DPRINT("todo index: %u", i); |
| |
| todo = &to_run[i]; |
| |
| cmd = todo->cmd; |
| |
| handle = (uint64_t)todo->handle; |
| time_arg = todo->time_arg; |
| hostcmd_handle = (uint64_t)todo->hostcmd_handle; |
| |
| DTRACE(); |
| |
| timeline = (struct goldfish_sync_timeline_obj *)(uintptr_t)handle; |
| |
| switch (cmd) { |
| case CMD_SYNC_READY: |
| break; |
| case CMD_CREATE_SYNC_TIMELINE: |
| DPRINT("exec CMD_CREATE_SYNC_TIMELINE: " |
| "handle=0x%llx time_arg=%d", |
| handle, time_arg); |
| timeline = goldfish_sync_timeline_create(); |
| timeline_ptr = (uintptr_t)timeline; |
| goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_TIMELINE, |
| timeline_ptr, |
| 0, |
| hostcmd_handle); |
| DPRINT("sync timeline created: %p", timeline); |
| break; |
| case CMD_CREATE_SYNC_FENCE: |
| DPRINT("exec CMD_CREATE_SYNC_FENCE: " |
| "handle=0x%llx time_arg=%d", |
| handle, time_arg); |
| sync_fence_fd = goldfish_sync_fence_create(timeline, time_arg); |
| goldfish_sync_hostcmd_reply(sync_state, CMD_CREATE_SYNC_FENCE, |
| sync_fence_fd, |
| 0, |
| hostcmd_handle); |
| break; |
| case CMD_SYNC_TIMELINE_INC: |
| DPRINT("exec CMD_SYNC_TIMELINE_INC: " |
| "handle=0x%llx time_arg=%d", |
| handle, time_arg); |
| goldfish_sync_timeline_inc(timeline, time_arg); |
| break; |
| case CMD_DESTROY_SYNC_TIMELINE: |
| DPRINT("exec CMD_DESTROY_SYNC_TIMELINE: " |
| "handle=0x%llx time_arg=%d", |
| handle, time_arg); |
| goldfish_sync_timeline_destroy(timeline); |
| break; |
| } |
| DPRINT("Done executing sync command"); |
| } |
| mutex_unlock(&sync_state->mutex_lock); |
| } |
| |
| /* Guest-side interface: file operations */ |
| |
| /* Goldfish sync context and ioctl info. |
| * |
| * When a sync context is created by open()-ing the goldfish sync device, we |
| * create a sync context (|goldfish_sync_context|). |
| * |
| * Currently, the only data required to track is the sync timeline itself |
| * along with the current time, which are all packed up in the |
| * |goldfish_sync_timeline_obj| field. We use a |goldfish_sync_context| |
| * as the filp->private_data. |
| * |
| * Next, when a sync context user requests that work be queued and a fence |
| * fd provided, we use the |goldfish_sync_ioctl_info| struct, which holds |
| * information about which host handles to touch for this particular |
| * queue-work operation. We need to know about the host-side sync thread |
| * and the particular host-side GLsync object. We also possibly write out |
| * a file descriptor. |
| */ |
| struct goldfish_sync_context { |
| struct goldfish_sync_timeline_obj *timeline; |
| }; |
| |
| struct goldfish_sync_ioctl_info { |
| uint64_t host_glsync_handle_in; |
| uint64_t host_syncthread_handle_in; |
| int fence_fd_out; |
| }; |
| |
| static int goldfish_sync_open(struct inode *inode, struct file *file) |
| { |
| |
| struct goldfish_sync_context *sync_context; |
| |
| DTRACE(); |
| |
| mutex_lock(&global_sync_state->mutex_lock); |
| |
| sync_context = kzalloc(sizeof(struct goldfish_sync_context), GFP_KERNEL); |
| |
| if (sync_context == NULL) { |
| ERR("Creation of goldfish sync context failed!"); |
| mutex_unlock(&global_sync_state->mutex_lock); |
| return -ENOMEM; |
| } |
| |
| sync_context->timeline = NULL; |
| |
| file->private_data = sync_context; |
| |
| DPRINT("successfully create a sync context @0x%p", sync_context); |
| |
| mutex_unlock(&global_sync_state->mutex_lock); |
| |
| return 0; |
| } |
| |
| static int goldfish_sync_release(struct inode *inode, struct file *file) |
| { |
| |
| struct goldfish_sync_context *sync_context; |
| |
| DTRACE(); |
| |
| mutex_lock(&global_sync_state->mutex_lock); |
| |
| sync_context = file->private_data; |
| |
| if (sync_context->timeline) |
| goldfish_sync_timeline_destroy(sync_context->timeline); |
| |
| sync_context->timeline = NULL; |
| |
| kfree(sync_context); |
| |
| mutex_unlock(&global_sync_state->mutex_lock); |
| |
| return 0; |
| } |
| |
| /* |goldfish_sync_ioctl| is the guest-facing interface of goldfish sync |
| * and is used in conjunction with eglCreateSyncKHR to queue up the |
| * actual work of waiting for the EGL sync command to complete, |
| * possibly returning a fence fd to the guest. |
| */ |
| static long goldfish_sync_ioctl(struct file *file, |
| unsigned int cmd, |
| unsigned long arg) |
| { |
| struct goldfish_sync_context *sync_context_data; |
| struct goldfish_sync_timeline_obj *timeline; |
| int fd_out; |
| struct goldfish_sync_ioctl_info ioctl_data; |
| |
| DTRACE(); |
| |
| sync_context_data = file->private_data; |
| fd_out = -1; |
| |
| switch (cmd) { |
| case GOLDFISH_SYNC_IOC_QUEUE_WORK: |
| |
| DPRINT("exec GOLDFISH_SYNC_IOC_QUEUE_WORK"); |
| |
| mutex_lock(&global_sync_state->mutex_lock); |
| |
| if (copy_from_user(&ioctl_data, |
| (void __user *)arg, |
| sizeof(ioctl_data))) { |
| ERR("Failed to copy memory for ioctl_data from user."); |
| mutex_unlock(&global_sync_state->mutex_lock); |
| return -EFAULT; |
| } |
| |
| if (ioctl_data.host_syncthread_handle_in == 0) { |
| DPRINT("Error: zero host syncthread handle!!!"); |
| mutex_unlock(&global_sync_state->mutex_lock); |
| return -EFAULT; |
| } |
| |
| if (!sync_context_data->timeline) { |
| DPRINT("no timeline yet, create one."); |
| sync_context_data->timeline = goldfish_sync_timeline_create(); |
| DPRINT("timeline: 0x%p", &sync_context_data->timeline); |
| } |
| |
| timeline = sync_context_data->timeline; |
| fd_out = goldfish_sync_fence_create(timeline, |
| timeline->current_time + 1); |
| DPRINT("Created fence with fd %d and current time %u (timeline: 0x%p)", |
| fd_out, |
| sync_context_data->timeline->current_time + 1, |
| sync_context_data->timeline); |
| |
| ioctl_data.fence_fd_out = fd_out; |
| |
| if (copy_to_user((void __user *)arg, |
| &ioctl_data, |
| sizeof(ioctl_data))) { |
| DPRINT("Error, could not copy to user!!!"); |
| |
| sys_close(fd_out); |
| /* We won't be doing an increment, kref_put immediately. */ |
| kref_put(&timeline->kref, delete_timeline_obj); |
| mutex_unlock(&global_sync_state->mutex_lock); |
| return -EFAULT; |
| } |
| |
| /* We are now about to trigger a host-side wait; |
| * accumulate on |pending_waits|. */ |
| goldfish_sync_send_guestcmd(global_sync_state, |
| CMD_TRIGGER_HOST_WAIT, |
| ioctl_data.host_glsync_handle_in, |
| ioctl_data.host_syncthread_handle_in, |
| (uint64_t)(uintptr_t)(sync_context_data->timeline)); |
| |
| mutex_unlock(&global_sync_state->mutex_lock); |
| return 0; |
| default: |
| return -ENOTTY; |
| } |
| } |
| |
| static const struct file_operations goldfish_sync_fops = { |
| .owner = THIS_MODULE, |
| .open = goldfish_sync_open, |
| .release = goldfish_sync_release, |
| .unlocked_ioctl = goldfish_sync_ioctl, |
| .compat_ioctl = goldfish_sync_ioctl, |
| }; |
| |
| static struct miscdevice goldfish_sync_device = { |
| .name = "goldfish_sync", |
| .fops = &goldfish_sync_fops, |
| }; |
| |
| |
| static bool setup_verify_batch_cmd_addr(struct goldfish_sync_state *sync_state, |
| void *batch_addr, |
| uint32_t addr_offset, |
| uint32_t addr_offset_high) |
| { |
| uint64_t batch_addr_phys; |
| uint32_t batch_addr_phys_test_lo; |
| uint32_t batch_addr_phys_test_hi; |
| |
| if (!batch_addr) { |
| ERR("Could not use batch command address!"); |
| return false; |
| } |
| |
| batch_addr_phys = virt_to_phys(batch_addr); |
| writel((uint32_t)(batch_addr_phys), |
| sync_state->reg_base + addr_offset); |
| writel((uint32_t)(batch_addr_phys >> 32), |
| sync_state->reg_base + addr_offset_high); |
| |
| batch_addr_phys_test_lo = |
| readl(sync_state->reg_base + addr_offset); |
| batch_addr_phys_test_hi = |
| readl(sync_state->reg_base + addr_offset_high); |
| |
| if (virt_to_phys(batch_addr) != |
| (((uint64_t)batch_addr_phys_test_hi << 32) | |
| batch_addr_phys_test_lo)) { |
| ERR("Invalid batch command address!"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| int goldfish_sync_probe(struct platform_device *pdev) |
| { |
| struct resource *ioresource; |
| struct goldfish_sync_state *sync_state = global_sync_state; |
| int status; |
| |
| DTRACE(); |
| |
| sync_state->to_do_end = 0; |
| |
| spin_lock_init(&sync_state->lock); |
| mutex_init(&sync_state->mutex_lock); |
| |
| platform_set_drvdata(pdev, sync_state); |
| |
| ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (ioresource == NULL) { |
| ERR("platform_get_resource failed"); |
| return -ENODEV; |
| } |
| |
| sync_state->reg_base = |
| devm_ioremap(&pdev->dev, ioresource->start, PAGE_SIZE); |
| if (sync_state->reg_base == NULL) { |
| ERR("Could not ioremap"); |
| return -ENOMEM; |
| } |
| |
| sync_state->irq = platform_get_irq(pdev, 0); |
| if (sync_state->irq < 0) { |
| ERR("Could not platform_get_irq"); |
| return -ENODEV; |
| } |
| |
| status = devm_request_irq(&pdev->dev, |
| sync_state->irq, |
| goldfish_sync_interrupt, |
| IRQF_SHARED, |
| pdev->name, |
| sync_state); |
| if (status) { |
| ERR("request_irq failed"); |
| return -ENODEV; |
| } |
| |
| INIT_WORK(&sync_state->work_item, |
| goldfish_sync_work_item_fn); |
| |
| misc_register(&goldfish_sync_device); |
| |
| /* Obtain addresses for batch send/recv of commands. */ |
| { |
| struct goldfish_sync_hostcmd *batch_addr_hostcmd; |
| struct goldfish_sync_guestcmd *batch_addr_guestcmd; |
| |
| batch_addr_hostcmd = |
| devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_hostcmd), |
| GFP_KERNEL); |
| batch_addr_guestcmd = |
| devm_kzalloc(&pdev->dev, sizeof(struct goldfish_sync_guestcmd), |
| GFP_KERNEL); |
| |
| if (!setup_verify_batch_cmd_addr(sync_state, |
| batch_addr_hostcmd, |
| SYNC_REG_BATCH_COMMAND_ADDR, |
| SYNC_REG_BATCH_COMMAND_ADDR_HIGH)) { |
| ERR("goldfish_sync: Could not setup batch command address"); |
| return -ENODEV; |
| } |
| |
| if (!setup_verify_batch_cmd_addr(sync_state, |
| batch_addr_guestcmd, |
| SYNC_REG_BATCH_GUESTCOMMAND_ADDR, |
| SYNC_REG_BATCH_GUESTCOMMAND_ADDR_HIGH)) { |
| ERR("goldfish_sync: Could not setup batch guest command address"); |
| return -ENODEV; |
| } |
| |
| sync_state->batch_hostcmd = batch_addr_hostcmd; |
| sync_state->batch_guestcmd = batch_addr_guestcmd; |
| } |
| |
| INFO("goldfish_sync: Initialized goldfish sync device"); |
| |
| writel(0, sync_state->reg_base + SYNC_REG_INIT); |
| |
| return 0; |
| } |
| |
| static int goldfish_sync_remove(struct platform_device *pdev) |
| { |
| struct goldfish_sync_state *sync_state = global_sync_state; |
| |
| DTRACE(); |
| |
| misc_deregister(&goldfish_sync_device); |
| memset(sync_state, 0, sizeof(struct goldfish_sync_state)); |
| return 0; |
| } |
| |
| static const struct of_device_id goldfish_sync_of_match[] = { |
| { .compatible = "google,goldfish-sync", }, |
| {}, |
| }; |
| MODULE_DEVICE_TABLE(of, goldfish_sync_of_match); |
| |
| static const struct acpi_device_id goldfish_sync_acpi_match[] = { |
| { "GFSH0006", 0 }, |
| { }, |
| }; |
| |
| MODULE_DEVICE_TABLE(acpi, goldfish_sync_acpi_match); |
| |
| static struct platform_driver goldfish_sync = { |
| .probe = goldfish_sync_probe, |
| .remove = goldfish_sync_remove, |
| .driver = { |
| .name = "goldfish_sync", |
| .of_match_table = goldfish_sync_of_match, |
| .acpi_match_table = ACPI_PTR(goldfish_sync_acpi_match), |
| } |
| }; |
| |
| module_platform_driver(goldfish_sync); |
| |
| MODULE_AUTHOR("Google, Inc."); |
| MODULE_DESCRIPTION("Android QEMU Sync Driver"); |
| MODULE_LICENSE("GPL"); |
| MODULE_VERSION("1.0"); |