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