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 <libzbi/zbi-zx.h>
6
7#include <fbl/type_support.h>
8#include <lib/zx/vmar.h>
9#include <lib/zx/vmo.h>
10#include <limits.h>
11#include <string.h>
12#include <zircon/assert.h>
13
14namespace zbi {
15
16namespace {
17
18size_t PageRound(size_t size) {
19    return (size + PAGE_SIZE) & -(size_t)PAGE_SIZE;
20}
21
22}
23
24zx_status_t ZbiVMO::Init(zx::vmo vmo) {
25    vmo_ = fbl::move(vmo);
26    auto status = vmo_.get_size(&capacity_);
27    if (status == ZX_OK && capacity_ > 0) {
28        status = Map();
29    }
30    return status;
31}
32
33zx::vmo ZbiVMO::Release() {
34    Unmap();
35    capacity_= 0;
36    return fbl::move(vmo_);
37}
38
39ZbiVMO::~ZbiVMO() {
40    Unmap();
41}
42
43zx_status_t ZbiVMO::Map() {
44    uintptr_t mapping;
45    auto status = zx::vmar::root_self()->map(
46        0, vmo_, 0, capacity_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
47        &mapping);
48    if (status == ZX_OK) {
49        base_ = reinterpret_cast<uint8_t*>(mapping);
50    }
51    return status;
52}
53
54void ZbiVMO::Unmap() {
55    if (base_) {
56        [[maybe_unused]] auto status =
57            zx::vmar::root_self()->unmap(reinterpret_cast<uintptr_t>(base_),
58                                         capacity_);
59        ZX_DEBUG_ASSERT(status == ZX_OK);
60        base_ = nullptr;
61    }
62}
63
64zbi_result_t ZbiVMO::AppendSection(uint32_t length, uint32_t type,
65                                   uint32_t extra, uint32_t flags,
66                                   const void* payload) {
67    void* dest;
68    auto result = CreateSection(length, type, extra, flags, &dest);
69    if (result == ZBI_RESULT_OK) {
70        memcpy(dest, payload, length);
71    }
72    return result;
73}
74
75zbi_result_t ZbiVMO::CreateSection(uint32_t length, uint32_t type,
76                                   uint32_t extra, uint32_t flags,
77                                   void** payload) {
78    auto result = Zbi::CreateSection(length, type, extra, flags, payload);
79    if (result == ZBI_RESULT_TOO_BIG) {
80        const size_t new_capacity =
81            PageRound(Length() + sizeof(zbi_header_t) + length);
82        ZX_DEBUG_ASSERT(new_capacity > capacity_);
83        auto status = vmo_.set_size(new_capacity);
84        if (status == ZX_OK) {
85            Unmap();
86            capacity_ = new_capacity;
87            status = Map();
88        }
89        if (status == ZX_OK) {
90            result = Zbi::CreateSection(length, type, extra, flags, payload);
91        }
92    }
93    return result;
94}
95
96zbi_result_t ZbiVMO::SplitComplete(ZbiVMO* kernel, ZbiVMO* data) const {
97    // First check that it's a proper complete ZBI.  After this it should be
98    // safe to trust the headers (modulo racing modification of the original
99    // VMO, which we can't help).
100    auto result = CheckComplete();
101    if (result != ZBI_RESULT_OK) {
102        return result;
103    }
104
105    // First clone a VMO covering just the leading kernel portion of the ZBI.
106    auto kernel_hdr = Header() + 1;
107    const uint32_t kernel_size =
108        static_cast<uint32_t>(sizeof(zbi_header_t) * 2) + kernel_hdr->length;
109    const size_t kernel_vmo_size = PageRound(kernel_size);
110    auto status = vmo_.clone(ZX_VMO_CLONE_COPY_ON_WRITE, 0, kernel_vmo_size,
111                             &kernel->vmo_);
112    if (status != ZX_OK) {
113        return ZBI_RESULT_TOO_BIG;
114    }
115
116    // Map it in.
117    kernel->Unmap();                    // Just in case.
118    kernel->capacity_ = kernel_vmo_size;
119    status = kernel->Map();
120    if (status != ZX_OK) {
121        return ZBI_RESULT_TOO_BIG;
122    }
123    // Update the size in the copied container header.
124    kernel->Header()->length =
125        kernel_size - static_cast<uint32_t>(sizeof(zbi_header_t));
126
127    // Now create (or clone if possible) a VMO for the remainder.
128    const uint32_t data_payload_size = Length() - kernel_size;
129    const size_t data_vmo_size = PageRound(
130        data_payload_size + static_cast<uint32_t>(sizeof(zbi_header_t)));
131
132    // If by some miracle the remainder is aligned exactly right, then
133    // we can clone the trailing portion as well.
134    bool clone = (kernel_size - sizeof(zbi_header_t)) % PAGE_SIZE == 0;
135    status = clone ?
136        vmo_.clone(ZX_VMO_CLONE_COPY_ON_WRITE,
137                   kernel_size - sizeof(zbi_header_t),
138                   data_vmo_size, &data->vmo_) :
139        vmo_.create(data_vmo_size, 0, &data->vmo_);
140    if (status != ZX_OK) {
141        return ZBI_RESULT_TOO_BIG;
142    }
143
144    // Map it in.
145    data->Unmap();                      // Just in case.
146    data->capacity_ = data_vmo_size;
147    status = data->Map();
148    if (status != ZX_OK) {
149        return ZBI_RESULT_TOO_BIG;
150    }
151
152    // Fill in the header and data (if not already virtually copied).
153    *data->Header() = (zbi_header_t)ZBI_CONTAINER_HEADER(data_payload_size);
154    if (!clone) {
155        memcpy(data->Payload(), Base() + kernel_size, data_payload_size);
156    }
157
158    return ZBI_RESULT_OK;
159}
160
161// C API wrapper.
162zbi_result_t SplitCompleteWrapper(zx_handle_t zbi_vmo,
163                                  zx_handle_t* kernel_vmo,
164                                  zx_handle_t* data_vmo) {
165    ZbiVMO zbi, kernel, data;
166    auto status = zbi.Init(zx::vmo(zbi_vmo));
167    if (status != ZX_OK) {
168        return ZBI_RESULT_TOO_BIG;
169    }
170    auto result = zbi.SplitComplete(&kernel, &data);
171    if (result == ZBI_RESULT_OK) {
172        *kernel_vmo = kernel.vmo_.release();
173        *data_vmo = data.vmo_.release();
174    }
175    return result;
176}
177
178zbi_result_t zbi_split_complete(zx_handle_t zbi_vmo,
179                                zx_handle_t* kernel_vmo,
180                                zx_handle_t* data_vmo) {
181    return SplitCompleteWrapper(zbi_vmo, kernel_vmo, data_vmo);
182}
183
184}  // namespace zbi
185