blob: 214b35fd144de937bab95d19d0e2f44a73516226 [file] [log] [blame] [edit]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_ZBITL_CHECKING_H_
#define LIB_ZBITL_CHECKING_H_
#include <lib/fitx/result.h>
#include <zircon/assert.h>
#include <zircon/boot/image.h>
#include <string_view>
namespace zbitl {
/// Provides a mode of severity for checking the validity of ZBI items.
enum class Checking {
/// In this most forgiving of modes, only structural requirements to ensure
/// safe iteration over items in the container are checked. Catering to a
/// common denominator, this is the likely mode of choice for dealing with
/// ZBIs constructed outside of the platform.
kPermissive,
/// Extending the bounds-checking of the permissive mode, in this mode common
/// header properties are also validated (e.g., for proper 'magic' and the
/// specification of required flags).
kStrict,
/// This is the strictest mode: it extends the previous mode by also
/// validating the CRC32 of an item's payload.
kCrc,
};
/// The capacity argument is the maximum space left in the container,
/// starting from the position of this header (not its payload).
template <Checking = Checking::kStrict>
fitx::result<std::string_view> CheckHeader(const zbi_header_t& header, size_t capacity);
template <>
fitx::result<std::string_view> CheckHeader<Checking::kStrict>(const zbi_header_t& header,
size_t capacity);
template <>
fitx::result<std::string_view> CheckHeader<Checking::kPermissive>(const zbi_header_t& header,
size_t capacity);
// CRC-checking mode doesn't apply to the header.
template <>
inline fitx::result<std::string_view> CheckHeader<Checking::kCrc>(const zbi_header_t& header,
size_t capacity) {
return CheckHeader<Checking::kStrict>(header, capacity);
}
// Modify a header so that it passes checks. This can be used to mint new
// items from a designated initializer that omits uninteresting bits.
inline constexpr zbi_header_t SanitizeHeader(zbi_header_t header) {
header.magic = ZBI_ITEM_MAGIC;
header.flags |= ZBI_FLAG_VERSION;
if (!(header.flags & ZBI_FLAG_CRC32)) {
header.crc32 = ZBI_ITEM_NO_CRC32;
}
return header;
}
/// Returns empty if and only if the ZBI is complete (bootable), otherwise an
/// error string. This takes any zbitl::View type or any type that acts like
/// it. Note this does not check for errors from zbi.take_error() so if Zbi is
/// zbitl::View then the caller must use zbi.take_error() afterwards. This
/// function always scans every item so all errors Zbi::iterator detects will
/// be found. But this function's return value only indicates if the items
/// that were scanned before any errors were encountered added up to a complete
/// ZBI (regardless of whether there were additional items with errors).
template <typename Zbi>
fitx::result<std::string_view> CheckComplete(Zbi&& zbi,
uint32_t kernel_type
#ifdef __aarch64__
= ZBI_TYPE_KERNEL_ARM64
#elif defined(__x86_64__)
= ZBI_TYPE_KERNEL_X64
#endif
,
uint32_t bootfs_type = ZBI_TYPE_STORAGE_BOOTFS) {
enum {
kKernelAbsent,
kKernelFirst,
kKernelLater,
} kernel = kKernelAbsent;
bool bootfs = false;
bool empty = true;
for (auto [header, payload] : zbi) {
if (header->type == kernel_type) {
kernel = (empty && kernel == kKernelAbsent) ? kKernelFirst : kKernelLater;
} else if (header->type == bootfs_type) {
bootfs = true;
}
empty = false;
}
if (empty) {
return fitx::error("empty ZBI");
}
switch (kernel) {
case kKernelAbsent:
return fitx::error("no kernel item found");
case kKernelLater:
return fitx::error("kernel item out of order: must be first");
case kKernelFirst:
if (bootfs) { // It's complete.
return fitx::ok();
}
return fitx::error("missing BOOTFS");
}
ZX_ASSERT_MSG(false, "unreachable");
}
} // namespace zbitl
#endif // LIB_ZBITL_CHECKING_H_