blob: 6af257fca9aa1d5229036e0f7234d51293418c5e [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
#ifndef ZIRCON_KERNEL_DEV_CORESIGHT_INCLUDE_DEV_CORESIGHT_ROM_TABLE_H_
#define ZIRCON_KERNEL_DEV_CORESIGHT_INCLUDE_DEV_CORESIGHT_ROM_TABLE_H_
#include <lib/fitx/result.h>
#include <zircon/assert.h>
#include <string_view>
#include <utility>
#include <dev/coresight/component.h>
#include <hwreg/bitfields.h>
namespace coresight {
// [CS] D5
// A ROM table is a basic component (of type
// ComponentIDRegister::Class::k0x1ROMTable or
// ComponentIDRegister::Class::kCoreSight) that provides pointers to other
// components (including other ROM tables) in its lower registers. It is an
// organizational structure that can be used to find all CoreSight components
// on a chip (or system of chips). Thought of as a tree, the leaves are the
// system's CoreSight components and the root is typically referred to as the
// "base ROM table" (or, more plainly, "the ROM table").
class ROMTable {
public:
// [CS] D6.4.4
struct Class0x1Entry : public hwreg::RegisterBase<Class0x1Entry, uint32_t> {
DEF_FIELD(31, 12, offset);
DEF_RSVDZ_FIELD(11, 9);
DEF_FIELD(8, 4, powerid);
DEF_RSVDZ_BIT(3);
DEF_BIT(2, powerid_valid);
DEF_BIT(1, format);
DEF_BIT(0, present);
static auto GetAt(uint32_t offset, uint32_t N) {
return hwreg::RegisterAddr<Class0x1Entry>(offset +
N * static_cast<uint32_t>(sizeof(uint32_t)));
}
};
// [CS] D7.5.17
struct Class0x9NarrowEntry : public hwreg::RegisterBase<Class0x9NarrowEntry, uint32_t> {
DEF_FIELD(31, 12, offset);
DEF_RSVDZ_FIELD(11, 9);
DEF_FIELD(8, 4, powerid);
DEF_RSVDZ_BIT(3);
DEF_BIT(2, powerid_valid);
DEF_FIELD(1, 0, present);
static auto GetAt(uint32_t offset, uint32_t N) {
return hwreg::RegisterAddr<Class0x9NarrowEntry>(offset +
N * static_cast<uint32_t>(sizeof(uint32_t)));
}
};
// [CS] D7.5.17
struct Class0x9WideEntry : public hwreg::RegisterBase<Class0x9WideEntry, uint64_t> {
DEF_FIELD(63, 12, offset);
DEF_RSVDZ_FIELD(11, 9);
DEF_FIELD(8, 4, powerid);
DEF_RSVDZ_BIT(3);
DEF_BIT(2, powerid_valid);
DEF_FIELD(1, 0, present);
static auto GetAt(uint32_t offset, uint32_t N) {
return hwreg::RegisterAddr<Class0x9WideEntry>(offset +
N * static_cast<uint32_t>(sizeof(uint64_t)));
}
};
// [CS] D7.5.10
struct Class0x9DeviceIDRegister : public hwreg::RegisterBase<Class0x9DeviceIDRegister, uint32_t> {
DEF_RSVDZ_FIELD(31, 6);
DEF_BIT(5, prr);
DEF_BIT(4, sysmem);
DEF_FIELD(3, 0, format);
static auto GetAt(uint32_t offset) {
return hwreg::RegisterAddr<Class0x9DeviceIDRegister>(offset + 0xfcc);
}
};
// Conceptually, we construct a ROM table from a span of bytes, the span
// being required to contain all of the components that the table
// transitively refers to.
ROMTable(uintptr_t base, uint32_t span_size) : base_(base), span_size_(span_size) {}
// The underlying tree of tables is walked with no dynamic allocation,
// calling a ComponentCallback on each CoreSight component found, the
// callback having a signature of uintptr_t -> void.
//
// The callback is also called on any global timestamp generator components
// found; these morally should be CoreSight components and ARM includes them
// as top-level table entries. A timestamp generator can be characterized as
// having a class of |ComponentIDRegister::Class::kNonStandard| and a part ID
// of |arm::partid::kTimestampGenerator|.
template <typename IoProvider, typename ComponentCallback>
fitx::result<std::string_view> Walk(IoProvider io, ComponentCallback&& callback) {
return WalkFrom(io, callback, 0);
}
private:
// [CS] D6.2.1, D7.2.1
// The largest possible ROM table entry index, for various types.
static constexpr uint32_t k0x1EntryUpperBound = 960u;
static constexpr uint32_t k0x9NarrowEntryUpperBound = 512u;
static constexpr uint32_t k0x9WideEntryUpperBound = 256u;
// [CS] D7.5.10
static constexpr uint8_t kDevidFormatNarrow = 0x0;
static constexpr uint8_t kDevidFormatWide = 0x1;
// There are several types of ROM table entry registers; this struct serves
// as unified front-end for accessing their contents.
struct EntryContents {
uint64_t value;
uint32_t offset;
bool present;
};
template <typename IoProvider, typename ComponentCallback>
fitx::result<std::string_view> WalkFrom(IoProvider io, ComponentCallback&& callback,
uint32_t offset) {
const ComponentIDRegister::Class classid =
ComponentIDRegister::GetAt(offset).ReadFrom(&io).classid();
const DeviceArchRegister arch_reg = DeviceArchRegister::GetAt(offset).ReadFrom(&io);
const auto architect = static_cast<uint16_t>(arch_reg.architect());
const auto archid = static_cast<uint16_t>(arch_reg.archid());
if (IsTable(classid, architect, archid)) {
const auto format =
static_cast<uint8_t>(Class0x9DeviceIDRegister::GetAt(offset).ReadFrom(&io).format());
fitx::result<std::string_view, uint32_t> upper_bound = EntryIndexUpperBound(classid, format);
if (upper_bound.is_error()) {
return fitx::error(upper_bound.error_value());
}
for (uint32_t i = 0; i < upper_bound.value(); ++i) {
auto result = ReadEntryAt(io, offset, i, classid, format);
if (result.is_error()) {
return fitx::error(result.error_value());
}
EntryContents contents = result.value();
if (contents.value == 0) {
break; // Terminal entry if identically zero.
} else if (!contents.present) {
continue;
}
// [CS] D5.4
// the offset provided by the ROM table entry requires a shift of 12 bits.
uint32_t new_offset = offset + (contents.offset << 12);
if (new_offset + kMinimumComponentSize > span_size_) {
printf("does not fit: (view size, offset) = (%u, %u)\n", span_size_, new_offset);
return fitx::error("does not fit");
}
if (auto result = WalkFrom(io, callback, new_offset); result.is_error()) {
return result;
}
}
return fitx::ok();
}
const uint16_t partid = GetPartIDAt(io, offset);
if (IsTerminalEntry(offset, classid, partid)) {
std::forward<ComponentCallback>(callback)(base_ + offset);
return fitx::ok();
}
printf(
"expected ROM table or component at offset %u: "
"(class, architect, archid) = (%#x (%s), %#x, %#x)",
offset, static_cast<uint8_t>(classid), ToString(classid).data(), architect, archid);
return fitx::error("unexpected component found");
}
bool IsTable(ComponentIDRegister::Class classid, uint16_t architect, uint16_t archid) const;
bool IsTerminalEntry(uint32_t offset, ComponentIDRegister::Class classid, uint16_t partid);
fitx::result<std::string_view, uint32_t> EntryIndexUpperBound(ComponentIDRegister::Class classid,
uint8_t format) const;
template <typename IoProvider>
fitx::result<std::string_view, EntryContents> ReadEntryAt(IoProvider io, uint32_t offset,
uint32_t N,
ComponentIDRegister::Class classid,
uint8_t format) {
if (classid == ComponentIDRegister::Class::k0x1ROMTable) {
auto entry = Class0x1Entry::GetAt(offset, N).ReadFrom(&io);
return fitx::ok(EntryContents{
.value = entry.reg_value(),
.offset = static_cast<uint32_t>(entry.offset()),
.present = static_cast<bool>(entry.present()),
});
} else if (classid == ComponentIDRegister::Class::kCoreSight) {
// [CS] D7.5.17: only a value of 0b11 for present() signifies presence.
switch (format) {
case kDevidFormatNarrow: {
auto entry = Class0x9NarrowEntry::GetAt(offset, N).ReadFrom(&io);
return fitx::ok(EntryContents{
.value = entry.reg_value(),
.offset = static_cast<uint32_t>(entry.offset()),
.present = static_cast<bool>(entry.present() & 0b11),
});
}
case kDevidFormatWide: {
auto entry = Class0x9WideEntry::GetAt(offset, N).ReadFrom(&io);
uint64_t u32_offset = entry.offset() & 0xffffffff;
if (entry.offset() != u32_offset) {
return fitx::error(
"a simplifying assumption is made that a ROM table entry's offset only contains 32 "
"bits of information. If this is no longer true, please file a bug.");
}
return fitx::ok(EntryContents{
.value = entry.reg_value(),
.offset = static_cast<uint32_t>(u32_offset),
.present = static_cast<bool>(entry.present() & 0b11),
});
}
default:
printf("bad format value: %#x", format);
return fitx::error("bad format value");
}
}
return fitx::error("not a ROM table");
}
uintptr_t base_;
uint32_t span_size_;
};
} // namespace coresight
#endif // ZIRCON_KERNEL_DEV_CORESIGHT_INCLUDE_DEV_CORESIGHT_ROM_TABLE_H_