blob: 0f8218c193d7edcaf92e0510a9601a0d48afffae [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fs/mapped-vmo.h>
#include <magenta/process.h>
#include <magenta/syscalls.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/unique_ptr.h>
mx_status_t MappedVmo::Create(size_t size, const char* name, fbl::unique_ptr<MappedVmo>* out) {
mx_handle_t vmo;
uintptr_t addr;
mx_status_t status;
if ((status = mx_vmo_create(size, 0, &vmo)) != MX_OK) {
return status;
} else if ((status = mx_vmar_map(mx_vmar_root_self(), 0, vmo, 0,
size, MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE,
&addr)) != MX_OK) {
mx_handle_close(vmo);
return status;
}
mx_object_set_property(vmo, MX_PROP_NAME, name, strlen(name));
fbl::AllocChecker ac;
fbl::unique_ptr<MappedVmo> mvmo(new (&ac) MappedVmo(vmo, addr, size));
if (!ac.check()) {
mx_vmar_unmap(mx_vmar_root_self(), addr, size);
mx_handle_close(vmo);
return MX_ERR_NO_MEMORY;
}
*out = fbl::move(mvmo);
return MX_OK;
}
mx_status_t MappedVmo::Shrink(size_t off, size_t len) {
if (len == 0 || off + len > len_ || off > len_ || off + len < off) {
return MX_ERR_INVALID_ARGS;
}
mx_status_t status;
mx_handle_t new_vmo;
if (off > 0) {
// Unmap everything before the offset
if ((status = mx_vmar_unmap(mx_vmar_root_self(), addr_, off)) != MX_OK) {
return status;
}
}
if (off + len < len_) {
// Unmap everything after the offset
if ((status = mx_vmar_unmap(mx_vmar_root_self(), addr_ + off + len, len_ - (off + len))) != MX_OK) {
return status;
}
}
if ((status = mx_vmo_clone(vmo_, MX_VMO_CLONE_COPY_ON_WRITE, off, len, &new_vmo)) != MX_OK) {
return status;
}
mx_handle_close(vmo_);
vmo_ = new_vmo;
addr_ = addr_ + off;
len_ = len;
return MX_OK;
}
mx_status_t MappedVmo::Grow(size_t len) {
if (len < len_) {
return MX_ERR_INVALID_ARGS;
}
len = fbl::roundup(len, static_cast<size_t>(PAGE_SIZE));
mx_status_t status;
uintptr_t addr;
if ((status = mx_vmo_set_size(vmo_, len)) != MX_OK) {
return status;
}
mx_info_vmar_t vmar_info;
if ((status = mx_object_get_info(mx_vmar_root_self(), MX_INFO_VMAR, &vmar_info, sizeof(vmar_info), NULL, NULL)) != MX_OK) {
return status;
}
// Try to extend mapping
if ((status = mx_vmar_map(mx_vmar_root_self(), addr_ + len_ - vmar_info.base, vmo_, len_, len - len_, MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE | MX_VM_FLAG_SPECIFIC, &addr)) != MX_OK) {
// If extension fails, create entirely new mapping and unmap the old one
if ((status = mx_vmar_map(mx_vmar_root_self(), 0, vmo_, 0, len, MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE, &addr)) != MX_OK) {
return status;
}
if ((status = mx_vmar_unmap(mx_vmar_root_self(), addr_, len_)) != MX_OK) {
return status;
}
addr_ = addr;
}
len_ = len;
return MX_OK;
}
mx_handle_t MappedVmo::GetVmo(void) const {
return vmo_;
}
void* MappedVmo::GetData(void) const {
return (void*)addr_;
}
size_t MappedVmo::GetSize(void) const {
return len_;
}
MappedVmo::MappedVmo(mx_handle_t vmo, uintptr_t addr, size_t len)
: vmo_(vmo), addr_(addr), len_(len) {}
MappedVmo::~MappedVmo() {
mx_vmar_unmap(mx_vmar_root_self(), addr_, len_);
mx_handle_close(vmo_);
}