1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <fbl/auto_call.h>
6 #include <lib/fzl/vmo-mapper.h>
7 #include <zircon/assert.h>
8 
9 #include <utility>
10 
11 namespace fzl {
12 
CreateAndMap(uint64_t size,zx_vm_option_t map_flags,fbl::RefPtr<VmarManager> vmar_manager,zx::vmo * vmo_out,zx_rights_t vmo_rights,uint32_t cache_policy)13 zx_status_t VmoMapper::CreateAndMap(uint64_t size,
14                                     zx_vm_option_t map_flags,
15                                     fbl::RefPtr<VmarManager> vmar_manager,
16                                     zx::vmo* vmo_out,
17                                     zx_rights_t vmo_rights,
18                                     uint32_t cache_policy) {
19     if (size == 0) {
20         return ZX_ERR_INVALID_ARGS;
21     }
22 
23     zx_status_t res = CheckReadyToMap(vmar_manager);
24     if (res != ZX_OK) {
25         return res;
26     }
27 
28     zx::vmo vmo;
29     zx_status_t ret = zx::vmo::create(size, 0, &vmo);
30     if (ret != ZX_OK) {
31         return ret;
32     }
33 
34     if (cache_policy != 0) {
35         ret = vmo.set_cache_policy(cache_policy);
36         if (ret != ZX_OK) {
37             return ret;
38         }
39     }
40 
41     ret = InternalMap(vmo, 0, size, map_flags, std::move(vmar_manager));
42     if (ret != ZX_OK) {
43         return ret;
44     }
45 
46     if (vmo_out) {
47         if (vmo_rights != ZX_RIGHT_SAME_RIGHTS) {
48             ret = vmo.replace(vmo_rights, &vmo);
49             if (ret != ZX_OK) {
50                 Unmap();
51                 return ret;
52             }
53         }
54 
55         *vmo_out = std::move(vmo);
56     }
57 
58     return ZX_OK;
59 }
60 
Map(const zx::vmo & vmo,uint64_t offset,uint64_t size,zx_vm_option_t map_options,fbl::RefPtr<VmarManager> vmar_manager)61 zx_status_t VmoMapper::Map(const zx::vmo& vmo,
62                            uint64_t offset,
63                            uint64_t size,
64                            zx_vm_option_t map_options,
65                            fbl::RefPtr<VmarManager> vmar_manager) {
66     zx_status_t res;
67 
68     if (!vmo.is_valid()) {
69         return ZX_ERR_INVALID_ARGS;
70     }
71 
72     res = CheckReadyToMap(vmar_manager);
73     if (res != ZX_OK) {
74         return res;
75     }
76 
77     uint64_t vmo_size;
78     res = vmo.get_size(&vmo_size);
79     if (res != ZX_OK) {
80         return res;
81     }
82 
83     uint64_t end_addr;
84     if (add_overflow(size, offset, &end_addr) || end_addr > vmo_size) {
85         return ZX_ERR_OUT_OF_RANGE;
86     }
87 
88     if (!size) {
89         size = vmo_size - offset;
90     }
91 
92     return InternalMap(vmo, offset, size, map_options, vmar_manager);
93 }
94 
Unmap()95 void VmoMapper::Unmap() {
96     if (start() != nullptr) {
97         ZX_DEBUG_ASSERT(size_ != 0);
98         zx_handle_t vmar_handle = (vmar_manager_ == nullptr)
99             ? zx::vmar::root_self()->get()
100             : vmar_manager_->vmar().get();
101 
102         __UNUSED zx_status_t res;
103         res = zx_vmar_unmap(vmar_handle, start_, size_);
104         ZX_DEBUG_ASSERT(res == ZX_OK);
105     }
106 
107     vmar_manager_.reset();
108     start_ = 0;
109     size_ = 0;
110 }
111 
CheckReadyToMap(const fbl::RefPtr<VmarManager> & vmar_manager)112 zx_status_t VmoMapper::CheckReadyToMap(const fbl::RefPtr<VmarManager>& vmar_manager) {
113     if (start_ != 0) {
114         return ZX_ERR_BAD_STATE;
115     }
116 
117     if ((vmar_manager != nullptr) && !vmar_manager->vmar().is_valid()) {
118         return ZX_ERR_INVALID_ARGS;
119     }
120 
121     return ZX_OK;
122 }
123 
InternalMap(const zx::vmo & vmo,uint64_t offset,uint64_t size,zx_vm_option_t map_options,fbl::RefPtr<VmarManager> vmar_manager)124 zx_status_t VmoMapper::InternalMap(const zx::vmo& vmo,
125                                    uint64_t offset,
126                                    uint64_t size,
127                                    zx_vm_option_t map_options,
128                                    fbl::RefPtr<VmarManager> vmar_manager) {
129     ZX_DEBUG_ASSERT(vmo.is_valid());
130     ZX_DEBUG_ASSERT(start() == nullptr);
131     ZX_DEBUG_ASSERT(size_ == 0);
132     ZX_DEBUG_ASSERT(vmar_manager_ == nullptr);
133 
134     zx_handle_t vmar_handle = (vmar_manager == nullptr)
135         ? zx::vmar::root_self()->get()
136         : vmar_manager->vmar().get();
137 
138     zx_status_t res = zx_vmar_map(vmar_handle, map_options, 0, vmo.get(), offset, size, &start_);
139     if (res != ZX_OK) {
140         return res;
141     }
142 
143     size_ = size;
144     vmar_manager_ = std::move(vmar_manager);
145 
146     return ZX_OK;
147 }
148 
149 } // namespace fzl
150