| // 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; |
| } |