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