blob: c85f79bf766e1fb64e510a99dd5df1964e596b70 [file] [log] [blame] [edit]
// 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 <lib/boot-options/boot-options.h>
#include <lib/boot-options/types.h>
#include <lib/console.h>
#include <lib/debuglog.h>
#include <lib/io.h>
#include <lib/ktrace.h>
#include <lib/mtrace.h>
#include <lib/persistent-debuglog.h>
#include <lib/syscalls/forward.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/errors.h>
#include <zircon/syscalls/debug.h>
#include <zircon/types.h>
#include <object/handle.h>
#include <object/process_dispatcher.h>
#include <object/resource.h>
#include <platform/debug.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());
if (gBootOptions->enable_serial_syscalls != SerialDebugSyscalls::kEnabled) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status;
if ((status = validate_ranged_resource(handle, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_DEBUG_BASE,
1)) != ZX_OK) {
return status;
}
size_t idx = 0;
for (; idx < max_len; ++idx) {
char c;
// Wait only on the first character.
// The API for this function can return any number of characters up to the supplied buffer
// length, however there is no notification mechanism for when there are bytes to read.
// Hence, we need to read at least one character or applications will be forced to spin poll.
// We avoid reading all the characters so that interactive applications can stay responsive
// without losing efficiency by being forced to read one character at a time.
bool wait = (idx == 0);
int err = platform_dgetc(&c, wait);
if (err < 0) {
return err;
} else if (err == 0) {
break;
}
if (c == '\r') {
c = '\n';
}
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 (gBootOptions->enable_serial_syscalls != SerialDebugSyscalls::kEnabled &&
gBootOptions->enable_serial_syscalls != SerialDebugSyscalls::kOutputOnly) {
return ZX_ERR_NOT_SUPPORTED;
}
if (len > kMaxDebugWriteSize)
len = kMaxDebugWriteSize;
char buf[kMaxDebugWriteSize];
if (ptr.copy_array_from_user(buf, len) != ZX_OK)
return ZX_ERR_INVALID_ARGS;
// Dump what we can into the persistent dlog, if we have one.
persistent_dlog_write({buf, len});
// 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 (!gBootOptions->enable_debugging_syscalls) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status;
if ((status = validate_ranged_resource(handle, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_DEBUG_BASE,
1)) != ZX_OK) {
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) {
// See also ktrace_init() in zircon/kernel/lib/ktrace/ktrace.cc.
if (!gBootOptions->enable_debugging_syscalls) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status;
if ((status = validate_ranged_resource(handle, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_DEBUG_BASE,
1)) != ZX_OK) {
return status;
}
ssize_t result = ktrace_read_user(_data, 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) {
// See also ktrace_init() in zircon/kernel/lib/ktrace/ktrace.cc.
if (!gBootOptions->enable_debugging_syscalls) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status;
if ((status = validate_ranged_resource(handle, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_DEBUG_BASE,
1)) != ZX_OK) {
return status;
}
switch (action) {
case KTRACE_ACTION_NEW_PROBE: {
char name[ZX_MAX_NAME_LEN];
if (_ptr.reinterpret<char>().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) {
return ZX_ERR_NOT_SUPPORTED;
}
// 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 (!gBootOptions->enable_debugging_syscalls) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t status;
if ((status = validate_ranged_resource(handle, ZX_RSRC_KIND_SYSTEM, ZX_RSRC_SYSTEM_DEBUG_BASE,
1)) != ZX_OK) {
return status;
}
return mtrace_control(kind, action, options, ptr, size);
}