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#include <stdio.h> 6#include <string.h> 7 8#include <ddk/binding.h> 9#include <ddk/device.h> 10#include <ddk/driver.h> 11 12#include "device-internal.h" 13#include "devcoordinator.h" 14 15#include <zircon/device/dmctl.h> 16 17static zx_device_t* dmctl_dev; 18 19static zx_status_t dmctl_cmd(dc_msg_t::Op op, const char* cmd, size_t cmdlen, 20 zx_handle_t* h, uint32_t hcount) { 21 dc_msg_t msg; 22 uint32_t msglen; 23 if (dc_msg_pack(&msg, &msglen, cmd, cmdlen, nullptr, nullptr) < 0) { 24 return ZX_ERR_INVALID_ARGS; 25 } 26 msg.op = op; 27 dc_status_t rsp; 28 return dc_msg_rpc(dmctl_dev->rpc, &msg, msglen, h, hcount, &rsp, sizeof(rsp), nullptr, nullptr); 29} 30 31static zx_status_t dmctl_write(void* ctx, const void* buf, size_t count, zx_off_t off, 32 size_t* actual) { 33 zx_status_t status = dmctl_cmd(dc_msg_t::Op::kDmCommand, static_cast<const char*>(buf), count, 34 nullptr, 0); 35 if (status >= 0) { 36 *actual = count; 37 status = ZX_OK; 38 } 39 return status; 40} 41 42static zx_status_t dmctl_ioctl(void* ctx, uint32_t op, 43 const void* in_buf, size_t in_len, 44 void* out_buf, size_t out_len, size_t* out_actual) { 45 switch (op) { 46 case IOCTL_DMCTL_COMMAND: { 47 if (in_len != sizeof(dmctl_cmd_t)) { 48 return ZX_ERR_INVALID_ARGS; 49 } 50 dmctl_cmd_t cmd; 51 memcpy(&cmd, in_buf, sizeof(cmd)); 52 cmd.name[sizeof(cmd.name) - 1] = 0; 53 *out_actual = 0; 54 zx_status_t status = dmctl_cmd(dc_msg_t::Op::kDmCommand, cmd.name, strlen(cmd.name), 55 &cmd.h, (cmd.h != ZX_HANDLE_INVALID) ? 1 : 0); 56 // NOT_SUPPORTED tells the dispatcher to close the handle for 57 // ioctls that accept a handle argument, so we have to avoid 58 // returning that in this case where the handle has been passed 59 // to another process (and effectively closed) 60 if (status == ZX_ERR_NOT_SUPPORTED) { 61 status = ZX_ERR_INTERNAL; 62 } 63 return status; 64 } 65 case IOCTL_DMCTL_OPEN_VIRTCON: 66 if (in_len != sizeof(zx_handle_t)) { 67 return ZX_ERR_INVALID_ARGS; 68 } 69 return dmctl_cmd(dc_msg_t::Op::kDmOpenVirtcon, nullptr, 0, ((zx_handle_t*) in_buf), 1); 70 case IOCTL_DMCTL_WATCH_DEVMGR: 71 if (in_len != sizeof(zx_handle_t)) { 72 return ZX_ERR_INVALID_ARGS; 73 } 74 return dmctl_cmd(dc_msg_t::Op::kDmWatch, nullptr, 0, ((zx_handle_t*) in_buf), 1); 75 case IOCTL_DMCTL_MEXEC: 76 if (in_len != sizeof(dmctl_mexec_args_t)) { 77 return ZX_ERR_INVALID_ARGS; 78 } 79 return dmctl_cmd(dc_msg_t::Op::kDmMexec, nullptr, 0, ((zx_handle_t*) in_buf), 2); 80 default: 81 return ZX_ERR_INVALID_ARGS; 82 } 83} 84 85static zx_protocol_device_t dmctl_device_ops = []() { 86 zx_protocol_device_t protocol = {}; 87 protocol.version = DEVICE_OPS_VERSION; 88 protocol.write = dmctl_write; 89 protocol.ioctl = dmctl_ioctl; 90 return protocol; 91}(); 92 93zx_status_t dmctl_bind(void* ctx, zx_device_t* parent) { 94 device_add_args_t args = {}; 95 args.version = DEVICE_ADD_ARGS_VERSION; 96 args.name = "dmctl"; 97 args.ops = &dmctl_device_ops; 98 99 return device_add(parent, &args, &dmctl_dev); 100} 101 102static zx_driver_ops_t dmctl_driver_ops = []() { 103 zx_driver_ops_t ops = {}; 104 ops.version = DRIVER_OPS_VERSION; 105 ops.bind = dmctl_bind; 106 return ops; 107}(); 108 109ZIRCON_DRIVER_BEGIN(dmctl, dmctl_driver_ops, "zircon", "0.1", 1) 110 BI_MATCH_IF(EQ, BIND_PROTOCOL, ZX_PROTOCOL_MISC_PARENT), 111ZIRCON_DRIVER_END(dmctl) 112