blob: 8a7e158a2547218f4654068a0839c892dc4224b0 [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/zbitl/items/mem_config.h>
#include <lib/zbitl/view.h>
#include <lib/zx/status.h>
#include <stdio.h>
#include <zircon/boot/e820.h>
#include <zircon/boot/image.h>
#include <zircon/limits.h>
#include <efi/boot-services.h>
#include <fbl/span.h>
namespace zbitl {
namespace {
using zbitl::internal::ToMemRange;
// Reinterpret the given ByteView as span of type T.
template <typename T>
fbl::Span<const T> ByteViewAsSpan(ByteView bytes) {
return fbl::Span<const T>(reinterpret_cast<const T*>(bytes.data()), bytes.size() / sizeof(T));
}
// Get the number of memory ranges in the given ZBI payload, assuming it
// is encoded as the given type.
size_t MemRangeElementCount(uint32_t type, ByteView payload) {
switch (type) {
case ZBI_TYPE_E820_TABLE:
return payload.size() / sizeof(e820entry_t);
case ZBI_TYPE_MEM_CONFIG:
return payload.size() / sizeof(zbi_mem_range_t);
case ZBI_TYPE_EFI_MEMORY_MAP:
return payload.size() / sizeof(efi_memory_descriptor);
default:
return 0;
}
}
// Get the n'th element from the given ZBI payload.
zbi_mem_range_t MemRangeElement(uint32_t type, ByteView payload, size_t n) {
switch (type) {
case ZBI_TYPE_E820_TABLE:
return ToMemRange(ByteViewAsSpan<e820entry_t>(payload)[n]);
case ZBI_TYPE_MEM_CONFIG:
return ByteViewAsSpan<zbi_mem_range_t>(payload)[n];
case ZBI_TYPE_EFI_MEMORY_MAP:
return ToMemRange(ByteViewAsSpan<efi_memory_descriptor>(payload)[n]);
default:
ZX_PANIC("Attempted to get element of non-memory payload type %" PRIx32 "\n", type);
}
}
} // 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
MemRangeTable::MemRangeTable() = default;
MemRangeTable::MemRangeTable(View<ByteView> view) : view_(std::move(view)) {}
MemRangeTable::iterator::iterator(MemRangeTable* parent) : parent_(parent) {
// Search for the first table element.
if (parent_ != nullptr) {
++*this;
}
}
MemRangeTable::iterator::iterator(MemRangeTable* parent, View<ByteView>::iterator it, size_t offset)
: parent_(parent), it_(it), offset_(offset) {}
MemRangeTable::iterator MemRangeTable::end() {
return MemRangeTable::iterator(this, view_.end(), 0);
}
MemRangeTable::iterator MemRangeTable::begin() {
// If we have a default-constructed ZBI, just return an empty iterator.
if (view_.storage().empty()) {
return end();
}
// Otherwise, begin iteration.
return MemRangeTable::iterator(this);
}
bool MemRangeTable::iterator::operator==(const iterator& other) const {
return std::tie(parent_, it_, offset_) == std::tie(other.parent_, other.it_, other.offset_);
}
zbi_mem_range_t MemRangeTable::iterator::operator*() const {
// Ensure user has called `Next()` at least once, and hasn't reached the end.
ZX_DEBUG_ASSERT_MSG(parent_ != nullptr, "Attempting to access invalid iterator.");
ZX_DEBUG_ASSERT_MSG(it_.has_value() && *it_ != parent_->view_.end(),
"Attempting to access invalid iterator.");
return MemRangeElement((**it_).header->type, (**it_).payload, offset_);
}
MemRangeTable::iterator& MemRangeTable::iterator::operator++() {
// Ensure we are not already at end or default-constructed.
ZX_DEBUG_ASSERT(parent_ != nullptr);
// If we have already started looking at a particular ZBI item's
// payload, keep searching through it until we reach the end.
if (it_.has_value()) {
size_t zbi_item_size = MemRangeElementCount((**it_).header->type, (**it_).payload);
++offset_;
if (offset_ < zbi_item_size) {
return *this;
}
}
// Either the previous ZBI item's payload has been exhausted, or this
// is our first call.
//
// Move to the next/first element.
if (!it_.has_value()) {
it_ = parent_->view_.begin();
} else {
it_ = ++it_.value();
}
// Keep searching until we find a valid payload.
while (it_ != parent_->view_.end()) {
size_t zbi_item_size = MemRangeElementCount((**it_).header->type, (**it_).payload);
if (zbi_item_size > 0) {
offset_ = 0;
return *this;
}
it_ = ++it_.value();
}
// Exhausted all ZBI items. Move to an end state.
it_ = parent_->view_.end();
offset_ = 0;
return *this;
}
MemRangeTable::iterator MemRangeTable::iterator::operator++(int) {
MemRangeTable::iterator old = *this;
++*this;
return old;
}
fitx::result<zbitl::View<ByteView>::Error> MemRangeTable::take_error() {
return view_.take_error();
}
} // namespace zbitl