text: // Copyright 2016 The Fuchsia Authors
  //
  // Use of this source code is governed by a MIT-style
  // license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT
  
  #include <err.h>
  #include <lib/console.h>
  #include <lib/debuglog.h>
  #include <lib/io.h>
  #include <lib/ktrace.h>
  #include <lib/mtrace.h>
  #include <lib/user_copy/user_ptr.h>
  #include <platform.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <trace.h>
  #include <zircon/syscalls/debug.h>
  
  #include <object/handle.h>
  #include <object/process_dispatcher.h>
  #include <object/resource.h>
  #include <platform/debug.h>
  
  #include "priv.h"
  
  #define LOCAL_TRACE 0
  
  constexpr uint32_t kMaxDebugWriteSize = 256u;
  
  // zx_status_t zx_debug_read
  zx_status_t sys_debug_read(zx_handle_t handle, user_out_ptr<char> ptr, size_t max_len,
                             user_out_ptr<size_t> len) {
    LTRACEF("ptr %p\n", ptr.get());
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    size_t idx = 0;
    for (; idx < max_len; ++idx) {
      int intc = getchar();
      if (intc < 0) {
        return intc;
      }
  
      if (intc == '\r') {
        intc = '\n';
      }
  
      char c = static_cast<char>(intc);
  
      status = ptr.copy_array_to_user(&c, 1, idx);
      if (status != ZX_OK) {
        return status;
      }
    }
    return len.copy_to_user(idx);
  }
  
  // zx_status_t zx_debug_write
  zx_status_t sys_debug_write(user_in_ptr<const char> ptr, size_t len) {
    LTRACEF("ptr %p, len %zu\n", ptr.get(), len);
  
    if (len > kMaxDebugWriteSize)
      len = kMaxDebugWriteSize;
  
    char buf[kMaxDebugWriteSize];
    if (ptr.copy_array_from_user(buf, len) != ZX_OK)
      return ZX_ERR_INVALID_ARGS;
  
    // This path to serial out arbitrates with the debug log
    // drainer and/or kernel ll debug path to minimize interleaving
    // of serial output between various sources
    dlog_serial_write(buf, len);
  
    return ZX_OK;
  }
  
  // zx_status_t zx_debug_send_command
  zx_status_t sys_debug_send_command(zx_handle_t handle, user_in_ptr<const char> ptr, size_t len) {
    LTRACEF("ptr %p, len %zu\n", ptr.get(), len);
  
    if (!DebuggingSyscallsEnabled()) {
      return ZX_ERR_NOT_SUPPORTED;
    }
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    if (len > kMaxDebugWriteSize)
      return ZX_ERR_INVALID_ARGS;
  
    char buf[kMaxDebugWriteSize + 2];
    if (ptr.copy_array_from_user(buf, len) != ZX_OK)
      return ZX_ERR_INVALID_ARGS;
  
    buf[len] = '\n';
    buf[len + 1] = 0;
    return console_run_script(buf);
  }
  
  // zx_status_t zx_ktrace_read
  zx_status_t sys_ktrace_read(zx_handle_t handle, user_out_ptr<void> _data, uint32_t offset,
                              size_t len, user_out_ptr<size_t> _actual) {
    if (!DebuggingSyscallsEnabled()) {
      return ZX_ERR_NOT_SUPPORTED;
    }
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    ssize_t result = ktrace_read_user(_data.get(), offset, len);
    if (result < 0)
      return static_cast<zx_status_t>(result);
  
    return _actual.copy_to_user(static_cast<size_t>(result));
  }
  
  // zx_status_t zx_ktrace_control
  zx_status_t sys_ktrace_control(zx_handle_t handle, uint32_t action, uint32_t options,
                                 user_inout_ptr<void> _ptr) {
    if (!DebuggingSyscallsEnabled()) {
      return ZX_ERR_NOT_SUPPORTED;
    }
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    switch (action) {
      case KTRACE_ACTION_NEW_PROBE: {
        char name[ZX_MAX_NAME_LEN];
        if (_ptr.copy_array_from_user(name, sizeof(name) - 1) != ZX_OK)
          return ZX_ERR_INVALID_ARGS;
        name[sizeof(name) - 1] = 0;
        return ktrace_control(action, options, name);
      }
      default:
        return ktrace_control(action, options, nullptr);
    }
  }
  
  // zx_status_t zx_ktrace_write
  zx_status_t sys_ktrace_write(zx_handle_t handle, uint32_t event_id, uint32_t arg0, uint32_t arg1) {
    if (!DebuggingSyscallsEnabled()) {
      return ZX_ERR_NOT_SUPPORTED;
    }
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    if (event_id > 0x7FF) {
      return ZX_ERR_INVALID_ARGS;
    }
  
    uint32_t* args = static_cast<uint32_t*>(ktrace_open(TAG_PROBE_24(event_id)));
    if (!args) {
      //  There is not a single reason for failure. Assume it reached the end.
      return ZX_ERR_UNAVAILABLE;
    }
  
    args[0] = arg0;
    args[1] = arg1;
    return ZX_OK;
  }
  
  // zx_status_t zx_mtrace_control
  zx_status_t sys_mtrace_control(zx_handle_t handle, uint32_t kind, uint32_t action, uint32_t options,
                                 user_inout_ptr<void> ptr, size_t size) {
    if (!DebuggingSyscallsEnabled()) {
      return ZX_ERR_NOT_SUPPORTED;
    }
  
    // TODO(ZX-971): finer grained validation
    zx_status_t status;
    if ((status = validate_resource(handle, ZX_RSRC_KIND_ROOT)) < 0) {
      return status;
    }
  
    return mtrace_control(kind, action, options, ptr, size);
  }
  