blob: ee54949319e202b3f54402ee41cadf46568ba33c [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.
#include "phys/efi/main.h"
#include <lib/abr/abr.h>
#include <stdio.h>
#include <phys/stdio.h>
#include "backends.h"
#include "fastboot_tcp.h"
#include "gigaboot/src/netifc.h"
#include "input.h"
#include "lib/zircon_boot/zircon_boot.h"
#include "utils.h"
#include "xefi.h"
#include "zircon_boot_ops.h"
#if defined(__x86_64__)
#include <cpuid.h>
#endif
// This is necessary because the Fastboot class inherits from FastbootBase,
// which is an abstract class with pure virtual functions.
// The _purecall definition is not provided in efi compilation, which leads to an
// 'undefined' error in asan builds.
extern "C" int _purecall(void) { return 0; }
namespace {
// Check for KVM or non-KVM QEMU using Hypervisor Vendor ID.
bool IsQemu() {
#if defined(__x86_64__)
uint32_t eax;
uint32_t name[3];
__cpuid(0x40000000, eax, name[0], name[1], name[2]);
std::string_view name_str(reinterpret_cast<const char*>(name), sizeof(name));
return name_str == "TCGTCGTCGTCG"sv || name_str == "KVMKVMKVM\0\0\0"sv;
#elif defined(__aarch64__)
uint64_t cpu_info;
__asm__ volatile("mrs %0, MIDR_EL1" : "=r"(cpu_info));
// Bits [31:24] define the implementor: 0x00 is "Reserved for software use".
return (cpu_info & 0xFF000000) == 0x0;
#else
return false;
#endif
}
// Always enable serial output if not already. Infra relies on serial to get device feedback.
// Without it, some CI/CQs fail.
void SetSerial() {
PhysConsole& console = PhysConsole::Get();
if (*console.serial() != FILE()) {
return;
}
// QEMU routes serial output to the console. To avoid double printing, avoid using serial
// output when running in QEMU.
if (IsQemu()) {
return;
}
// Temporarily set `gEfiSystemTable->ConOut` to NULL to force serial console setup.
// This won't affect the graphic set up done earlier.
//
// This function can be removed once SetEfiStdout() can correctly set up both graphics and serial
// output.
auto conn_out = gEfiSystemTable->ConOut;
gEfiSystemTable->ConOut = nullptr;
SetEfiStdout(gEfiSystemTable);
gEfiSystemTable->ConOut = conn_out;
}
} // namespace
// TODO(b/285053546) 'BootByte' usage should be removed in favour of ABR Metadata
bool ResetRebootMode(gigaboot::RebootMode reboot_mode, const AbrOps& abr_ops) {
if (reboot_mode != gigaboot::RebootMode::kNormal &&
!SetRebootMode(gigaboot::RebootMode::kNormal)) {
printf("Failed to reset reboot mode\n");
return false;
}
return true;
}
int main(int argc, char** argv) {
SetSerial();
printf("Gigaboot main\n");
auto is_secureboot_on = gigaboot::IsSecureBootOn();
if (is_secureboot_on.is_error()) {
printf("Failed to query SecureBoot variable\n");
} else {
printf("Secure Boot: %s\n", *is_secureboot_on ? "On" : "Off");
}
// TODO(b/235489025): We reuse some legacy C gigaboot code for stuff like network stack.
// This initializes the global variables the legacy code needs. Once these needed features are
// re-implemented, remove these dependencies.
xefi_init(gEfiImageHandle, gEfiSystemTable);
// Log TPM info if the device has one.
if (efi_status res = gigaboot::PrintTpm2Capability(); res != EFI_SUCCESS) {
printf("Failed to log TPM 2.0 capability %s. TPM 2.0 may not be supported\n",
gigaboot::EfiStatusToString(res));
}
// Print OneShotFlags from ABR
AbrDataOneShotFlags one_shot_flags;
ZirconBootOps zb_ops = gigaboot::GetZirconBootOps();
AbrOps abr_ops = GetAbrOpsFromZirconBootOps(&zb_ops);
AbrResult abr_res = AbrGetAndClearOneShotFlags(&abr_ops, &one_shot_flags);
if (abr_res != kAbrResultOk) {
printf("Failed to get one shot flags from ABR\n");
return 1;
}
printf("abr.one_shot_flags = 0x%02x\n", one_shot_flags);
gigaboot::RebootMode reboot_mode =
gigaboot::GetRebootMode(one_shot_flags).value_or(gigaboot::RebootMode::kNormal);
// TODO(b/285053546) 'BootByte' usage should be removed in favour of ABR Metadata
// Reset previous reboot mode immediately to prevent it from being sticky.
if (!ResetRebootMode(reboot_mode, abr_ops)) {
printf("Failed to reset reboot mode\n");
return 1;
}
bool enter_fastboot = reboot_mode == gigaboot::RebootMode::kBootloader;
if (enter_fastboot) {
printf(
"Your BIOS or ABR instructed Gigaboot to enter fastboot directly and skip normal boot.\n");
} else {
constexpr zx::duration timeout = zx::sec(2);
gigaboot::InputReceiver receiver(gEfiSystemTable);
printf("Press f to enter fastboot.\n");
std::optional<char> key = receiver.GetKeyPrompt("f", timeout, "Auto boot in");
enter_fastboot = key == 'f';
}
if (enter_fastboot) {
zx::result<> ret = gigaboot::FastbootTcpMain();
if (ret.is_error()) {
printf("Fastboot failed\n");
return 1;
}
}
ZirconBootFlags boot_flags = reboot_mode == gigaboot::RebootMode::kRecovery
? kZirconBootFlagsForceRecovery
: kZirconBootFlagsNone;
// TODO(b/236039205): Implement logic to construct these arguments for the API. This
// is currently a placeholder for testing compilation/linking.
ZirconBootOps zircon_boot_ops = gigaboot::GetZirconBootOps();
ZirconBootResult res = LoadAndBoot(&zircon_boot_ops, boot_flags);
if (res != kBootResultOK) {
printf("Failed to boot zircon\n");
return 1;
}
return 0;
}