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 <lib/rodso.h>
8 
9 #include <inttypes.h>
10 #include <vm/vm_address_region.h>
11 #include <vm/vm_aspace.h>
12 #include <vm/vm_object.h>
13 #include <vm/vm_object_paged.h>
14 #include <object/handle.h>
15 #include <object/vm_address_region_dispatcher.h>
16 #include <object/vm_object_dispatcher.h>
17 
RoDso(const char * name,const void * image,size_t size,uintptr_t code_start)18 RoDso::RoDso(const char* name, const void* image, size_t size,
19              uintptr_t code_start)
20     : name_(name), code_start_(code_start), size_(size) {
21     DEBUG_ASSERT(IS_PAGE_ALIGNED(size));
22     DEBUG_ASSERT(IS_PAGE_ALIGNED(code_start));
23     DEBUG_ASSERT(code_start > 0);
24     DEBUG_ASSERT(code_start < size);
25     fbl::RefPtr<Dispatcher> dispatcher;
26 
27     // create vmo out of ro data mapped in kernel space
28     fbl::RefPtr<VmObject> vmo;
29     zx_status_t status = VmObjectPaged::CreateFromROData(image, size, &vmo);
30     ASSERT(status == ZX_OK);
31 
32     // build and point a dispatcher at it
33     status = VmObjectDispatcher::Create(
34         ktl::move(vmo),
35         &dispatcher, &vmo_rights_);
36     ASSERT(status == ZX_OK);
37 
38     status = dispatcher->set_name(name, strlen(name));
39     ASSERT(status == ZX_OK);
40     vmo_ = DownCastDispatcher<VmObjectDispatcher>(&dispatcher);
41     vmo_rights_ &= ~ZX_RIGHT_WRITE;
42 
43     // unmap it from the kernel
44     // NOTE: this means the image can no longer be referenced from original pointer
45     status = VmAspace::kernel_aspace()->arch_aspace().Unmap(
46             reinterpret_cast<vaddr_t>(image),
47             size / PAGE_SIZE, nullptr);
48     ASSERT(status == ZX_OK);
49 }
50 
vmo_handle() const51 HandleOwner RoDso::vmo_handle() const {
52     return Handle::Make(vmo_, vmo_rights_);
53 }
54 
55 // Map one segment from our VM object.
MapSegment(fbl::RefPtr<VmAddressRegionDispatcher> vmar,bool code,size_t vmar_offset,size_t start_offset,size_t end_offset) const56 zx_status_t RoDso::MapSegment(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
57                               bool code,
58                               size_t vmar_offset,
59                               size_t start_offset,
60                               size_t end_offset) const {
61 
62     uint32_t flags = ZX_VM_SPECIFIC | ZX_VM_PERM_READ;
63     if (code)
64         flags |= ZX_VM_PERM_EXECUTE;
65 
66     size_t len = end_offset - start_offset;
67 
68     fbl::RefPtr<VmMapping> mapping;
69     zx_status_t status = vmar->Map(vmar_offset, vmo_->vmo(),
70                                    start_offset, len, flags, &mapping);
71 
72     const char* segment_name = code ? "code" : "rodata";
73     if (status != ZX_OK) {
74         dprintf(CRITICAL,
75                 "userboot: %s %s mapping %#zx @ %#" PRIxPTR
76                 " size %#zx failed %d\n",
77                 name_, segment_name, start_offset,
78                 vmar->vmar()->base() + vmar_offset, len, status);
79     } else {
80         DEBUG_ASSERT(mapping->base() == vmar->vmar()->base() + vmar_offset);
81         dprintf(SPEW, "userboot: %-8s %-6s %#7zx @ [%#" PRIxPTR
82                 ",%#" PRIxPTR ")\n", name_, segment_name, start_offset,
83                 mapping->base(), mapping->base() + len);
84     }
85 
86     return status;
87 }
88 
Map(fbl::RefPtr<VmAddressRegionDispatcher> vmar,size_t offset) const89 zx_status_t RoDso::Map(fbl::RefPtr<VmAddressRegionDispatcher> vmar,
90                        size_t offset) const {
91     zx_status_t status = MapSegment(vmar, false, offset, 0, code_start_);
92     if (status == ZX_OK)
93         status = MapSegment(ktl::move(vmar), true,
94                             offset + code_start_, code_start_, size_);
95     return status;
96 }
97