mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-22 09:21:57 -05:00
LibDeviceTree: Add functions for getting and translating addresses
I created this test file by running the following command: dtc -o address-translation.dtb <<EOF /dts-v1/; / { compatible = "serenity,address-translation-test"; #address-cells = <2>; #size-cells = <1>; soc { compatible = "simple-bus"; #address-cells = <1>; #size-cells = <1>; ranges = <0xa0000000 0xfe 0xd0000000 0x40000000>; usb@a0010000 { reg = <0xa0010000 0x100000>; }; some-bus@b0000000 { compatible = "simple-bus"; #address-cells = <2>; #size-cells = <2>; ranges = <0x02 0x00 0xb0000000 0x00 0x200000>; leds@200100000 { reg = <0x02 0x100000 0x00 0x1000>; }; }; }; }; EOF
This commit is contained in:
parent
7d41d5191b
commit
ddd9ab21e6
8 changed files with 407 additions and 11 deletions
|
@ -82,6 +82,10 @@
|
||||||
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
# cmakedefine01 CSS_TRANSITIONS_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEVICETREE_DEBUG
|
||||||
|
# cmakedefine01 DEVICETREE_DEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DDS_DEBUG
|
#ifndef DDS_DEBUG
|
||||||
# cmakedefine01 DDS_DEBUG
|
# cmakedefine01 DDS_DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,6 +28,7 @@ set(CSS_PARSER_DEBUG ON)
|
||||||
set(CSS_TOKENIZER_DEBUG ON)
|
set(CSS_TOKENIZER_DEBUG ON)
|
||||||
set(CSS_TRANSITIONS_DEBUG ON)
|
set(CSS_TRANSITIONS_DEBUG ON)
|
||||||
set(DDS_DEBUG ON)
|
set(DDS_DEBUG ON)
|
||||||
|
set(DEVICETREE_DEBUG ON)
|
||||||
set(DHCPV4CLIENT_DEBUG ON)
|
set(DHCPV4CLIENT_DEBUG ON)
|
||||||
set(DHCPV4_DEBUG ON)
|
set(DHCPV4_DEBUG ON)
|
||||||
set(DIFF_DEBUG ON)
|
set(DIFF_DEBUG ON)
|
||||||
|
|
|
@ -250,6 +250,7 @@ write_cmake_config("ak_debug_gen") {
|
||||||
"CSS_TOKENIZER_DEBUG=",
|
"CSS_TOKENIZER_DEBUG=",
|
||||||
"CSS_TRANSITIONS_DEBUG=",
|
"CSS_TRANSITIONS_DEBUG=",
|
||||||
"DDS_DEBUG=",
|
"DDS_DEBUG=",
|
||||||
|
"DEVICETREE_DEBUG=",
|
||||||
"DHCPV4CLIENT_DEBUG=",
|
"DHCPV4CLIENT_DEBUG=",
|
||||||
"DHCPV4_DEBUG=",
|
"DHCPV4_DEBUG=",
|
||||||
"DIFF_DEBUG=",
|
"DIFF_DEBUG=",
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
set(TEST_SOURCES
|
set(TEST_SOURCES
|
||||||
TestLookup.cpp
|
TestLookup.cpp
|
||||||
|
TestAddressTranslation.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(source IN LISTS TEST_SOURCES)
|
foreach(source IN LISTS TEST_SOURCES)
|
||||||
|
@ -7,3 +8,4 @@ foreach(source IN LISTS TEST_SOURCES)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
install(FILES dtb.dtb DESTINATION usr/Tests/LibDeviceTree)
|
install(FILES dtb.dtb DESTINATION usr/Tests/LibDeviceTree)
|
||||||
|
install(FILES address-translation.dtb DESTINATION usr/Tests/LibDeviceTree)
|
||||||
|
|
47
Tests/LibDeviceTree/TestAddressTranslation.cpp
Normal file
47
Tests/LibDeviceTree/TestAddressTranslation.cpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024, Sönke Holz <sholz8530@gmail.com>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <LibTest/TestCase.h>
|
||||||
|
|
||||||
|
#include <LibCore/File.h>
|
||||||
|
#include <LibCore/System.h>
|
||||||
|
#include <LibDeviceTree/DeviceTree.h>
|
||||||
|
#include <LibDeviceTree/FlattenedDeviceTree.h>
|
||||||
|
|
||||||
|
TEST_CASE(address_translation)
|
||||||
|
{
|
||||||
|
auto fdt_file = TRY_OR_FAIL(Core::File::open("/usr/Tests/LibDeviceTree/address-translation.dtb"sv, Core::File::OpenMode::Read));
|
||||||
|
auto fdt = TRY_OR_FAIL(fdt_file->read_until_eof());
|
||||||
|
|
||||||
|
auto device_tree = TRY_OR_FAIL(DeviceTree::DeviceTree::parse(fdt));
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const* usb = device_tree->resolve_node("/soc/usb@a0010000"sv);
|
||||||
|
EXPECT(usb != nullptr);
|
||||||
|
|
||||||
|
auto usb_reg_entry_0 = TRY_OR_FAIL(TRY_OR_FAIL(usb->reg()).entry(0));
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(usb_reg_entry_0.bus_address().as_flatptr()), 0xa001'0000uz);
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(usb_reg_entry_0.length().as_size_t()), 0x10'0000uz);
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(TRY_OR_FAIL(usb_reg_entry_0.resolve_root_address()).as_flatptr()), 0xfe'd001'0000uz);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto const* leds = device_tree->resolve_node("/soc/some-bus@b0000000/leds@200100000"sv);
|
||||||
|
EXPECT(leds != nullptr);
|
||||||
|
|
||||||
|
auto const* leds_parent = leds->parent();
|
||||||
|
EXPECT(leds_parent != nullptr);
|
||||||
|
|
||||||
|
auto leds_parent_ranges = TRY_OR_FAIL(leds_parent->ranges());
|
||||||
|
|
||||||
|
auto leds_reg_entry_0 = TRY_OR_FAIL(TRY_OR_FAIL(leds->reg()).entry(0));
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(leds_reg_entry_0.bus_address().as_flatptr()), 0x2'0010'0000uz);
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(leds_reg_entry_0.length().as_size_t()), 0x1000uz);
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(TRY_OR_FAIL(leds_parent_ranges.translate_child_bus_address_to_parent_bus_address(leds_reg_entry_0.bus_address())).as_flatptr()), 0xb010'0000uz);
|
||||||
|
|
||||||
|
EXPECT_EQ(TRY_OR_FAIL(TRY_OR_FAIL(leds_reg_entry_0.resolve_root_address()).as_flatptr()), 0xfe'e010'0000uz);
|
||||||
|
}
|
||||||
|
}
|
BIN
Tests/LibDeviceTree/address-translation.dtb
Normal file
BIN
Tests/LibDeviceTree/address-translation.dtb
Normal file
Binary file not shown.
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "DeviceTree.h"
|
#include "DeviceTree.h"
|
||||||
#include "FlattenedDeviceTree.h"
|
#include "FlattenedDeviceTree.h"
|
||||||
|
#include <AK/Debug.h>
|
||||||
#include <AK/NonnullOwnPtr.h>
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/Types.h>
|
#include <AK/Types.h>
|
||||||
|
|
||||||
|
@ -93,4 +94,173 @@ bool Node::is_compatible_with(StringView wanted_compatible_string) const
|
||||||
return is_compatible;
|
return is_compatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<Reg> Node::reg() const
|
||||||
|
{
|
||||||
|
if (parent() == nullptr)
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
auto reg_prop = get_property("reg"sv);
|
||||||
|
if (!reg_prop.has_value())
|
||||||
|
return Error::from_errno(ENOENT);
|
||||||
|
|
||||||
|
return Reg { reg_prop->raw_data, *this };
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Ranges> Node::ranges() const
|
||||||
|
{
|
||||||
|
if (parent() == nullptr)
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
auto ranges_prop = get_property("ranges"sv);
|
||||||
|
if (!ranges_prop.has_value())
|
||||||
|
return Error::from_errno(ENOENT);
|
||||||
|
|
||||||
|
return Ranges { ranges_prop->raw_data, *this };
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Address> RegEntry::resolve_root_address() const
|
||||||
|
{
|
||||||
|
VERIFY(m_node.parent() != nullptr);
|
||||||
|
return m_node.parent()->translate_child_bus_address_to_root_address(bus_address());
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<RegEntry> Reg::entry(size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= entry_count())
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
VERIFY(m_node.parent() != nullptr);
|
||||||
|
|
||||||
|
auto parent_address_cells = m_node.parent()->address_cells();
|
||||||
|
auto parent_size_cells = m_node.parent()->size_cells();
|
||||||
|
|
||||||
|
size_t const start_index = index * (parent_address_cells + parent_size_cells) * sizeof(u32);
|
||||||
|
|
||||||
|
auto address = Address { m_raw.slice(start_index, parent_address_cells * sizeof(u32)) };
|
||||||
|
auto length = Size { m_raw.slice(start_index + (parent_address_cells * sizeof(u32)), parent_size_cells * sizeof(u32)) };
|
||||||
|
|
||||||
|
return RegEntry {
|
||||||
|
move(address),
|
||||||
|
move(length),
|
||||||
|
m_node,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Reg::entry_count() const
|
||||||
|
{
|
||||||
|
VERIFY(m_node.parent() != nullptr);
|
||||||
|
|
||||||
|
auto parent_address_cells = m_node.parent()->address_cells();
|
||||||
|
auto parent_size_cells = m_node.parent()->size_cells();
|
||||||
|
|
||||||
|
// #address-cells should never be 0, but still avoid dividing by 0.
|
||||||
|
if (parent_address_cells + parent_size_cells == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_raw.size() / ((parent_address_cells + parent_size_cells) * sizeof(u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Address> RangesEntry::translate_child_bus_address_to_parent_bus_address(Address const& address) const
|
||||||
|
{
|
||||||
|
auto maybe_device_type = m_node.get_property("device_type"sv);
|
||||||
|
|
||||||
|
if (maybe_device_type.has_value() && maybe_device_type->as_string() == "pci"sv) {
|
||||||
|
// TODO
|
||||||
|
return Error::from_errno(ENOTSUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto address_as_flatptr = TRY(address.as_flatptr());
|
||||||
|
auto child_bus_address_as_flatptr = TRY(m_child_bus_address.as_flatptr());
|
||||||
|
auto parent_bus_address_as_flatptr = TRY(m_parent_bus_address.as_flatptr());
|
||||||
|
auto length_as_size_t = TRY(m_length.as_size_t());
|
||||||
|
|
||||||
|
if (address_as_flatptr >= child_bus_address_as_flatptr && address_as_flatptr < (child_bus_address_as_flatptr + length_as_size_t))
|
||||||
|
return Address::from_flatptr(address_as_flatptr - child_bus_address_as_flatptr + parent_bus_address_as_flatptr);
|
||||||
|
|
||||||
|
return Error::from_errno(EFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<RangesEntry> Ranges::entry(size_t index) const
|
||||||
|
{
|
||||||
|
if (index >= entry_count())
|
||||||
|
return Error::from_errno(EINVAL);
|
||||||
|
|
||||||
|
VERIFY(m_node.parent() != nullptr);
|
||||||
|
|
||||||
|
auto address_cells = m_node.address_cells();
|
||||||
|
auto parent_address_cells = m_node.parent()->address_cells();
|
||||||
|
auto size_cells = m_node.size_cells();
|
||||||
|
|
||||||
|
size_t const start_index = index * (address_cells + parent_address_cells + size_cells) * sizeof(u32);
|
||||||
|
|
||||||
|
auto child_bus_address = Address { m_raw.slice(start_index, address_cells * sizeof(u32)) };
|
||||||
|
auto parent_bus_addres = Address { m_raw.slice(start_index + (address_cells * sizeof(u32)), parent_address_cells * sizeof(u32)) };
|
||||||
|
auto size = Size { m_raw.slice(start_index + ((address_cells + parent_address_cells) * sizeof(u32)), size_cells * sizeof(u32)) };
|
||||||
|
|
||||||
|
return RangesEntry {
|
||||||
|
move(child_bus_address),
|
||||||
|
move(parent_bus_addres),
|
||||||
|
move(size),
|
||||||
|
m_node,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Ranges::entry_count() const
|
||||||
|
{
|
||||||
|
VERIFY(m_node.parent() != nullptr);
|
||||||
|
|
||||||
|
auto address_cells = m_node.address_cells();
|
||||||
|
auto parent_address_cells = m_node.parent()->address_cells();
|
||||||
|
auto size_cells = m_node.size_cells();
|
||||||
|
|
||||||
|
// #address-cells should never be 0, but still avoid dividing by 0.
|
||||||
|
if (address_cells + parent_address_cells + size_cells == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return m_raw.size() / ((address_cells + parent_address_cells + size_cells) * sizeof(u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Address> Ranges::translate_child_bus_address_to_parent_bus_address(Address const& addr) const
|
||||||
|
{
|
||||||
|
// 2.3.8 ranges
|
||||||
|
// If the property is defined with an <empty> value, it specifies that the parent and child address space is identical,
|
||||||
|
// and no address translation is required.
|
||||||
|
if (entry_count() == 0)
|
||||||
|
return addr;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < entry_count(); i++) {
|
||||||
|
if (auto translation_or_error = MUST(entry(i)).translate_child_bus_address_to_parent_bus_address(addr); !translation_or_error.is_error())
|
||||||
|
return translation_or_error.release_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Error::from_errno(EFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Address> Node::translate_child_bus_address_to_root_address(Address const& addr) const
|
||||||
|
{
|
||||||
|
dbgln_if(DEVICETREE_DEBUG, "DeviceTree: Translating bus address {:hex-dump}", addr.raw());
|
||||||
|
|
||||||
|
auto const* current_node = this;
|
||||||
|
auto current_address = addr;
|
||||||
|
|
||||||
|
while (!current_node->is_root()) {
|
||||||
|
// 2.3.8 ranges
|
||||||
|
// If the property is not present in a bus node, it is assumed that no mapping exists between children of the node
|
||||||
|
// and the parent address space.
|
||||||
|
auto ranges_or_error = current_node->ranges();
|
||||||
|
if (ranges_or_error.is_error()) {
|
||||||
|
VERIFY(ranges_or_error.release_error().code() == ENOENT);
|
||||||
|
return Error::from_errno(EFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_address = TRY(ranges_or_error.release_value().translate_child_bus_address_to_parent_bus_address(current_address));
|
||||||
|
|
||||||
|
current_node = current_node->parent();
|
||||||
|
|
||||||
|
dbgln_if(DEVICETREE_DEBUG, "DeviceTree: -> {} address: {:hex-dump}", current_node->is_root() ? "root" : "parent bus", current_address.raw());
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_address;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include <AK/Concepts.h>
|
#include <AK/Concepts.h>
|
||||||
#include <AK/Endian.h>
|
#include <AK/Endian.h>
|
||||||
|
#include <AK/FixedArray.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/IterationDecision.h>
|
#include <AK/IterationDecision.h>
|
||||||
|
@ -18,6 +19,67 @@
|
||||||
|
|
||||||
namespace DeviceTree {
|
namespace DeviceTree {
|
||||||
|
|
||||||
|
// Devicetree Specification 0.4 (DTSpec): https://github.com/devicetree-org/devicetree-specification/releases/download/v0.4/devicetree-specification-v0.4.pdf
|
||||||
|
|
||||||
|
class DeviceTree;
|
||||||
|
class Node;
|
||||||
|
|
||||||
|
class Address {
|
||||||
|
public:
|
||||||
|
Address() = default;
|
||||||
|
Address(ReadonlyBytes data)
|
||||||
|
: m_raw(static_cast<decltype(m_raw)>(data))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadonlyBytes raw() const { return m_raw; }
|
||||||
|
|
||||||
|
static Address from_flatptr(FlatPtr flatptr)
|
||||||
|
{
|
||||||
|
BigEndian<FlatPtr> big_endian_flatptr { flatptr };
|
||||||
|
|
||||||
|
Address address;
|
||||||
|
address.m_raw.resize(sizeof(FlatPtr));
|
||||||
|
__builtin_memcpy(address.m_raw.data(), &big_endian_flatptr, sizeof(big_endian_flatptr));
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<FlatPtr> as_flatptr() const
|
||||||
|
{
|
||||||
|
if (m_raw.size() == sizeof(u32))
|
||||||
|
return *reinterpret_cast<BigEndian<u32> const*>(m_raw.data());
|
||||||
|
if (m_raw.size() == 2 * sizeof(u32))
|
||||||
|
return *reinterpret_cast<BigEndian<u64> const*>(m_raw.data());
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<u8, 4 * sizeof(u32)> m_raw;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Size {
|
||||||
|
public:
|
||||||
|
Size() = default;
|
||||||
|
Size(ReadonlyBytes data)
|
||||||
|
: m_raw(static_cast<decltype(m_raw)>(data))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadonlyBytes raw() const { return m_raw; }
|
||||||
|
|
||||||
|
ErrorOr<size_t> as_size_t() const
|
||||||
|
{
|
||||||
|
if (m_raw.size() == sizeof(u32))
|
||||||
|
return *reinterpret_cast<BigEndian<u32> const*>(m_raw.data());
|
||||||
|
if (m_raw.size() == 2 * sizeof(u32))
|
||||||
|
return *reinterpret_cast<BigEndian<u64> const*>(m_raw.data());
|
||||||
|
return ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<u8, 2 * sizeof(u32)> m_raw;
|
||||||
|
};
|
||||||
|
|
||||||
struct Property {
|
struct Property {
|
||||||
class ValueStream : public FixedMemoryStream {
|
class ValueStream : public FixedMemoryStream {
|
||||||
public:
|
public:
|
||||||
|
@ -95,6 +157,100 @@ struct Property {
|
||||||
ValueStream as_stream() const { return ValueStream { raw_data }; }
|
ValueStream as_stream() const { return ValueStream { raw_data }; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 2.3.6 reg
|
||||||
|
class RegEntry {
|
||||||
|
public:
|
||||||
|
RegEntry(Address const& address, Size const& size, Node const& node)
|
||||||
|
: m_address(address)
|
||||||
|
, m_length(size)
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RegEntry(Address&& address, Size&& size, Node const& node)
|
||||||
|
: m_address(move(address))
|
||||||
|
, m_length(move(size))
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Address bus_address() const { return m_address; }
|
||||||
|
Size length() const { return m_length; }
|
||||||
|
|
||||||
|
ErrorOr<Address> resolve_root_address() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Address m_address;
|
||||||
|
Size m_length;
|
||||||
|
Node const& m_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Reg {
|
||||||
|
public:
|
||||||
|
Reg(ReadonlyBytes data, Node const& node)
|
||||||
|
: m_raw(data)
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<RegEntry> entry(size_t index) const;
|
||||||
|
size_t entry_count() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ReadonlyBytes m_raw;
|
||||||
|
Node const& m_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2.3.8 ranges
|
||||||
|
class RangesEntry {
|
||||||
|
public:
|
||||||
|
RangesEntry(Address const& child_bus_address, Address const& parent_bus_address, Size const& length, Node const& node)
|
||||||
|
: m_child_bus_address(child_bus_address)
|
||||||
|
, m_parent_bus_address(parent_bus_address)
|
||||||
|
, m_length(length)
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
RangesEntry(Address&& child_bus_address, Address&& parent_bus_address, Size&& length, Node const& node)
|
||||||
|
: m_child_bus_address(move(child_bus_address))
|
||||||
|
, m_parent_bus_address(move(parent_bus_address))
|
||||||
|
, m_length(move(length))
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Address child_bus_address() const { return m_child_bus_address; }
|
||||||
|
Address parent_bus_address() const { return m_parent_bus_address; }
|
||||||
|
Size length() const { return m_length; }
|
||||||
|
|
||||||
|
ErrorOr<Address> translate_child_bus_address_to_parent_bus_address(Address const&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Address m_child_bus_address;
|
||||||
|
Address m_parent_bus_address;
|
||||||
|
Size m_length;
|
||||||
|
Node const& m_node;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Ranges {
|
||||||
|
public:
|
||||||
|
Ranges(ReadonlyBytes data, Node const& node)
|
||||||
|
: m_raw(data)
|
||||||
|
, m_node(node)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<RangesEntry> entry(size_t index) const;
|
||||||
|
size_t entry_count() const;
|
||||||
|
|
||||||
|
ErrorOr<Address> translate_child_bus_address_to_parent_bus_address(Address const&) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ReadonlyBytes m_raw;
|
||||||
|
Node const& m_node;
|
||||||
|
};
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
AK_MAKE_NONCOPYABLE(Node);
|
AK_MAKE_NONCOPYABLE(Node);
|
||||||
AK_MAKE_DEFAULT_MOVABLE(Node);
|
AK_MAKE_DEFAULT_MOVABLE(Node);
|
||||||
|
@ -112,18 +268,38 @@ public:
|
||||||
HashMap<StringView, Node> const& children() const { return m_children; }
|
HashMap<StringView, Node> const& children() const { return m_children; }
|
||||||
HashMap<StringView, Property> const& properties() const { return m_properties; }
|
HashMap<StringView, Property> const& properties() const { return m_properties; }
|
||||||
|
|
||||||
|
bool is_root() const { return m_parent == nullptr; }
|
||||||
|
|
||||||
Node const* parent() const { return m_parent; }
|
Node const* parent() const { return m_parent; }
|
||||||
|
|
||||||
// NOTE: When checking for multiple drivers, prefer iterating over the string array instead,
|
// NOTE: When checking for multiple drivers, prefer iterating over the string array instead,
|
||||||
// as the compatible strings are sorted by preference, which this function cannot account for.
|
// as the compatible strings are sorted by preference, which this function cannot account for.
|
||||||
bool is_compatible_with(StringView) const;
|
bool is_compatible_with(StringView) const;
|
||||||
|
|
||||||
// FIXME: Add convenience functions for common properties like "reg" and "compatible"
|
// 2.3.5 #address-cells and #size-cells
|
||||||
// Note: The "reg" property is a list of address and size pairs, but the address is not always a u32 or u64
|
u32 address_cells() const
|
||||||
// In pci devices the #address-size is 3 cells: (phys.lo phys.mid phys.hi)
|
{
|
||||||
// with the following format:
|
if (auto prop = get_property("#address-cells"sv); prop.has_value())
|
||||||
// phys.lo, phys.mid: 64-bit Address - BigEndian
|
return prop.release_value().as<u32>();
|
||||||
// phys.hi: relocatable(1), prefetchable(1), aliased(1), 000(3), space type(2), bus number(8), device number(5), function number(3), register number(8) - BigEndian
|
|
||||||
|
// If missing, a client program should assume a default value of 2 for #address-cells, and a value of 1 for #size-cells.
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.3.5 #address-cells and #size-cells
|
||||||
|
u32 size_cells() const
|
||||||
|
{
|
||||||
|
if (auto prop = get_property("#size-cells"sv); prop.has_value())
|
||||||
|
return prop.release_value().as<u32>();
|
||||||
|
|
||||||
|
// If missing, a client program should assume a default value of 2 for #address-cells, and a value of 1 for #size-cells.
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<Reg> reg() const;
|
||||||
|
ErrorOr<Ranges> ranges() const;
|
||||||
|
|
||||||
|
ErrorOr<Address> translate_child_bus_address_to_root_address(Address const&) const;
|
||||||
|
|
||||||
// FIXME: Stringify?
|
// FIXME: Stringify?
|
||||||
// FIXME: Flatten?
|
// FIXME: Flatten?
|
||||||
|
@ -189,11 +365,6 @@ public:
|
||||||
return node->get_property(property_name);
|
return node->get_property(property_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Add a helper to iterate over each descendant fulfilling some properties
|
|
||||||
// Like each node with a "compatible" property containing "pci" or "usb",
|
|
||||||
// bonus points if it could automatically recurse in the tree under some conditions,
|
|
||||||
// like "simple-bus" or "pci-bridge" nodes
|
|
||||||
|
|
||||||
auto for_each_node(CallableAs<ErrorOr<RecursionDecision>, StringView, Node const&> auto callback) const
|
auto for_each_node(CallableAs<ErrorOr<RecursionDecision>, StringView, Node const&> auto callback) const
|
||||||
{
|
{
|
||||||
auto iterate = [&](auto self, StringView name, Node const& node) -> ErrorOr<RecursionDecision> {
|
auto iterate = [&](auto self, StringView name, Node const& node) -> ErrorOr<RecursionDecision> {
|
||||||
|
|
Loading…
Reference in a new issue