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