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