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/algorithm.h>
6 #include <fbl/alloc_checker.h>
7 #include <lib/fzl/owned-vmo-mapper.h>
8 #include <lib/fzl/resizeable-vmo-mapper.h>
9 #include <string.h>
10
11 #include <utility>
12
13 namespace fzl {
14
Create(uint64_t size,const char * name,uint32_t map_options,fbl::RefPtr<VmarManager> vmar_manager,uint32_t cache_policy)15 fbl::unique_ptr<ResizeableVmoMapper> ResizeableVmoMapper::Create(
16 uint64_t size,
17 const char* name,
18 uint32_t map_options,
19 fbl::RefPtr<VmarManager> vmar_manager,
20 uint32_t cache_policy) {
21 fbl::AllocChecker ac;
22 auto ret = fbl::make_unique_checked<ResizeableVmoMapper>(&ac);
23 if (!ac.check()) {
24 return nullptr;
25 }
26
27 zx_status_t res = ret->CreateAndMap(size, name, map_options, vmar_manager, cache_policy);
28 if (res != ZX_OK) {
29 return nullptr;
30 }
31
32 return ret;
33 }
34
CreateAndMap(uint64_t size,const char * name,zx_vm_option_t map_options,fbl::RefPtr<VmarManager> vmar_manager,uint32_t cache_policy)35 zx_status_t ResizeableVmoMapper::CreateAndMap(uint64_t size,
36 const char* name,
37 zx_vm_option_t map_options,
38 fbl::RefPtr<VmarManager> vmar_manager,
39 uint32_t cache_policy) {
40 zx::vmo temp;
41 zx_status_t res = OwnedVmoMapper::CreateAndMap(size, name, map_options, std::move(vmar_manager),
42 cache_policy);
43 if (res == ZX_OK) {
44 map_options_ = map_options;
45 }
46
47 return res;
48 }
49
Map(zx::vmo vmo,uint64_t size,zx_vm_option_t map_options,fbl::RefPtr<VmarManager> vmar_manager)50 zx_status_t ResizeableVmoMapper::Map(zx::vmo vmo,
51 uint64_t size,
52 zx_vm_option_t map_options,
53 fbl::RefPtr<VmarManager> vmar_manager) {
54 zx_status_t res = OwnedVmoMapper::Map(std::move(vmo), size, map_options,
55 std::move(vmar_manager));
56 if (res == ZX_OK) {
57 map_options_ = map_options;
58 }
59
60 return res;
61 }
62
Shrink(size_t size)63 zx_status_t ResizeableVmoMapper::Shrink(size_t size) {
64 if (!vmo().is_valid()) {
65 return ZX_ERR_BAD_STATE;
66 } else if (size == 0 || size > size_) {
67 return ZX_ERR_INVALID_ARGS;
68 } else if (size == size_) {
69 return ZX_OK;
70 }
71
72 zx_status_t status;
73 zx_handle_t vmar_handle = vmar_manager_ ? vmar_manager_->vmar().get() : zx_vmar_root_self();
74
75 // Unmap everything after the offset
76 if ((status = zx_vmar_unmap(vmar_handle, start_ + size, size_ - size)) != ZX_OK) {
77 return status;
78 }
79 size_ = size;
80
81 if ((status = vmo().op_range(ZX_VMO_OP_DECOMMIT, size, size_ - size,
82 nullptr, 0)) != ZX_OK) {
83 // We can tolerate this error; from a client's perspective, the VMO
84 // still should appear smaller.
85 fprintf(stderr, "ResizeableVmoMapper::Shrink: VMO Decommit failed: %d\n", status);
86 }
87
88 return ZX_OK;
89 }
90
Grow(size_t size)91 zx_status_t ResizeableVmoMapper::Grow(size_t size) {
92 if (!vmo().is_valid()) {
93 return ZX_ERR_BAD_STATE;
94 } else if (size < size_) {
95 return ZX_ERR_INVALID_ARGS;
96 }
97
98 size = fbl::round_up<size_t>(size, ZX_PAGE_SIZE);
99 zx_status_t status;
100
101 zx_info_vmar_t vmar_info;
102 zx_handle_t vmar_handle = vmar_manager_ ? vmar_manager_->vmar().get() : zx_vmar_root_self();
103 if ((status = zx_object_get_info(vmar_handle, ZX_INFO_VMAR,
104 &vmar_info, sizeof(vmar_info), NULL, NULL)) != ZX_OK) {
105 return status;
106 }
107
108 if ((status = vmo().set_size(size)) != ZX_OK) {
109 return status;
110 }
111
112 // Try to extend mapping
113 uintptr_t new_start;
114 if ((status = zx_vmar_map(vmar_handle,
115 map_options_ | ZX_VM_FLAG_SPECIFIC,
116 start_ + size_ - vmar_info.base,
117 vmo().get(),
118 size_,
119 size - size_,
120 &new_start)) != ZX_OK) {
121 // If extension fails, create entirely new mapping and unmap the old one
122 if ((status = zx_vmar_map(vmar_handle, map_options_, 0,
123 vmo().get(), 0, size, &new_start)) != ZX_OK) {
124
125 // If we could not extend the old mapping, and we cannot create a
126 // new mapping, then we are done. Attempt to shrink the VMO back to
127 // its original size. This operation should *never* fail. If it
128 // does, something has gone insanely wrong and it is time to
129 // terminate this process.
130 zx_status_t stat2 = vmo().set_size(size_);
131 ZX_ASSERT_MSG(stat2 == ZX_OK,
132 "Failed to shrink to original size (0x%zx -> 0x%lx : res %d)\n",
133 size, this->size(), stat2);
134 return status;
135 }
136
137 // Now that we have a new mapping, unmap our original mapping. Once
138 // again, this should *never* fail. Hard assert that this is the case.
139 status = zx_vmar_unmap(vmar_handle, start_, size_);
140 ZX_ASSERT_MSG(status == ZX_OK,
141 "Failed to destroy original mapping ([%p, len 0x%lx] : res %d\n",
142 start(), this->size(), status);
143
144 start_ = new_start;
145 }
146
147 size_ = size;
148 return ZX_OK;
149 }
150
151 } // namespace fzl
152