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
18RoDso::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        fbl::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
51HandleOwner RoDso::vmo_handle() const {
52    return Handle::Make(vmo_, vmo_rights_);
53}
54
55// Map one segment from our VM object.
56zx_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
89zx_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(fbl::move(vmar), true,
94                            offset + code_start_, code_start_, size_);
95    return status;
96}
97