blob: 731c6f9b886957d2ae0200debf581920ad8dd121 [file] [log] [blame]
// Copyright 2019 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/syscalls/forward.h>
#include <object/clock_dispatcher.h>
namespace {
constexpr uint64_t GetArgsVersion(uint64_t options) {
return (options & ZX_CLOCK_ARGS_VERSION_MASK) >> ZX_CLOCK_ARGS_VERSION_SHIFT;
}
} // namespace
zx_status_t sys_clock_create(uint64_t options, user_in_ptr<const void> user_args,
user_out_handle* clock_out) {
KernelHandle<ClockDispatcher> clock_handle;
zx_clock_create_args_v1_t args{};
zx_rights_t rights;
zx_status_t result;
// Extract the creation arguments based on the version signalled in options.
switch (GetArgsVersion(options)) {
// v0 implies "just use the defaults". No args structure should have been
// passed. Just set our local v1 args structure to the default backstop
// time of 0.
case 0:
if (user_args) {
return ZX_ERR_INVALID_ARGS;
}
args.backstop_time = 0;
break;
// Extract the user args from the v1 structure. They will be sanity checked
// during the dispatcher's static Create
case 1:
result = user_args.reinterpret<const zx_clock_create_args_v1_t>().copy_from_user(&args);
if (result != ZX_OK) {
return result;
}
break;
default:
return ZX_ERR_INVALID_ARGS;
}
result = ClockDispatcher::Create(options, args, &clock_handle, &rights);
if (result == ZX_OK) {
result = clock_out->make(ktl::move(clock_handle), rights);
}
return result;
}
zx_status_t sys_clock_read(zx_handle_t clock_handle, user_out_ptr<zx_time_t> user_now) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ClockDispatcher> clock;
zx_status_t status =
up->handle_table().GetDispatcherWithRights(*up, clock_handle, ZX_RIGHT_READ, &clock);
if (status != ZX_OK) {
return status;
}
zx_time_t now{};
status = clock->Read(&now);
if (status != ZX_OK) {
return status;
}
return user_now.copy_to_user(now);
}
zx_status_t sys_clock_get_details(zx_handle_t clock_handle, uint64_t options,
user_out_ptr<void> user_details) {
// Currently, the only version of the details structure defined is V1. If the
// user failed to provide a buffer, or signaled a different version of the
// structure, then it is an error.
zx_clock_details_v1_t details{};
if ((options != ZX_CLOCK_ARGS_VERSION(1)) || !user_details) {
return ZX_ERR_INVALID_ARGS;
}
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ClockDispatcher> clock;
zx_status_t status =
up->handle_table().GetDispatcherWithRights(*up, clock_handle, ZX_RIGHT_READ, &clock);
if (status != ZX_OK) {
return status;
}
status = clock->GetDetails(&details);
if (status != ZX_OK) {
return status;
}
return user_details.reinterpret<zx_clock_details_v1>().copy_to_user(details);
}
zx_status_t sys_clock_update(zx_handle_t clock_handle, uint64_t options,
user_in_ptr<const void> user_args) {
// Currently, there are only 2 versions of the update structure defined; V1
// and V2. If the user failed to provide a buffer, or signaled any other
// version of the structure, then it is an error.
if (!user_args) {
return ZX_ERR_INVALID_ARGS;
}
union {
zx_clock_update_args_v1_t v1;
zx_clock_update_args_v2_t v2;
} args;
zx_status_t status;
const uint64_t version = GetArgsVersion(options);
int32_t rate_adjust;
switch (version) {
case 1:
status = user_args.reinterpret<const zx_clock_update_args_v1_t>().copy_from_user(&args.v1);
rate_adjust = args.v1.rate_adjust;
break;
case 2:
status = user_args.reinterpret<const zx_clock_update_args_v2_t>().copy_from_user(&args.v2);
rate_adjust = args.v2.rate_adjust;
break;
default:
return ZX_ERR_INVALID_ARGS;
}
if (status != ZX_OK) {
return status;
}
// Before going further, perform basic sanity checks of the update arguments.
//
// Only the defined options may be present in the request, and at least one of
// them must be specified.
options = options & ~ZX_CLOCK_ARGS_VERSION_MASK;
if ((options & ~ZX_CLOCK_UPDATE_OPTIONS_ALL) || !(options & ZX_CLOCK_UPDATE_OPTIONS_ALL)) {
return ZX_ERR_INVALID_ARGS;
}
// The PPM adjustment must be within the legal range.
if ((options & ZX_CLOCK_UPDATE_OPTION_RATE_ADJUST_VALID) &&
((rate_adjust < ZX_CLOCK_UPDATE_MIN_RATE_ADJUST) ||
(rate_adjust > ZX_CLOCK_UPDATE_MAX_RATE_ADJUST))) {
return ZX_ERR_INVALID_ARGS;
}
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ClockDispatcher> clock;
status = up->handle_table().GetDispatcherWithRights(*up, clock_handle, ZX_RIGHT_WRITE, &clock);
if (status != ZX_OK) {
return status;
}
if (version == 1) {
return clock->Update(options, args.v1);
} else {
return clock->Update(options, args.v2);
}
}