blob: 4ce6a9b21fc5af06ac8051d46a0bd73b536072d7 [file] [log] [blame]
// Copyright 2020 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
#include <lib/boot-options/boot-options.h>
#include <lib/uart/all.h>
#include <lib/zbitl/view.h>
#include <stdio.h>
#include <limits>
#include <ktl/byte.h>
#include <ktl/span.h>
#include <phys/arch.h>
#include <phys/main.h>
FILE FILE::stdout_;
namespace {
using UartKernelDriver = uart::all::KernelDriver<uart::BasicIoProvider, uart::Unsynchronized>;
// Configure the global "stdout" variable to use the given UART configuration.
//
// This allows functions such as printf() to output via the UART.
void ConfigureStdout(const uart::all::Driver& uart) {
static UartKernelDriver global_uart;
global_uart = uart;
global_uart.Visit([](auto&& driver) {
driver.Init();
// Update stdout global to write to the configured driver.
FILE::stdout_ = FILE{&driver};
});
}
} // namespace
void PhysMain(void* zbi, arch::EarlyTicks ticks) {
// Apply any relocations required to ourself.
ApplyRelocations();
// Initially set up stdout to write to the null uart driver.
ConfigureStdout(uart::null::Driver());
// Scan through the ZBI looking for items that configure the serial console.
// Note that as each item is encountered, it resets uart to the appropriate
// variant and sets its configuration values. So a later item will override
// the selection and configuration of an earlier item. But this all happens
// before anything touches hardware.
UartKernelDriver uart;
zbitl::View<zbitl::ByteView> zbi_view{
zbitl::StorageFromRawHeader(static_cast<const zbi_header_t*>(zbi))};
for (auto [header, payload] : zbi_view) {
uart.Match(*header, payload.data());
}
// Don't bother with any errors reading the ZBI. Either the console got set
// up or it didn't. If the program cares about the ZBI being valid, it will
// scan it again.
zbi_view.ignore_error();
// Initialize kernel.serial from whatever we chose based on ZBI items.
static BootOptions boot_opts;
boot_opts.serial = uart.uart();
// Now process command line items from the ZBI to set boot options. This is
// a separate loop so that kernel.serial settings override any ZBI item that
// chose a UART, regardless of the relative order of UART and CMDLINE items.
// The last word in the last CMDLINE item always wins.
for (auto [header, payload] : zbi_view) {
if (header->type == ZBI_TYPE_CMDLINE) {
boot_opts.SetMany({reinterpret_cast<const char*>(payload.data()), payload.size()});
}
}
zbi_view.ignore_error();
// Configure the selected UART.
//
// Note we don't do this after parsing ZBI items and before parsing command
// line options, because if kernel.serial overrode what the ZBI items said,
// we shouldn't be sending output to the wrong UART in between.
ConfigureStdout(boot_opts.serial);
// The global is a pointer just for uniformity between the code in phys and
// in the kernel proper.
gBootOptions = &boot_opts;
// Perform any architecture-specific set up.
ArchSetUp();
// Call the real entry point now that it can use printf! It does not return.
ZbiMain(zbi, ticks);
}