1240116Smarcel// Copyright 2016 The Fuchsia Authors 2240116Smarcel// 3240116Smarcel// Use of this source code is governed by a MIT-style 4240116Smarcel// license that can be found in the LICENSE file or at 5240116Smarcel// https://opensource.org/licenses/MIT 6240116Smarcel 7240116Smarcel#include <err.h> 8240116Smarcel#include <inttypes.h> 9240116Smarcel#include <trace.h> 10240116Smarcel 11240116Smarcel#include <vm/vm_object.h> 12240116Smarcel#include <vm/vm_address_region.h> 13240116Smarcel 14240116Smarcel#include <lib/user_copy/user_ptr.h> 15240116Smarcel 16240116Smarcel#include <object/handle.h> 17240116Smarcel#include <object/process_dispatcher.h> 18240116Smarcel#include <object/vm_address_region_dispatcher.h> 19240116Smarcel#include <object/vm_object_dispatcher.h> 20240116Smarcel 21240116Smarcel#include <fbl/auto_call.h> 22240116Smarcel#include <fbl/ref_ptr.h> 23240116Smarcel 24240116Smarcel#include "priv.h" 25240116Smarcel 26240116Smarcel#define LOCAL_TRACE 0 27240116Smarcel 28240116Smarcelzx_status_t sys_vmar_allocate(zx_handle_t parent_vmar_handle, 29240116Smarcel zx_vm_option_t options, uint64_t offset, uint64_t size, 30240116Smarcel user_out_handle* child_vmar, 31240116Smarcel user_out_ptr<zx_vaddr_t> child_addr) { 32240116Smarcel 33240116Smarcel auto up = ProcessDispatcher::GetCurrent(); 34240116Smarcel 35240116Smarcel // Compute needed rights from requested mapping protections. 36240116Smarcel zx_rights_t vmar_rights = 0u; 37240116Smarcel if (options & ZX_VM_CAN_MAP_READ) 38240116Smarcel vmar_rights |= ZX_RIGHT_READ; 39240116Smarcel if (options & ZX_VM_CAN_MAP_WRITE) 40240116Smarcel vmar_rights |= ZX_RIGHT_WRITE; 41240116Smarcel if (options & ZX_VM_CAN_MAP_EXECUTE) 42240116Smarcel vmar_rights |= ZX_RIGHT_EXECUTE; 43240116Smarcel 44240116Smarcel // lookup the dispatcher from handle 45240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> vmar; 46240116Smarcel zx_status_t status = up->GetDispatcherWithRights(parent_vmar_handle, vmar_rights, &vmar); 47240116Smarcel if (status != ZX_OK) 48240116Smarcel return status; 49240116Smarcel 50240116Smarcel // Create the new VMAR 51240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> new_vmar; 52240116Smarcel zx_rights_t new_rights; 53240116Smarcel status = vmar->Allocate(offset, size, options, &new_vmar, &new_rights); 54240116Smarcel if (status != ZX_OK) 55240116Smarcel return status; 56240116Smarcel 57240116Smarcel // Setup a handler to destroy the new VMAR if the syscall is unsuccessful. 58240116Smarcel // Note that new_vmar is being passed by value, so a new reference is held 59240116Smarcel // there. 60240116Smarcel auto cleanup_handler = fbl::MakeAutoCall([new_vmar]() { 61240116Smarcel new_vmar->Destroy(); 62240116Smarcel }); 63240116Smarcel 64240116Smarcel // Extract the base address before we give away the ref. 65240116Smarcel uintptr_t base = new_vmar->vmar()->base(); 66240116Smarcel 67240116Smarcel // Create a handle and attach the dispatcher to it 68240116Smarcel status = child_vmar->make(fbl::move(new_vmar), new_rights); 69240116Smarcel 70240116Smarcel if (status == ZX_OK) 71240116Smarcel status = child_addr.copy_to_user(base); 72240116Smarcel 73240116Smarcel if (status == ZX_OK) 74240116Smarcel cleanup_handler.cancel(); 75240116Smarcel return status; 76240116Smarcel} 77240116Smarcel 78240116Smarcelzx_status_t sys_vmar_allocate_old(zx_handle_t parent_vmar_handle, 79240116Smarcel uint64_t offset, uint64_t size, uint32_t map_flags, 80240116Smarcel user_out_handle* child_vmar, 81240116Smarcel user_out_ptr<zx_vaddr_t> child_addr) { 82240116Smarcel return sys_vmar_allocate(parent_vmar_handle, map_flags, offset, size, child_vmar, child_addr); 83240116Smarcel} 84240116Smarcel 85240116Smarcelzx_status_t sys_vmar_destroy(zx_handle_t vmar_handle) { 86240116Smarcel auto up = ProcessDispatcher::GetCurrent(); 87240116Smarcel 88240116Smarcel // lookup the dispatcher from handle 89240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> vmar; 90240116Smarcel zx_status_t status = up->GetDispatcher(vmar_handle, &vmar); 91240116Smarcel if (status != ZX_OK) 92240116Smarcel return status; 93240116Smarcel 94240116Smarcel return vmar->Destroy(); 95240116Smarcel} 96240116Smarcel 97240116Smarcelzx_status_t sys_vmar_map(zx_handle_t vmar_handle, zx_vm_option_t options, 98240116Smarcel uint64_t vmar_offset, zx_handle_t vmo_handle, 99240116Smarcel uint64_t vmo_offset, uint64_t len, 100240116Smarcel user_out_ptr<zx_vaddr_t> mapped_addr) { 101240116Smarcel auto up = ProcessDispatcher::GetCurrent(); 102240116Smarcel 103240116Smarcel // lookup the VMAR dispatcher from handle 104240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> vmar; 105240116Smarcel zx_rights_t vmar_rights; 106240116Smarcel zx_status_t status = up->GetDispatcherAndRights(vmar_handle, &vmar, &vmar_rights); 107240116Smarcel if (status != ZX_OK) 108240116Smarcel return status; 109240116Smarcel 110240116Smarcel // lookup the VMO dispatcher from handle 111240116Smarcel fbl::RefPtr<VmObjectDispatcher> vmo; 112240116Smarcel zx_rights_t vmo_rights; 113240116Smarcel status = up->GetDispatcherAndRights(vmo_handle, &vmo, &vmo_rights); 114240116Smarcel if (status != ZX_OK) 115240116Smarcel return status; 116240116Smarcel 117240116Smarcel // test to see if we should even be able to map this 118240116Smarcel if (!(vmo_rights & ZX_RIGHT_MAP)) 119240116Smarcel return ZX_ERR_ACCESS_DENIED; 120240116Smarcel 121240116Smarcel if (!VmAddressRegionDispatcher::is_valid_mapping_protection(options)) 122240116Smarcel return ZX_ERR_INVALID_ARGS; 123240116Smarcel 124240116Smarcel bool do_map_range = false; 125240116Smarcel if (options & ZX_VM_MAP_RANGE) { 126240116Smarcel do_map_range = true; 127240116Smarcel options &= ~ZX_VM_MAP_RANGE; 128240116Smarcel } 129240116Smarcel 130240116Smarcel if (do_map_range && (options & ZX_VM_SPECIFIC_OVERWRITE)) { 131240116Smarcel return ZX_ERR_INVALID_ARGS; 132240116Smarcel } 133240116Smarcel 134240116Smarcel // Usermode is not allowed to specify these flags on mappings, though we may 135240116Smarcel // set them below. 136240116Smarcel if (options & (ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_EXECUTE)) { 137240116Smarcel return ZX_ERR_INVALID_ARGS; 138240116Smarcel } 139240116Smarcel 140240116Smarcel // Permissions allowed by both the VMO and the VMAR 141240116Smarcel const bool can_read = (vmo_rights & ZX_RIGHT_READ) && (vmar_rights & ZX_RIGHT_READ); 142240116Smarcel const bool can_write = (vmo_rights & ZX_RIGHT_WRITE) && (vmar_rights & ZX_RIGHT_WRITE); 143240116Smarcel const bool can_exec = (vmo_rights & ZX_RIGHT_EXECUTE) && (vmar_rights & ZX_RIGHT_EXECUTE); 144240116Smarcel 145240116Smarcel // test to see if the requested mapping protections are allowed 146240116Smarcel if ((options & ZX_VM_PERM_READ) && !can_read) 147240116Smarcel return ZX_ERR_ACCESS_DENIED; 148240116Smarcel if ((options & ZX_VM_PERM_WRITE) && !can_write) 149240116Smarcel return ZX_ERR_ACCESS_DENIED; 150240116Smarcel if ((options & ZX_VM_PERM_EXECUTE) && !can_exec) 151240116Smarcel return ZX_ERR_ACCESS_DENIED; 152240116Smarcel 153240116Smarcel // If a permission is allowed by both the VMO and the VMAR, add it to the 154240116Smarcel // flags for the new mapping, so that the VMO's rights as of now can be used 155240116Smarcel // to constrain future permission changes via Protect(). 156240116Smarcel if (can_read) 157240116Smarcel options |= ZX_VM_CAN_MAP_READ; 158240116Smarcel if (can_write) 159240116Smarcel options |= ZX_VM_CAN_MAP_WRITE; 160240116Smarcel if (can_exec) 161240116Smarcel options |= ZX_VM_CAN_MAP_EXECUTE; 162240116Smarcel 163240116Smarcel fbl::RefPtr<VmMapping> vm_mapping; 164240116Smarcel status = vmar->Map(vmar_offset, vmo->vmo(), vmo_offset, len, options, &vm_mapping); 165240116Smarcel if (status != ZX_OK) 166240116Smarcel return status; 167240116Smarcel 168240116Smarcel // Setup a handler to destroy the new mapping if the syscall is unsuccessful. 169240116Smarcel auto cleanup_handler = fbl::MakeAutoCall([vm_mapping]() { 170240116Smarcel vm_mapping->Destroy(); 171240116Smarcel }); 172240116Smarcel 173240116Smarcel if (do_map_range) { 174240116Smarcel status = vm_mapping->MapRange(vmo_offset, len, false); 175240116Smarcel if (status != ZX_OK) { 176240116Smarcel return status; 177240116Smarcel } 178240116Smarcel } 179240116Smarcel 180240116Smarcel status = mapped_addr.copy_to_user(vm_mapping->base()); 181240116Smarcel if (status != ZX_OK) 182240116Smarcel return status; 183240116Smarcel 184240116Smarcel cleanup_handler.cancel(); 185240116Smarcel return ZX_OK; 186240116Smarcel} 187240116Smarcel 188240116Smarcelzx_status_t sys_vmar_map_old(zx_handle_t vmar_handle, uint64_t vmar_offset, 189240116Smarcel zx_handle_t vmo_handle, uint64_t vmo_offset, uint64_t len, 190240116Smarcel uint32_t map_flags, user_out_ptr<zx_vaddr_t> mapped_addr) { 191240116Smarcel return sys_vmar_map(vmar_handle, map_flags, vmar_offset, vmo_handle, vmo_offset, len, mapped_addr); 192240116Smarcel} 193240116Smarcel 194240116Smarcelzx_status_t sys_vmar_unmap(zx_handle_t vmar_handle, zx_vaddr_t addr, uint64_t len) { 195240116Smarcel auto up = ProcessDispatcher::GetCurrent(); 196240116Smarcel 197240116Smarcel // lookup the dispatcher from handle 198240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> vmar; 199240116Smarcel zx_status_t status = up->GetDispatcher(vmar_handle, &vmar); 200240116Smarcel if (status != ZX_OK) 201240116Smarcel return status; 202240116Smarcel 203240116Smarcel return vmar->Unmap(addr, len); 204240116Smarcel} 205240116Smarcel 206240116Smarcelzx_status_t sys_vmar_protect(zx_handle_t vmar_handle, zx_vm_option_t options, zx_vaddr_t addr, uint64_t len) { 207240116Smarcel auto up = ProcessDispatcher::GetCurrent(); 208240116Smarcel 209240116Smarcel zx_rights_t vmar_rights = 0u; 210273929Sjmmv if (options & ZX_VM_PERM_READ) 211240116Smarcel vmar_rights |= ZX_RIGHT_READ; 212240116Smarcel if (options & ZX_VM_PERM_WRITE) 213240116Smarcel vmar_rights |= ZX_RIGHT_WRITE; 214240116Smarcel if (options & ZX_VM_PERM_EXECUTE) 215240116Smarcel vmar_rights |= ZX_RIGHT_EXECUTE; 216240116Smarcel 217240116Smarcel // lookup the dispatcher from handle 218240116Smarcel fbl::RefPtr<VmAddressRegionDispatcher> vmar; 219240116Smarcel zx_status_t status = up->GetDispatcherWithRights(vmar_handle, vmar_rights, &vmar); 220240116Smarcel if (status != ZX_OK) 221240116Smarcel return status; 222240116Smarcel 223240116Smarcel if (!VmAddressRegionDispatcher::is_valid_mapping_protection(options)) 224240116Smarcel return ZX_ERR_INVALID_ARGS; 225240116Smarcel 226240116Smarcel return vmar->Protect(addr, len, options); 227240116Smarcel} 228240116Smarcel 229240116Smarcelzx_status_t sys_vmar_protect_old(zx_handle_t vmar_handle, zx_vaddr_t addr, uint64_t len, uint32_t prot) { 230240116Smarcel return sys_vmar_protect(vmar_handle, prot, addr, len); 231240116Smarcel} 232240116Smarcel