blob: 711112f4713269793361275a324f5ea4d1442451 [file] [log] [blame]
// Copyright 2018 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 <align.h>
#include <lib/syscalls/forward.h>
#include <zircon/syscalls-next.h>
#include <fbl/ref_ptr.h>
#include <ktl/move.h>
#include <object/pager_dispatcher.h>
#include <object/vm_object_dispatcher.h>
#include <vm/vm_object_paged.h>
#include <ktl/enforce.h>
namespace {
// Split out the pager vmo creation flags between PageSource and VmObjectPaged flags.
void split_syscall_flags(uint32_t flags, uint32_t* source_flags, uint32_t* vmo_flags) {
// Extract any option flags relevant to creation of the page source.
uint32_t src_flags = 0;
if (flags & ZX_VMO_TRAP_DIRTY) {
src_flags |= ZX_VMO_TRAP_DIRTY;
}
// Mask out any source flags. The remaining flags are the vmo creation flags; vmo creation will
// perform validation on them.
flags &= ~(ZX_VMO_TRAP_DIRTY);
*vmo_flags = flags;
*source_flags = src_flags;
}
} // namespace
// zx_status_t zx_pager_create
zx_status_t sys_pager_create(uint32_t options, zx_handle_t* out) {
auto up = ProcessDispatcher::GetCurrent();
zx_status_t status = up->EnforceBasicPolicy(ZX_POL_NEW_PAGER);
if (status != ZX_OK) {
return status;
}
if (options) {
return ZX_ERR_INVALID_ARGS;
}
KernelHandle<PagerDispatcher> handle;
zx_rights_t rights;
status = PagerDispatcher::Create(&handle, &rights);
if (status != ZX_OK) {
return status;
}
return up->MakeAndAddHandle(ktl::move(handle), rights, out);
}
// zx_status_t zx_pager_create_vmo
zx_status_t sys_pager_create_vmo(zx_handle_t pager, uint32_t options, zx_handle_t port,
uint64_t key, uint64_t size, zx_handle_t* out) {
auto up = ProcessDispatcher::GetCurrent();
zx_status_t status = up->EnforceBasicPolicy(ZX_POL_NEW_VMO);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, pager, ZX_RIGHT_ATTACH_VMO,
&pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<PortDispatcher> port_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, port, ZX_RIGHT_WRITE, &port_dispatcher);
if (status != ZX_OK) {
return status;
}
uint32_t source_flags = 0;
uint32_t vmo_flags = 0;
split_syscall_flags(options, &source_flags, &vmo_flags);
fbl::RefPtr<PageSource> src;
status = pager_dispatcher->CreateSource(ktl::move(port_dispatcher), key, source_flags, &src);
if (status != ZX_OK) {
return status;
}
zx::result<VmObjectDispatcher::CreateStats> parse_result =
VmObjectDispatcher::parse_create_syscall_flags(vmo_flags, size);
if (parse_result.is_error()) {
return parse_result.error_value();
}
VmObjectDispatcher::CreateStats stats = parse_result.value();
fbl::RefPtr<VmObjectPaged> vmo;
status = VmObjectPaged::CreateExternal(ktl::move(src), stats.flags, stats.size, &vmo);
if (status != ZX_OK) {
return status;
}
KernelHandle<VmObjectDispatcher> kernel_handle;
zx_rights_t rights;
status = VmObjectDispatcher::Create(vmo, size, pager_dispatcher->get_koid(),
VmObjectDispatcher::InitialMutability::kMutable,
&kernel_handle, &rights);
if (status != ZX_OK) {
return status;
}
return up->MakeAndAddHandle(ktl::move(kernel_handle), rights, out);
}
// zx_status_t zx_pager_detach_vmo
zx_status_t sys_pager_detach_vmo(zx_handle_t pager, zx_handle_t vmo) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
// TODO: Consider rights on the pager dispatcher.
zx_status_t status = up->handle_table().GetDispatcherWithRights(
*up, pager, ZX_RIGHT_ATTACH_VMO | ZX_RIGHT_MANAGE_VMO, &pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<VmObjectDispatcher> vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, vmo, ZX_RIGHT_WRITE, &vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
if (vmo_dispatcher->pager_koid() != pager_dispatcher->get_koid()) {
return ZX_ERR_INVALID_ARGS;
}
vmo_dispatcher->vmo()->DetachSource();
return ZX_OK;
}
// zx_status_t zx_pager_supply_pages
zx_status_t sys_pager_supply_pages(zx_handle_t pager, zx_handle_t pager_vmo, uint64_t offset,
uint64_t size, zx_handle_t aux_vmo_handle, uint64_t aux_offset) {
if (!IS_PAGE_ALIGNED(offset) || !IS_PAGE_ALIGNED(size) || !IS_PAGE_ALIGNED(aux_offset)) {
return ZX_ERR_INVALID_ARGS;
}
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
zx_status_t status = up->handle_table().GetDispatcherWithRights(*up, pager, ZX_RIGHT_MANAGE_VMO,
&pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<VmObjectDispatcher> pager_vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, pager_vmo, ZX_RIGHT_WRITE,
&pager_vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
if (pager_vmo_dispatcher->pager_koid() != pager_dispatcher->get_koid()) {
return ZX_ERR_INVALID_ARGS;
}
fbl::RefPtr<VmObjectDispatcher> aux_vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(
*up, aux_vmo_handle, ZX_RIGHT_READ | ZX_RIGHT_WRITE, &aux_vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
VmPageSpliceList pages;
status = aux_vmo_dispatcher->vmo()->TakePages(aux_offset, size, &pages);
if (status != ZX_OK) {
return status;
}
return pager_vmo_dispatcher->vmo()->SupplyPages(offset, size, &pages, SupplyOptions::PagerSupply);
}
// zx_status_t zx_pager_op_range
zx_status_t sys_pager_op_range(zx_handle_t pager, uint32_t op, zx_handle_t pager_vmo,
uint64_t offset, uint64_t length, uint64_t data) {
if (!IS_PAGE_ALIGNED(offset) || !IS_PAGE_ALIGNED(length)) {
return ZX_ERR_INVALID_ARGS;
}
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
zx_status_t status = up->handle_table().GetDispatcherWithRights(*up, pager, ZX_RIGHT_MANAGE_VMO,
&pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<VmObjectDispatcher> pager_vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, pager_vmo, ZX_RIGHT_WRITE,
&pager_vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
if (pager_vmo_dispatcher->pager_koid() != pager_dispatcher->get_koid()) {
return ZX_ERR_INVALID_ARGS;
}
return pager_dispatcher->RangeOp(op, pager_vmo_dispatcher->vmo(), offset, length, data);
}
// zx_status_t zx_pager_query_dirty_ranges
zx_status_t sys_pager_query_dirty_ranges(zx_handle_t pager, zx_handle_t pager_vmo, uint64_t offset,
uint64_t length, user_out_ptr<void> buffer,
size_t buffer_size, user_out_ptr<size_t> actual,
user_out_ptr<size_t> avail) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
zx_status_t status = up->handle_table().GetDispatcherWithRights(*up, pager, ZX_RIGHT_MANAGE_VMO,
&pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<VmObjectDispatcher> pager_vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, pager_vmo, ZX_RIGHT_READ,
&pager_vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
if (pager_vmo_dispatcher->pager_koid() != pager_dispatcher->get_koid()) {
return ZX_ERR_INVALID_ARGS;
}
return pager_dispatcher->QueryDirtyRanges(pager_vmo_dispatcher->vmo(), offset, length, buffer,
buffer_size, actual, avail);
}
// zx_status_t zx_pager_query_vmo_stats
zx_status_t sys_pager_query_vmo_stats(zx_handle_t pager, zx_handle_t pager_vmo, uint32_t options,
user_out_ptr<void> buffer, size_t buffer_size) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<PagerDispatcher> pager_dispatcher;
zx_status_t status = up->handle_table().GetDispatcherWithRights(*up, pager, ZX_RIGHT_MANAGE_VMO,
&pager_dispatcher);
if (status != ZX_OK) {
return status;
}
fbl::RefPtr<VmObjectDispatcher> pager_vmo_dispatcher;
status = up->handle_table().GetDispatcherWithRights(*up, pager_vmo, ZX_RIGHT_READ,
&pager_vmo_dispatcher);
if (status != ZX_OK) {
return status;
}
if (pager_vmo_dispatcher->pager_koid() != pager_dispatcher->get_koid()) {
return ZX_ERR_INVALID_ARGS;
}
return pager_dispatcher->QueryPagerVmoStats(pager_vmo_dispatcher->vmo(), options, buffer,
buffer_size);
}