blob: 2b9646a157ec85babd52e7104c188c5b2e2b74c1 [file] [log] [blame]
// Copyright 2023 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 "phys/boot-shim/devicetree.h"
#include <lib/boot-options/boot-options.h>
#include <lib/boot-shim/devicetree.h>
#include <lib/devicetree/devicetree.h>
#include <lib/devicetree/matcher.h>
#include <lib/memalloc/range.h>
#include <zircon/assert.h>
#include <array>
#include <variant>
#include <ktl/array.h>
#include <ktl/type_traits.h>
#include <phys/address-space.h>
#include <phys/allocation.h>
#include <phys/boot-options.h>
#include <phys/main.h>
#include <phys/uart.h>
DevicetreeBoot gDevicetreeBoot;
namespace {
// Other platforms such as Linux provide a few of preallocated buffers for storing memory ranges,
// |kMaxRanges| is a big enough upperbound for the combined number of ranges provided by such
// buffers.
//
// This represents a recommended number of entries to be allocated for storing real world memory
// ranges.
constexpr size_t kDevicetreeMaxMemoryRanges = 512;
} // namespace
void InitMemory(void* dtb, AddressSpace* aspace) {
static std::array<memalloc::Range, kDevicetreeMaxMemoryRanges> range_storage;
devicetree::ByteView fdt_blob(static_cast<const uint8_t*>(dtb),
std::numeric_limits<uintptr_t>::max());
devicetree::Devicetree fdt(fdt_blob);
boot_shim::DevicetreeMemoryMatcher memory("init-memory", stdout, range_storage);
boot_shim::DevicetreeChosenNodeMatcher<> chosen("init-memory", stdout);
ZX_ASSERT(devicetree::Match(fdt, chosen, memory));
//
// The following 'special' memory ranges are those that we already know are
// populated.
//
uint64_t phys_start = reinterpret_cast<uint64_t>(PHYS_LOAD_ADDRESS);
uint64_t phys_end = reinterpret_cast<uint64_t>(_end);
ktl::array<memalloc::Range, 3> special_range_storage = {
memalloc::Range{
.addr = phys_start,
.size = phys_end - phys_start,
.type = memalloc::Type::kPhysKernel,
},
{
.addr = reinterpret_cast<uintptr_t>(fdt.fdt().data()),
.size = fdt.size_bytes(),
.type = memalloc::Type::kDevicetreeBlob,
},
};
cpp20::span<memalloc::Range> special_ranges = cpp20::span{special_range_storage}.subspan(0, 2);
if (!chosen.zbi().empty()) {
special_range_storage[2] = memalloc::Range{
.addr = reinterpret_cast<uintptr_t>(chosen.zbi().data()),
.size = chosen.zbi().size(),
.type = memalloc::Type::kDataZbi,
};
special_ranges = special_range_storage;
}
// The matching phase above recorded all of the memory ranges encoded within
// the devicetree tree structure, leaving the memory reservations. Since
// bootloaders may sometimes generate spurious memory reservations for things
// like the devicetree blob and ramdisk, we take care to exclude those ranges
// from the translation to RESERVED ranges. This is handled by
// ForEachDevicetreeMemoryReservation below.
cpp20::span<memalloc::Range> ranges;
{
// ForEachDevicetreeMemoryReservation requires that the 'exclusions' be
// non-overlapping and sorted. The special ranges are surely
// non-overlapping.
std::sort(special_ranges.begin(), special_ranges.end(),
[](auto a, auto b) { return (a.addr < b.addr); });
size_t written = memory.ranges().size();
bool recorded = boot_shim::ForEachDevicetreeMemoryReservation(
fdt, /*exclusions=*/special_ranges, [&written](devicetree::MemoryReservation res) {
if (written >= range_storage.size()) {
return false;
}
range_storage[written++] = memalloc::Range{
.addr = res.start,
.size = res.size,
.type = memalloc::Type::kReserved,
};
return true;
});
ZX_ASSERT_MSG(recorded, "Insufficient space to record devicetree memory reservations");
ranges = cpp20::span{range_storage}.subspan(0, written);
}
// This instance of |BootOptions| is not meant to be wired anywhere, its sole purpose is to select
// the proper uart from the cmdline if its present.
static BootOptions boot_options;
// If the chosen matcher did not find a serial console setting, keep whatever
// current setting was in place before calling InitMemory. That's often the
// null driver, but could be something else.
boot_options.serial = chosen.uart().value_or(GetUartDriver().uart());
SetBootOptionsWithoutEntropy(boot_options, {}, chosen.cmdline().value_or(""));
SetUartConsole(boot_options.serial);
Allocation::Init(ranges, special_ranges);
if (aspace) {
ArchSetUpAddressSpaceEarly(*aspace);
}
Allocation::GetPool().PrintMemoryRanges(ProgramName());
gDevicetreeBoot = {
.cmdline = chosen.cmdline().value_or(""),
.ramdisk = chosen.zbi(),
.fdt = fdt,
};
}