blob: 4af9a4e0fe49b9581de759b4362ab802d5787540 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "phys/efi/efi-boot-zbi.h"
#include <lib/arch/zbi-boot.h>
#include <lib/memalloc/range.h>
#include <lib/zbitl/error-stdio.h>
#include <lib/zbitl/image.h>
#include <lib/zbitl/view.h>
#include <zircon/assert.h>
#include <ktl/move.h>
#include <phys/allocation.h>
#include <phys/efi/main.h>
#include <phys/symbolize.h>
#include <ktl/enforce.h>
fit::result<EfiBootZbi::Zbi::Error, EfiBootZbi::Zbi> EfiBootZbi::Load(
uint32_t extra_data_capacity) {
auto file_error = [](EfiFileZbi::Error error) {
printf("%s: Cannot load ZBI from file: ", ProgramName());
zbitl::PrintViewError(error);
return fit::error{Zbi::Error{.zbi_error = error.zbi_error}};
};
auto it = file_zbi_.begin();
if (it == file_zbi_.end()) {
if (auto result = file_zbi_.take_error(); result.is_error()) {
return file_error(result.error_value());
}
return fit::error{Zbi::Error{.zbi_error = "empty ZBI"}};
}
auto kernel_item = it;
if (kernel_item->header->type != arch::kZbiBootKernelType ||
kernel_item->header->length < sizeof(zbi_kernel_t)) {
file_zbi_.ignore_error();
return fit::error{Zbi::Error{
.zbi_error = "first item is not a valid kernel item",
}};
}
++it;
zbi_kernel_t kernel_header;
if (auto result = EfiFileZbi::Traits::Read(file_zbi_.storage(), kernel_item->payload,
&kernel_header, sizeof(kernel_header));
result.is_error()) {
return file_error({
.zbi_error = "cannot read kernel header",
.storage_error = result.error_value(),
});
}
auto zbi_copy = [this](size_t& alloc_size, memalloc::Type type, size_t alignment,
EfiFileZbi::iterator first,
EfiFileZbi::iterator last) -> fit::result<EfiFileZbi::Error, Allocation> {
alloc_size = (alloc_size + kEfiPageSize - 1) & -kEfiPageSize;
fbl::AllocChecker ac;
Allocation buffer = Allocation::New(ac, type, alloc_size, alignment);
if (!ac.check()) {
return fit::error{EfiFileZbi::Error{.zbi_error = "out of memory"}};
}
auto result = file_zbi_.Copy(buffer.data(), first, last);
if (result.is_error()) {
return fit::error{EfiFileZbi::Error{
.zbi_error = result.error_value().zbi_error,
.item_offset = result.error_value().read_offset,
.storage_error = result.error_value().read_error,
}};
}
return fit::ok(ktl::move(buffer));
};
// Set up the in-memory kernel item as the "input ZBI" like Init() would do.
// But allocate enough extra space after it to guarantee "in-place" loading.
const size_t kernel_load_size =
offsetof(arch::ZbiKernelImage, data_kernel) + kernel_item->header->length;
size_t kernel_alloc_size = kernel_load_size + kernel_header.reserve_memory_size;
if (auto result = zbi_copy(kernel_alloc_size, memalloc::Type::kKernel,
arch::kZbiBootKernelAlignment, kernel_item, it);
result.is_ok()) {
InitKernel(ktl::move(result).value());
// Now the kernel is already deemed loaded "in place" as Load() would do.
ZX_ASSERT(KernelCanLoadInPlace());
} else {
printf("%s: Cannot load kernel (%#zx bytes) into %#zx bytes space.\n", ProgramName(),
kernel_load_size, kernel_alloc_size);
file_zbi_.ignore_error();
return file_error(result.error_value());
}
// If there appear to be no data items, it might have been an iteration error
// after the kernel item.
size_t data_items_size;
if (it == file_zbi_.end()) {
if (auto result = file_zbi_.take_error(); result.is_error()) {
return file_error(result.error_value());
}
// Nope, the data ZBI really will start as an empty container.
data_items_size = 0;
} else {
data_items_size = file_zbi_.size_bytes() - it.item_offset();
}
// Set up the in-memory data ZBI like Load() would do.
const size_t data_zbi_size = sizeof(zbi_header_t) + data_items_size;
size_t data_capacity = data_zbi_size + extra_data_capacity;
if (auto result = zbi_copy(data_capacity, memalloc::Type::kDataZbi, arch::kZbiBootDataAlignment,
it, file_zbi_.end());
result.is_ok()) {
InitData(ktl::move(result).value());
} else {
printf("%s: Cannot load data ZBI (%#zx) into %#zx bytes space.\n", ProgramName(), data_zbi_size,
data_capacity);
// If there were authentically no data items, then we did take_error()
// above and can't do it again, even via ignore_error(). Otherwise, we
// have the original iteration still open that needs to be cleaned up
// though it won't have interesting error state since the copy's failure
// includes all the error state we need.
if (it != file_zbi_.end()) {
file_zbi_.ignore_error();
}
return file_error(result.error_value());
}
return fit::ok(DataZbi());
}
fit::result<EfiBootZbi::Zbi::Error> EfiBootZbi::LastChance(const EfiBootZbi::Zbi& zbi) {
DataZbi() = zbi;
ZX_ASSERT(reinterpret_cast<zbi_header_t*>(DataZbi().storage().data())->length > 32);
Log();
return fit::ok();
}