blob: 7df7cccd6213a7ff4ec33cbf5558390268880701 [file] [log] [blame]
// Copyright 2021 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
#ifndef ZIRCON_KERNEL_PHYS_INCLUDE_PHYS_BOOT_ZBI_H_
#define ZIRCON_KERNEL_PHYS_INCLUDE_PHYS_BOOT_ZBI_H_
#include <lib/arch/zbi-boot.h>
#include <lib/fitx/result.h>
#include <lib/zbitl/image.h>
#include <lib/zbitl/view.h>
#include <cstdint>
#include <ktl/span.h>
#include <phys/allocation.h>
// BootZbi represents a bootable ZBI and manages the memory allocation and ZBI
// protocol details for getting its kernel image and data ZBI in place and
// handing off control.
//
// BootZbi is a move-only object that can only be created by BootZbi::Create
// from a valid input ZBI. After construction yields information about the
// kernel's load requirements before doing anything. Then Load() does any
// necessary allocations via Allocation::New and/or reuses the input ZBI's
// storage() space. DataZbi() can then be amended before calling Boot().
class BootZbi {
public:
using Bytes = ktl::span<std::byte>;
using InputZbi = zbitl::View<zbitl::ByteView>;
using Zbi = zbitl::Image<Bytes>;
using Error = InputZbi::CopyError<Bytes>;
struct Size {
size_t size, alignment;
};
// Default-constructible and move-only.
BootZbi() = default;
BootZbi(BootZbi&&) = default;
BootZbi& operator=(BootZbi&&) = default;
// Suggest allocation parameters for a whole bootable ZBI image whose
// incoming size is known but whose contents haven't been seen yet. A
// conforming allocation will be optimal for reuse by Load().
static Size SuggestedAllocation(uint32_t zbi_size_bytes);
// This initializes a new BootZbi object representing the input ZBI. This is
// to be called on a default-constructed object, which should not otherwise
// be used before calling Init() or being moved-into from a BootZbi object on
// which Init() has been called. The initialized object describes the kernel
// image in place before loading.
fitx::result<Error> Init(InputZbi zbi);
// Load the kernel and data ZBIs from the input ZBI. The data ZBI will have
// at least extra_data_capacity bytes of space available to Append() items to
// it. The input ZBI's memory may be reused for the kernel or data images,
// so it should not be referenced afterwards.
fitx::result<Error> Load(uint32_t extra_data_capacity = 0);
// Boot into the kernel loaded by Load(), which must have been called first.
// This cannot fail and never returns.
[[noreturn]] void Boot();
// The Kernel* methods can be used at any time, even before Load().
const zircon_kernel_t* KernelImage() const { return kernel_; }
const zbi_kernel_t* KernelHeader() const { return &KernelImage()->data_kernel; }
uint64_t KernelLoadAddress() const { return reinterpret_cast<uintptr_t>(KernelImage()); }
uint32_t KernelLoadSize() const {
return static_cast<uint32_t>(offsetof(zircon_kernel_t, data_kernel)) +
KernelImage()->hdr_kernel.length;
}
uint64_t KernelMemorySize() const {
return KernelLoadSize() + KernelHeader()->reserve_memory_size;
}
uint64_t KernelEntryAddress() const { return KernelLoadAddress() + KernelHeader()->entry; }
// If this returns true, then instead of using Load() it works to just assign
// to DataZbi().storage() with a different data location and use the kernel
// image already in place after construction by Init().
bool KernelCanLoadInPlace() const {
return KernelLoadAddress() % arch::kZbiBootKernelAlignment == 0;
}
// The Data* methods can be used after a successful Load() or if a valid data
// ZBI has been installed directly into DataZbi(). The data ZBI can be
// modified in place up to its capacity. Assigning to DataZbi() or its
// storage() after Load() is allowed if the new address is properly aligned.
Zbi& DataZbi() { return data_; }
uint64_t DataLoadAddress() const { return reinterpret_cast<uint64_t>(data_.storage().data()); }
uint64_t DataLoadSize() { return static_cast<uint64_t>(data_.size_bytes()); }
private:
// These are set on construction by Init().
InputZbi zbi_;
InputZbi::iterator kernel_item_;
// These are set by Load().
Allocation kernel_buffer_, data_buffer_;
Zbi data_;
// This points to the kernel load image at its aligned start, i.e. the start
// of the container header before the kernel item. In some cases there isn't
// actually a container header there (at kernel_->hdr_file), so we only refer
// only to the kernel item header (at kernel_->hdr_kernel) and payload. At
// construction by Init() this points to just before kernel_item_. After
// Load() it may instead point into kernel_buffer_.data(), but is guaranteed
// to be aligned to arch::kZbiBootKernelAlignment and to have enough memory
// after the load image for the requested reserves (either allocated in
// kernel_buffer_ or reusing zbi_.storage() when no longer in use for data).
const zircon_kernel_t* kernel_ = nullptr;
};
#endif // ZIRCON_KERNEL_PHYS_INCLUDE_PHYS_BOOT_ZBI_H_