blob: 7090d4cec3741d9e423a06f84b734f7f9c4fe0b3 [file] [log] [blame]
// Copyright 2022 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 SRC_FIRMWARE_GIGABOOT_CPP_UTILS_H_
#define SRC_FIRMWARE_GIGABOOT_CPP_UTILS_H_
#include <lib/abr/ops.h>
#include <lib/stdcompat/span.h>
#include <lib/zx/time.h>
#include <xefi.h>
#include <optional>
#include <string_view>
#include <efi/boot-services.h>
#include <efi/protocol/block-io.h>
#include <efi/protocol/device-path.h>
#include <efi/protocol/disk-io.h>
#include <efi/protocol/file.h>
#include <efi/protocol/graphics-output.h>
#include <efi/protocol/managed-network.h>
#include <efi/protocol/serial-io.h>
#include <efi/protocol/simple-text-output.h>
#include <efi/protocol/tcg2.h>
#include <efi/system-table.h>
#include <efi/types.h>
#include <fbl/vector.h>
#include <phys/efi/main.h>
#include <phys/efi/protocol.h>
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_device_path_protocol> = DevicePathProtocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_block_io_protocol> = BlockIoProtocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_disk_io_protocol> = DiskIoProtocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_tcg2_protocol> = Tcg2Protocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_graphics_output_protocol> =
GraphicsOutputProtocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_serial_io_protocol> = SerialIoProtocol;
template <>
inline constexpr const efi_guid& kEfiProtocolGuid<efi_managed_network_protocol> =
ManagedNetworkProtocol;
namespace gigaboot {
// This is a utility function useful for calculating e.g. the number of pages to allocate to back a
// certain number of bytes, or the number of disk blocks to back a partition, rounding up for a
// margin of safety.
template <typename T>
auto constexpr DivideRoundUp(T t1, T t2) -> decltype(t1 + t2) {
return (t1 + t2 - 1) / t2;
}
// This calls into the LocateProtocol() boot service. It returns a null pointer if operation fails.
template <class Protocol>
inline fit::result<efi_status, EfiProtocolPtr<Protocol>> EfiLocateProtocol() {
void* ptr = nullptr;
efi_status status =
gEfiSystemTable->BootServices->LocateProtocol(&kEfiProtocolGuid<Protocol>, nullptr, &ptr);
if (status != EFI_SUCCESS) {
return fit::error{status};
}
return fit::ok(EfiProtocolPtr<Protocol>(static_cast<Protocol*>(ptr)));
}
// A wrapper type for the list of handles returned by LocateHandleBuffer() boot service. It owns
// the memory backing the list and will free it upon destruction
class HandleBuffer {
public:
HandleBuffer(efi_handle* handles, size_t count) : handles_(handles), count_(count) {}
cpp20::span<efi_handle> AsSpan() { return cpp20::span<efi_handle>{handles_.get(), count_}; }
private:
// The deleter frees the list by calling the FreePool() boot service.
struct HandlePtrDeleter {
void operator()(efi_handle* ptr) { gEfiSystemTable->BootServices->FreePool(ptr); }
};
std::unique_ptr<efi_handle, HandlePtrDeleter> handles_;
const size_t count_ = 0;
};
// This calls into LocateHandleBuffer() with ByProtocol search type and the given protocol.
// It returns a list of efi_handles that support the given protocol
template <class Protocol>
inline fit::result<efi_status, HandleBuffer> EfiLocateHandleBufferByProtocol() {
size_t count;
efi_handle* handles;
efi_status status = gEfiSystemTable->BootServices->LocateHandleBuffer(
ByProtocol, &kEfiProtocolGuid<Protocol>, nullptr, &count, &handles);
if (status != EFI_SUCCESS) {
return fit::error{status};
}
return fit::ok(HandleBuffer(handles, count));
}
// Convert a given efi_status code to informative string.
const char* EfiStatusToString(efi_status status);
// Convert efi memory type code to zbi memory type code.
uint32_t EfiToZbiMemRangeType(uint32_t efi_mem_type);
// Convert an integer to big endian byte order
uint64_t ToBigEndian(uint64_t val);
// Convert an given integer, assuming in big endian to little endian order.
uint64_t BigToHostEndian(uint64_t val);
constexpr size_t kUefiPageSize = 4096;
efi_status PrintTpm2Capability();
// Check whether secure boot is turned on by querying the `SecureBoot` global variable.
// Returns error if fail to query `SecureBoot`.
fit::result<efi_status, bool> IsSecureBootOn();
// Can't include gpt.h due to circular includes so declare the type here.
class EfiGptBlockDevice;
std::string_view MaybeMapPartitionName(const EfiGptBlockDevice& device, std::string_view partition);
enum class RebootMode : uint8_t {
kNormal = 0x1,
kRecovery = 0x2,
kBootloader = 0x4,
kBootloaderDefault = 0xFF,
};
constexpr uint8_t RebootModeToByte(RebootMode m) { return static_cast<uint8_t>(m); }
// Set reboot mode. Returns true if succeeds, false otherwise.
bool SetRebootMode(RebootMode mode);
// Get reboot mode.
// Returns std::nullopt on failure
std::optional<RebootMode> GetRebootMode(AbrDataOneShotFlags one_shot_flags);
// Convert hex string to `efi_guid`
// Input string should be in RFC4122 "registry format"
// `aabbccdd-eeff-gghh-iijj-kkllmmnnoopp`
fit::result<efi_status, efi_guid> ToGuid(std::string_view guid_str);
// Convert `efi_guid` to string according to https://www.rfc-editor.org/rfc/rfc4122
// Return value is '\0' terminated.
constexpr size_t kByteToHexLen = 2;
constexpr size_t kEfiGuidStrLen = (sizeof(efi_guid) * kByteToHexLen + 4 /*dashes*/);
fbl::Vector<char> ToStr(const efi_guid& g);
// A wrapper around a UEFI timer.
class Timer {
public:
enum class Status {
kReady,
kWaiting,
kError,
};
explicit Timer(efi_system_table* sys) : sys_(sys) {}
Timer() = delete;
Timer(const Timer&) = delete;
Timer(Timer&&) = delete;
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&&) = delete;
~Timer() {
// The timer event is lazily constructed, so it may be null.
if (timer_event_) {
gEfiSystemTable->BootServices->CloseEvent(timer_event_);
}
}
// Configure the timer's behavior.
//
// Both 0 and zx::duration::infinite() are valid values for timeout.
// See the comment for CheckTimer for their semantics.
fit::result<efi_status> SetTimer(efi_timer_delay type, zx::duration timeout);
// Check to see if the timer has expired or if an error has occurred.
//
// A timer set with a duration of 0 will ALWAYS return Status::kReady.
// A timer set with an infinite duration will ALWAYS return Status::kWaiting.
// Calling CheckTimer on a timer that has not been set will return Status::kError.
Status CheckTimer();
// Cancel the timer.
fit::result<efi_status> Cancel();
private:
enum class State {
kNormal,
kZero,
kInfinite,
};
State state_ = State::kNormal;
efi_system_table* sys_;
efi_event timer_event_ = nullptr;
};
} // namespace gigaboot
#endif // SRC_FIRMWARE_GIGABOOT_CPP_UTILS_H_