1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #include <object/vm_object_dispatcher.h>
8 
9 #include <vm/vm_aspace.h>
10 #include <vm/vm_object.h>
11 
12 #include <zircon/rights.h>
13 
14 #include <fbl/alloc_checker.h>
15 
16 #include <assert.h>
17 #include <err.h>
18 #include <inttypes.h>
19 #include <trace.h>
20 
21 #define LOCAL_TRACE 0
22 
Create(fbl::RefPtr<VmObject> vmo,fbl::RefPtr<Dispatcher> * dispatcher,zx_rights_t * rights)23 zx_status_t VmObjectDispatcher::Create(fbl::RefPtr<VmObject> vmo,
24                                        fbl::RefPtr<Dispatcher>* dispatcher,
25                                        zx_rights_t* rights) {
26     fbl::AllocChecker ac;
27     auto disp = new (&ac) VmObjectDispatcher(ktl::move(vmo));
28     if (!ac.check())
29         return ZX_ERR_NO_MEMORY;
30 
31     disp->vmo()->set_user_id(disp->get_koid());
32     *rights = default_rights();
33     *dispatcher = fbl::AdoptRef<Dispatcher>(disp);
34     return ZX_OK;
35 }
36 
VmObjectDispatcher(fbl::RefPtr<VmObject> vmo)37 VmObjectDispatcher::VmObjectDispatcher(fbl::RefPtr<VmObject> vmo)
38     : SoloDispatcher(ZX_VMO_ZERO_CHILDREN), vmo_(vmo) {
39         vmo_->SetChildObserver(this);
40     }
41 
~VmObjectDispatcher()42 VmObjectDispatcher::~VmObjectDispatcher() {
43     // Intentionally leave vmo_->user_id() set to our koid even though we're
44     // dying and the koid will no longer map to a Dispatcher. koids are never
45     // recycled, and it could be a useful breadcrumb.
46     vmo_->SetChildObserver(nullptr);
47 }
48 
49 
OnZeroChild()50 void VmObjectDispatcher::OnZeroChild() {
51     UpdateState(0, ZX_VMO_ZERO_CHILDREN);
52 }
53 
OnOneChild()54 void VmObjectDispatcher::OnOneChild() {
55     UpdateState(ZX_VMO_ZERO_CHILDREN, 0);
56 }
57 
get_name(char out_name[ZX_MAX_NAME_LEN]) const58 void VmObjectDispatcher::get_name(char out_name[ZX_MAX_NAME_LEN]) const {
59     canary_.Assert();
60     vmo_->get_name(out_name, ZX_MAX_NAME_LEN);
61 }
62 
set_name(const char * name,size_t len)63 zx_status_t VmObjectDispatcher::set_name(const char* name, size_t len) {
64     canary_.Assert();
65     return vmo_->set_name(name, len);
66 }
67 
Read(user_out_ptr<void> user_data,size_t length,uint64_t offset)68 zx_status_t VmObjectDispatcher::Read(user_out_ptr<void> user_data,
69                                      size_t length,
70                                      uint64_t offset) {
71     canary_.Assert();
72 
73     return vmo_->ReadUser(user_data, offset, length);
74 }
75 
Write(user_in_ptr<const void> user_data,size_t length,uint64_t offset)76 zx_status_t VmObjectDispatcher::Write(user_in_ptr<const void> user_data,
77                                       size_t length,
78                                       uint64_t offset) {
79     canary_.Assert();
80 
81     return vmo_->WriteUser(user_data, offset, length);
82 }
83 
SetSize(uint64_t size)84 zx_status_t VmObjectDispatcher::SetSize(uint64_t size) {
85     canary_.Assert();
86 
87     return vmo_->Resize(size);
88 }
89 
GetSize(uint64_t * size)90 zx_status_t VmObjectDispatcher::GetSize(uint64_t* size) {
91     canary_.Assert();
92 
93     *size = vmo_->size();
94 
95     return ZX_OK;
96 }
97 
VmoToInfoEntry(const VmObject * vmo,bool is_handle,zx_rights_t handle_rights)98 zx_info_vmo_t VmoToInfoEntry(const VmObject* vmo,
99                              bool is_handle, zx_rights_t handle_rights) {
100     zx_info_vmo_t entry = {};
101     entry.koid = vmo->user_id();
102     vmo->get_name(entry.name, sizeof(entry.name));
103     entry.size_bytes = vmo->size();
104     entry.create_options = vmo->create_options();
105     entry.parent_koid = vmo->parent_user_id();
106     entry.num_children = vmo->num_children();
107     entry.num_mappings = vmo->num_mappings();
108     entry.share_count = vmo->share_count();
109     entry.flags =
110         (vmo->is_paged() ? ZX_INFO_VMO_TYPE_PAGED : ZX_INFO_VMO_TYPE_PHYSICAL) |
111         (vmo->is_cow_clone() ? ZX_INFO_VMO_IS_COW_CLONE : 0);
112     entry.committed_bytes = vmo->AllocatedPages() * PAGE_SIZE;
113     entry.cache_policy = vmo->GetMappingCachePolicy();
114     if (is_handle) {
115         entry.flags |= ZX_INFO_VMO_VIA_HANDLE;
116         entry.handle_rights = handle_rights;
117     } else {
118         entry.flags |= ZX_INFO_VMO_VIA_MAPPING;
119     }
120     return entry;
121 }
122 
GetVmoInfo(void)123 zx_info_vmo_t VmObjectDispatcher::GetVmoInfo(void)
124 {
125     return VmoToInfoEntry(vmo().get(), true, 0);
126 }
127 
RangeOp(uint32_t op,uint64_t offset,uint64_t size,user_inout_ptr<void> buffer,size_t buffer_size,zx_rights_t rights)128 zx_status_t VmObjectDispatcher::RangeOp(uint32_t op, uint64_t offset, uint64_t size,
129                                         user_inout_ptr<void> buffer, size_t buffer_size,
130                                         zx_rights_t rights) {
131     canary_.Assert();
132 
133     LTRACEF("op %u offset %#" PRIx64 " size %#" PRIx64
134             " buffer %p buffer_size %zu rights %#x\n",
135             op, offset, size, buffer.get(), buffer_size, rights);
136 
137     switch (op) {
138         case ZX_VMO_OP_COMMIT: {
139             if ((rights & ZX_RIGHT_WRITE) == 0) {
140                 return ZX_ERR_ACCESS_DENIED;
141             }
142             // TODO: handle partial commits
143             auto status = vmo_->CommitRange(offset, size);
144             return status;
145         }
146         case ZX_VMO_OP_DECOMMIT: {
147             if ((rights & ZX_RIGHT_WRITE) == 0) {
148                 return ZX_ERR_ACCESS_DENIED;
149             }
150             // TODO: handle partial decommits
151             auto status = vmo_->DecommitRange(offset, size);
152             return status;
153         }
154         case ZX_VMO_OP_LOCK:
155         case ZX_VMO_OP_UNLOCK:
156             // TODO: handle or remove
157             return ZX_ERR_NOT_SUPPORTED;
158 
159         case ZX_VMO_OP_CACHE_SYNC:
160             if ((rights & ZX_RIGHT_READ) == 0) {
161                 return ZX_ERR_ACCESS_DENIED;
162             }
163             return vmo_->SyncCache(offset, size);
164         case ZX_VMO_OP_CACHE_INVALIDATE:
165             // A straight invalidate op requires the write right since
166             // it may drop dirty cache lines, thus modifying the contents
167             // of the VMO.
168             if ((rights & ZX_RIGHT_WRITE) == 0) {
169                 return ZX_ERR_ACCESS_DENIED;
170             }
171             return vmo_->InvalidateCache(offset, size);
172         case ZX_VMO_OP_CACHE_CLEAN:
173             if ((rights & ZX_RIGHT_READ) == 0) {
174                 return ZX_ERR_ACCESS_DENIED;
175             }
176             return vmo_->CleanCache(offset, size);
177         case ZX_VMO_OP_CACHE_CLEAN_INVALIDATE:
178             if ((rights & ZX_RIGHT_READ) == 0) {
179                 return ZX_ERR_ACCESS_DENIED;
180             }
181             return vmo_->CleanInvalidateCache(offset, size);
182         default:
183             return ZX_ERR_INVALID_ARGS;
184     }
185 }
186 
SetMappingCachePolicy(uint32_t cache_policy)187 zx_status_t VmObjectDispatcher::SetMappingCachePolicy(uint32_t cache_policy) {
188     return vmo_->SetMappingCachePolicy(cache_policy);
189 }
190 
Clone(uint32_t options,uint64_t offset,uint64_t size,bool copy_name,fbl::RefPtr<VmObject> * clone_vmo)191 zx_status_t VmObjectDispatcher::Clone(uint32_t options, uint64_t offset, uint64_t size,
192         bool copy_name, fbl::RefPtr<VmObject>* clone_vmo) {
193     canary_.Assert();
194 
195     LTRACEF("options 0x%x offset %#" PRIx64 " size %#" PRIx64 "\n",
196             options, offset, size);
197 
198     bool resizable = true;
199     if (options & ZX_VMO_CLONE_COPY_ON_WRITE) {
200         options &= ~ZX_VMO_CLONE_COPY_ON_WRITE;
201     } else {
202         return ZX_ERR_INVALID_ARGS;
203     }
204 
205     if (options & ZX_VMO_CLONE_NON_RESIZEABLE) {
206         resizable = false;
207         options &= ~ZX_VMO_CLONE_NON_RESIZEABLE;
208     }
209 
210     if (options)
211         return ZX_ERR_INVALID_ARGS;
212 
213     return vmo_->CloneCOW(resizable, offset, size, copy_name, clone_vmo);
214 }
215