blob: c2b5fa92005d3110697867cae73dafcfc70f1828 [file] [log] [blame]
// Copyright 2020 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 "mem_config.h"
#include <inttypes.h>
#include <lib/stdcompat/span.h>
#include <lib/zbitl/items/mem_config.h>
#include <lib/zbitl/view.h>
#include <lib/zx/status.h>
#include <stdio.h>
#include <zircon/assert.h>
#include <zircon/boot/e820.h>
#include <zircon/boot/image.h>
#include <zircon/limits.h>
#include <efi/boot-services.h>
namespace zbitl {
using zbitl::internal::E820Table;
using zbitl::internal::EfiTable;
using zbitl::internal::MemConfigTable;
using zbitl::internal::ToMemRange;
namespace {
// Ensure the given payload is a valid EFI memory table.
//
// The EFI memory dump format is described in the UEFI Spec (version 2.8),
// Section 7.2 under "EFI_BOOT_SERVICES.GetMemoryMap()".
//
// The format consists of a 64-bit `entry_size` value, followed by one or more
// table entries. Each table entry consists of `entry_size` bytes, the
// beginning of each containing a `efi_memory_descriptor` structure.
//
// We return "true" if the table is valid, along with the number of entries and
// the size of each entry. Otherwise, we return false.
bool ParseEfiPayload(ByteView payload, size_t* num_entries, size_t* entry_size) {
*num_entries = 0;
*entry_size = 0;
if (payload.size() < sizeof(uint64_t)) {
return false;
}
*entry_size = static_cast<size_t>(*reinterpret_cast<const uint64_t*>(payload.data()));
if (*entry_size < sizeof(efi_memory_descriptor)) {
return false;
}
if (*entry_size % alignof(efi_memory_descriptor) != 0) {
return false;
}
if ((payload.size() - sizeof(uint64_t)) % *entry_size != 0) {
return false;
}
*num_entries = (payload.size() - sizeof(uint64_t)) / *entry_size;
return true;
}
zbi_mem_range_t GetTableEntry(const EfiTable& table, size_t n) {
// Ensure the index is valid.
ZX_ASSERT(n < table.num_entries);
// Convert the n'th entry.
size_t offset = sizeof(uint64_t) + n * table.entry_size;
ZX_DEBUG_ASSERT(offset + sizeof(efi_memory_descriptor) <= table.payload.size());
efi_memory_descriptor descriptor;
memcpy(&descriptor, &table.payload[offset], sizeof(descriptor));
return ToMemRange(descriptor);
}
zbi_mem_range_t GetTableEntry(const E820Table& table, size_t n) {
return ToMemRange(table.table[n]);
}
zbi_mem_range_t GetTableEntry(const MemConfigTable& table, size_t n) { return table.table[n]; }
size_t GetTableSize(const EfiTable& table) { return table.num_entries; }
size_t GetTableSize(const E820Table& table) { return table.table.size(); }
size_t GetTableSize(const MemConfigTable& table) { return table.table.size(); }
// Return "true" if the given payload type is a memory range table type.
size_t IsMemRangeType(uint32_t type) {
switch (type) {
case ZBI_TYPE_E820_TABLE:
case ZBI_TYPE_MEM_CONFIG:
case ZBI_TYPE_EFI_MEMORY_MAP:
return true;
default:
return false;
}
}
} // namespace
namespace internal {
zbi_mem_range_t ToMemRange(const e820entry_t& range) {
return zbi_mem_range_t{
.paddr = range.addr,
.length = range.size,
.type = range.type == E820_RAM ? static_cast<uint32_t>(ZBI_MEM_RANGE_RAM)
: static_cast<uint32_t>(ZBI_MEM_RANGE_RESERVED),
.reserved = 0,
};
}
zbi_mem_range_t ToMemRange(const efi_memory_descriptor& range) {
const uint32_t type = [&range]() {
switch (range.Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
case EfiConventionalMemory:
return ZBI_MEM_RANGE_RAM;
default:
return ZBI_MEM_RANGE_RESERVED;
}
}();
return zbi_mem_range_t{
.paddr = range.PhysicalStart,
.length = range.NumberOfPages * ZX_PAGE_SIZE,
.type = type,
.reserved = 0,
};
}
} // namespace internal
fitx::result<std::string_view, MemRangeTable> MemRangeTable::FromView(View<ByteView> view) {
// Find the last memory table in the ZBI.
View<ByteView>::iterator table = view.end();
for (View<ByteView>::iterator it = view.begin(); it != view.end(); ++it) {
if (IsMemRangeType(it->header->type)) {
table = it;
// keep searching, in case there is another item later.
}
}
// Return any errors we encountered during iteration.
if (auto error = view.take_error(); error.is_error()) {
return fitx::error(error.error_value().zbi_error);
}
// If nothing was found, return an error.
if (table == view.end()) {
return fitx::error("No memory information found.");
}
// Return the table.
return FromItem(table);
}
fitx::result<std::string_view, MemRangeTable> MemRangeTable::FromItem(View<ByteView>::iterator it) {
return FromSpan(it->header->type, it->payload);
}
fitx::result<std::string_view, MemRangeTable> MemRangeTable::FromSpan(uint32_t zbi_type,
ByteView payload) {
MemRangeTable result;
switch (zbi_type) {
case ZBI_TYPE_E820_TABLE:
if (payload.size() % sizeof(e820entry_t) != 0) {
return fitx::error("Invalid size for E820 table");
};
result.table_ = E820Table{AsSpan<const e820entry_t>(payload)};
break;
case ZBI_TYPE_MEM_CONFIG:
if (payload.size() % sizeof(zbi_mem_range_t) != 0) {
return fitx::error("Invalid size for MemConfig table");
}
result.table_ = internal::MemConfigTable{AsSpan<const zbi_mem_range_t>(payload)};
break;
case ZBI_TYPE_EFI_MEMORY_MAP: {
size_t num_entries;
size_t entry_size;
if (!ParseEfiPayload(payload, &num_entries, &entry_size)) {
return fitx::error("Could not parse EFI memory map");
}
result.table_ = EfiTable{
.num_entries = num_entries,
.entry_size = entry_size,
.payload = payload,
};
break;
}
default:
return fitx::error("Unknown memory table type");
}
return fitx::ok(result);
}
zbi_mem_range_t MemRangeTable::operator[](size_t n) const {
return std::visit([n](const auto& table) -> zbi_mem_range_t { return GetTableEntry(table, n); },
table_);
}
size_t MemRangeTable::size() const {
return std::visit([](const auto& table) -> size_t { return GetTableSize(table); }, table_);
}
bool MemRangeTable::iterator::operator==(const iterator& other) const {
return std::tie(parent_, offset_) == std::tie(other.parent_, other.offset_);
}
zbi_mem_range_t MemRangeTable::iterator::operator*() const {
ZX_DEBUG_ASSERT_MSG(parent_ != nullptr, "Attempting to access invalid iterator.");
return (*parent_)[offset_];
}
// Convert a zbi_mem_range_t memory type into a human-readable string.
std::string_view MemRangeTypeName(uint32_t type) {
switch (type) {
case ZBI_MEM_RANGE_RAM:
return "RAM";
case ZBI_MEM_RANGE_PERIPHERAL:
return "peripheral";
case ZBI_MEM_RANGE_RESERVED:
return "reserved";
default:
return {};
}
}
} // namespace zbitl