Kernel: Rewrite whole AML parser

Now AML parsing is actually done while respecting namespaces and
scopes. I implemented the minimal functionality to parse qemu's AML.

Next step is to implement AML interpreting and then we can drop lai
as a dependency.
This commit is contained in:
2024-04-09 01:16:07 +03:00
parent 090a294017
commit b16e65168f
38 changed files with 1891 additions and 2412 deletions

View File

@@ -0,0 +1,157 @@
#pragma once
#include <BAN/Optional.h>
#include <BAN/String.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI::AML
{
struct NameSeg
{
union {
char chars[4];
uint32_t u32;
};
NameSeg() = default;
NameSeg(BAN::StringView name)
{
ASSERT(name.size() <= 4);
for (size_t i = 0; i < name.size(); i++)
chars[i] = static_cast<char>(name[i]);
for (size_t i = name.size(); i < 4; i++)
chars[i] = '_';
}
NameSeg(BAN::ConstByteSpan& aml_data)
{
ASSERT(aml_data.size() >= 4);
for (size_t i = 0; i < 4; i++)
chars[i] = static_cast<char>(aml_data[i]);
aml_data = aml_data.slice(4);
}
static BAN::Optional<NameSeg> parse(BAN::ConstByteSpan& aml_data)
{
if (aml_data.size() < 4)
return {};
if (!is_lead_name_char(aml_data[0])
|| !is_name_char(aml_data[1])
|| !is_name_char(aml_data[2])
|| !is_name_char(aml_data[3]))
return {};
return NameSeg(aml_data);
}
constexpr bool operator==(const NameSeg& other) const
{
return u32 == other.u32;
}
void debug_print() const
{
size_t len = 4;
while (len > 0 && chars[len - 1] == '_')
len--;
for (size_t i = 0; i < len; i++)
AML_DEBUG_PUTC(chars[i]);
}
};
struct NameString
{
BAN::String prefix;
BAN::Vector<NameSeg> path;
static BAN::Optional<NameString> parse(BAN::ConstByteSpan& aml_data)
{
if (aml_data.size() == 0)
return {};
NameString result;
if (static_cast<AML::Byte>(aml_data[0]) == AML::Byte::RootChar)
{
MUST(result.prefix.push_back('\\'));
aml_data = aml_data.slice(1);
}
else
{
while (aml_data.size() > 0 && static_cast<AML::Byte>(aml_data[0]) == AML::Byte::ParentPrefixChar)
{
MUST(result.prefix.push_back(aml_data[0]));
aml_data = aml_data.slice(1);
}
}
if (aml_data.size() == 0)
return {};
size_t name_count = 1;
switch (static_cast<AML::Byte>(aml_data[0]))
{
case AML::Byte::NullName:
name_count = 0;
aml_data = aml_data.slice(1);
break;
case AML::Byte::DualNamePrefix:
name_count = 2;
aml_data = aml_data.slice(1);
break;
case AML::Byte::MultiNamePrefix:
if (aml_data.size() < 2)
return {};
name_count = aml_data[1];
aml_data = aml_data.slice(2);
break;
default:
break;
}
for (size_t i = 0; i < name_count; i++)
{
auto name_seg = NameSeg::parse(aml_data);
if (!name_seg.has_value())
return {};
MUST(result.path.push_back(name_seg.release_value()));
}
return result;
}
void debug_print() const
{
for (size_t i = 0; i < prefix.size(); i++)
AML_DEBUG_PUTC(prefix[i]);
if (!path.empty())
path.front().debug_print();
for (size_t i = 1; i < path.size(); i++)
{
AML_DEBUG_PUTC('.');
path[i].debug_print();
}
}
};
}
namespace BAN
{
template<>
struct hash<Kernel::ACPI::AML::NameSeg>
{
constexpr hash_t operator()(Kernel::ACPI::AML::NameSeg name) const
{
return hash<uint32_t>()(name.u32);
}
};
}