1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// See the README.md in this directory for documentation. 6 7#include <assert.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <threads.h> 12 13#include <ddk/binding.h> 14#include <ddk/device.h> 15#include <ddk/driver.h> 16#include <ddk/protocol/platform-defs.h> 17#include <ddk/protocol/platform-device.h> 18 19#include <zircon/syscalls.h> 20#include <zircon/syscalls/resource.h> 21#include <zircon/types.h> 22 23#include "cpu-trace-private.h" 24 25static zx_status_t cpu_trace_open(void* ctx, zx_device_t** dev_out, uint32_t flags) { 26 cpu_trace_device_t* dev = ctx; 27 if (dev->opened) 28 return ZX_ERR_ALREADY_BOUND; 29 30 dev->opened = true; 31 return ZX_OK; 32} 33 34static zx_status_t cpu_trace_close(void* ctx, uint32_t flags) { 35 cpu_trace_device_t* dev = ctx; 36 37 dev->opened = false; 38 return ZX_OK; 39} 40 41static zx_status_t cpu_trace_ioctl(void* ctx, uint32_t op, 42 const void* cmd, size_t cmdlen, 43 void* reply, size_t replymax, 44 size_t* out_actual) { 45 cpu_trace_device_t* dev = ctx; 46 47 mtx_lock(&dev->lock); 48 49 ssize_t result; 50 switch (IOCTL_FAMILY(op)) { 51#ifdef __x86_64__ 52 case IOCTL_FAMILY_CPUPERF: 53 result = cpuperf_ioctl(dev, op, cmd, cmdlen, 54 reply, replymax, out_actual); 55 break; 56 case IOCTL_FAMILY_INSNTRACE: 57 result = insntrace_ioctl(dev, op, cmd, cmdlen, 58 reply, replymax, out_actual); 59 break; 60#endif 61 default: 62 result = ZX_ERR_INVALID_ARGS; 63 break; 64 } 65 66 mtx_unlock(&dev->lock); 67 68 return result; 69} 70 71static void cpu_trace_release(void* ctx) { 72 cpu_trace_device_t* dev = ctx; 73 74#ifdef __x86_64__ 75 insntrace_release(dev); 76 cpuperf_release(dev); 77#endif 78 79 zx_handle_close(dev->bti); 80 free(dev); 81} 82 83static zx_protocol_device_t cpu_trace_device_proto = { 84 .version = DEVICE_OPS_VERSION, 85 .open = cpu_trace_open, 86 .close = cpu_trace_close, 87 .ioctl = cpu_trace_ioctl, 88 .release = cpu_trace_release, 89}; 90 91static zx_status_t cpu_trace_bind(void* ctx, zx_device_t* parent) { 92#ifdef __x86_64__ 93 insntrace_init_once(); 94 cpuperf_init_once(); 95#endif 96 97 platform_device_protocol_t pdev; 98 zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PLATFORM_DEV, &pdev); 99 if (status != ZX_OK) { 100 return status; 101 } 102 103 cpu_trace_device_t* dev = calloc(1, sizeof(*dev)); 104 if (!dev) { 105 return ZX_ERR_NO_MEMORY; 106 } 107 108 status = pdev_get_bti(&pdev, 0, &dev->bti); 109 if (status != ZX_OK) { 110 goto fail; 111 } 112 113 device_add_args_t args = { 114 .version = DEVICE_ADD_ARGS_VERSION, 115 .name = "cpu-trace", 116 .ctx = dev, 117 .ops = &cpu_trace_device_proto, 118 }; 119 120 if ((status = device_add(parent, &args, NULL)) < 0) { 121 goto fail; 122 } 123 124 return ZX_OK; 125 126fail: 127 zx_handle_close(dev->bti); 128 free(dev); 129 return status; 130} 131 132static zx_driver_ops_t cpu_trace_driver_ops = { 133 .version = DRIVER_OPS_VERSION, 134 .bind = cpu_trace_bind, 135}; 136 137ZIRCON_DRIVER_BEGIN(cpu_trace, cpu_trace_driver_ops, "zircon", "0.1", 4) 138 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_PLATFORM_DEV), 139 BI_ABORT_IF(NE, BIND_PLATFORM_DEV_VID, PDEV_VID_INTEL), 140 BI_ABORT_IF(NE, BIND_PLATFORM_DEV_PID, PDEV_PID_GENERIC), 141 BI_MATCH_IF(EQ, BIND_PLATFORM_DEV_DID, PDEV_DID_INTEL_CPU_TRACE), 142ZIRCON_DRIVER_END(cpu_trace) 143