Compare commits
12 Commits
869f4011a1
...
3a6cdfff45
Author | SHA1 | Date |
---|---|---|
|
3a6cdfff45 | |
|
c26e347e91 | |
|
1b94957b07 | |
|
2e39668605 | |
|
0642c569b4 | |
|
4e364bd2f6 | |
|
b9fe564d78 | |
|
437fa45ca2 | |
|
85e215650c | |
|
7eb186dad4 | |
|
2259614640 | |
|
a584e1a4ec |
|
@ -1,10 +1,10 @@
|
|||
set(KERNEL_SOURCES
|
||||
font/prefs.psf.o
|
||||
kernel/ACPI/ACPI.cpp
|
||||
kernel/ACPI/BatterySystem.cpp
|
||||
kernel/ACPI/AML/Namespace.cpp
|
||||
kernel/ACPI/AML/Node.cpp
|
||||
kernel/ACPI/AML/OpRegion.cpp
|
||||
kernel/ACPI/AML/Scope.cpp
|
||||
kernel/APIC.cpp
|
||||
kernel/BootInfo.cpp
|
||||
kernel/CPUID.cpp
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace Kernel::ACPI
|
|||
// 2: SAPIC
|
||||
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
|
||||
|
||||
BAN::ErrorOr<void> initialize_acpi_devices();
|
||||
|
||||
BAN::ErrorOr<void> poweroff();
|
||||
BAN::ErrorOr<void> reset();
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <BAN/ByteSpan.h>
|
||||
#include <BAN/Function.h>
|
||||
#include <BAN/HashMap.h>
|
||||
#include <BAN/HashSet.h>
|
||||
#include <BAN/Iteration.h>
|
||||
|
||||
#include <kernel/ACPI/AML/Node.h>
|
||||
|
@ -19,11 +20,10 @@ namespace Kernel::ACPI::AML
|
|||
Namespace() = default;
|
||||
~Namespace();
|
||||
|
||||
static BAN::ErrorOr<void> initialize_root_namespace();
|
||||
static BAN::ErrorOr<void> prepare_root_namespace();
|
||||
static Namespace& root_namespace();
|
||||
|
||||
// this has to be called after initalizing ACPI namespace
|
||||
BAN::ErrorOr<void> initalize_op_regions();
|
||||
BAN::ErrorOr<void> post_load_initialize();
|
||||
|
||||
BAN::ErrorOr<void> parse(BAN::ConstByteSpan);
|
||||
|
||||
|
@ -31,12 +31,10 @@ namespace Kernel::ACPI::AML
|
|||
|
||||
// returns empty scope if object already exited
|
||||
BAN::ErrorOr<Scope> add_named_object(const Scope& scope, const NameString& name_string, Node&& node);
|
||||
BAN::ErrorOr<Scope> add_named_object(const Scope& scope, const NameString& name_string, Reference* reference);
|
||||
BAN::ErrorOr<Scope> add_alias(const Scope& scope, const NameString& name_string, Reference* reference);
|
||||
|
||||
BAN::ErrorOr<void> remove_named_object(const Scope& absolute_path);
|
||||
|
||||
BAN::ErrorOr<void> initialize_devices();
|
||||
|
||||
// node is nullptr if it is not found
|
||||
struct FindResult
|
||||
{
|
||||
|
@ -48,6 +46,8 @@ namespace Kernel::ACPI::AML
|
|||
BAN::ErrorOr<void> for_each_child(const Scope&, const BAN::Function<BAN::Iteration(BAN::StringView, Reference*)>&);
|
||||
BAN::ErrorOr<void> for_each_child(const Scope&, const BAN::Function<BAN::Iteration(const Scope&, Reference*)>&);
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<Scope>> find_device_with_eisa_id(BAN::StringView eisa_id);
|
||||
|
||||
private:
|
||||
BAN::ErrorOr<Scope> resolve_path(const Scope& scope, const NameString& name_string);
|
||||
|
||||
|
@ -55,10 +55,16 @@ namespace Kernel::ACPI::AML
|
|||
|
||||
BAN::ErrorOr<void> opregion_call_reg(const Scope& scope, const Node& opregion);
|
||||
|
||||
BAN::ErrorOr<uint64_t> evaluate_sta(const Scope& scope);
|
||||
BAN::ErrorOr<void> evaluate_ini(const Scope& scope);
|
||||
|
||||
BAN::ErrorOr<void> initialize_op_regions();
|
||||
|
||||
private:
|
||||
bool m_has_parsed_namespace { false };
|
||||
bool m_has_initialized_namespace { false };
|
||||
BAN::HashMap<Scope, Reference*> m_named_objects;
|
||||
BAN::HashMap<Scope, uint32_t> m_called_reg_bitmaps;
|
||||
BAN::HashSet<Scope> m_aliases;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -24,10 +24,12 @@ namespace Kernel::ACPI::AML
|
|||
public:
|
||||
NameString() = default;
|
||||
NameString(NameString&& other)
|
||||
: parts(BAN::move(other.parts))
|
||||
: base(other.base)
|
||||
, parts(BAN::move(other.parts))
|
||||
{}
|
||||
NameString& operator=(NameString&& other)
|
||||
{
|
||||
base = other.base;
|
||||
parts = BAN::move(other.parts);
|
||||
return *this;
|
||||
}
|
||||
|
@ -72,6 +74,11 @@ namespace Kernel::ACPI::AML
|
|||
|
||||
struct Buffer
|
||||
{
|
||||
BAN::StringView as_sv() const
|
||||
{
|
||||
return BAN::StringView(reinterpret_cast<const char*>(bytes), size);
|
||||
}
|
||||
|
||||
uint64_t size;
|
||||
uint32_t ref_count;
|
||||
uint8_t bytes[];
|
||||
|
@ -335,12 +342,7 @@ namespace BAN::Formatter
|
|||
print(putc, "<integer 0x{H}>", node.as.integer.value);
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::String:
|
||||
print(putc, "<string '{}'>",
|
||||
BAN::StringView(
|
||||
reinterpret_cast<const char*>(node.as.str_buf->bytes),
|
||||
node.as.str_buf->size
|
||||
)
|
||||
);
|
||||
print(putc, "<string '{}'>", node.as.str_buf->as_sv());
|
||||
break;
|
||||
case Kernel::ACPI::AML::Node::Type::Package:
|
||||
print(putc, "{}", *node.as.package);
|
||||
|
|
|
@ -37,8 +37,6 @@ namespace Kernel::ACPI::AML
|
|||
}
|
||||
};
|
||||
|
||||
BAN::ErrorOr<void> initialize_scope(const Scope&);
|
||||
|
||||
}
|
||||
|
||||
namespace BAN
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/FS/TmpFS/Inode.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
class BatterySystem
|
||||
{
|
||||
BAN_NON_COPYABLE(BatterySystem);
|
||||
BAN_NON_MOVABLE(BatterySystem);
|
||||
public:
|
||||
static BAN::ErrorOr<void> initialize(AML::Namespace& acpi_namespace);
|
||||
|
||||
private:
|
||||
BatterySystem(AML::Namespace&);
|
||||
|
||||
BAN::ErrorOr<void> initialize_impl();
|
||||
|
||||
private:
|
||||
AML::Namespace& m_acpi_namespace;
|
||||
BAN::RefPtr<TmpDirectoryInode> m_directory;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#include <BAN/ScopeGuard.h>
|
||||
#include <BAN/StringView.h>
|
||||
#include <kernel/ACPI/ACPI.h>
|
||||
#include <kernel/ACPI/BatterySystem.h>
|
||||
#include <kernel/ACPI/AML/OpRegion.h>
|
||||
#include <kernel/BootInfo.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
|
@ -623,7 +624,7 @@ acpi_release_global_lock:
|
|||
{
|
||||
ASSERT(!m_namespace);
|
||||
|
||||
TRY(AML::Namespace::initialize_root_namespace());
|
||||
TRY(AML::Namespace::prepare_root_namespace());
|
||||
m_namespace = &AML::Namespace::root_namespace();
|
||||
|
||||
if (auto ret = load_aml_tables("DSDT"_sv, false); ret.is_error())
|
||||
|
@ -662,22 +663,9 @@ acpi_release_global_lock:
|
|||
|
||||
dprintln("Entered ACPI mode");
|
||||
|
||||
dprintln("Calling opregion _REG methods");
|
||||
if (auto ret = m_namespace->post_load_initialize(); ret.is_error())
|
||||
dwarnln("Failed to initialize ACPI namespace: {}", ret.error());
|
||||
|
||||
if (auto ret = m_namespace->initalize_op_regions(); ret.is_error())
|
||||
dwarnln("failed to call _REG methods: {}", ret.error());
|
||||
|
||||
dprintln("Initializing \\_SB");
|
||||
|
||||
// Initialize \\_SB
|
||||
auto [sb_path, sb_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_SB_"_sv))));
|
||||
if (sb_obj && sb_obj->node.is_scope())
|
||||
if (auto ret = AML::initialize_scope(sb_path); ret.is_error())
|
||||
dwarnln("Failed to initialize \\_SB: {}", ret.error());
|
||||
|
||||
dprintln("Evaluating \\_PIC");
|
||||
|
||||
// Evaluate \\_PIC (mode)
|
||||
auto [pic_path, pic_obj] = TRY(m_namespace->find_named_object({}, TRY(AML::NameString::from_string("\\_PIC"_sv))));
|
||||
if (pic_obj && pic_obj->node.type == AML::Node::Type::Method)
|
||||
{
|
||||
|
@ -698,7 +686,7 @@ acpi_release_global_lock:
|
|||
TRY(AML::method_call(pic_path, pic_node, BAN::move(arguments)));
|
||||
}
|
||||
|
||||
dprintln("Initializing ACPI interrupts");
|
||||
dprintln("Evaluated \\_PIC({})", mode);
|
||||
|
||||
uint8_t irq = fadt().sci_int;
|
||||
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
|
||||
|
@ -771,6 +759,15 @@ acpi_release_global_lock:
|
|||
Process::create_kernel([](void*) { get().acpi_event_task(); }, nullptr);
|
||||
}
|
||||
|
||||
dprintln("Initialized ACPI interrupts");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> ACPI::initialize_acpi_devices()
|
||||
{
|
||||
ASSERT(m_namespace);
|
||||
TRY(BatterySystem::initialize(*m_namespace));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
#include <kernel/ACPI/AML/Node.h>
|
||||
#include <kernel/ACPI/Headers.h>
|
||||
|
||||
#define STA_PRESENT 0x01
|
||||
#define STA_FUNCTION 0x08
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
|
@ -41,7 +46,7 @@ namespace Kernel::ACPI::AML
|
|||
delete reference;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::initialize_root_namespace()
|
||||
BAN::ErrorOr<void> Namespace::prepare_root_namespace()
|
||||
{
|
||||
// https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html?highlight=predefined#predefined-root-namespaces
|
||||
|
||||
|
@ -86,15 +91,11 @@ namespace Kernel::ACPI::AML
|
|||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
const auto arg0 = BAN::StringView(
|
||||
reinterpret_cast<const char*>(args[0]->node.as.str_buf->bytes),
|
||||
args[0]->node.as.str_buf->size
|
||||
);
|
||||
|
||||
Node result {};
|
||||
result.type = Node::Type::Integer;
|
||||
result.as.integer.value = 0;
|
||||
|
||||
const auto arg0 = args[0]->node.as.str_buf->as_sv();
|
||||
for (auto supported : s_supported_osi_strings)
|
||||
{
|
||||
if (supported != arg0)
|
||||
|
@ -130,10 +131,92 @@ namespace Kernel::ACPI::AML
|
|||
return s_root_namespace;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::initalize_op_regions()
|
||||
BAN::ErrorOr<uint64_t> Namespace::evaluate_sta(const Scope& scope)
|
||||
{
|
||||
m_has_parsed_namespace = true;
|
||||
auto [child_path, child_ref] = TRY(find_named_object(scope, TRY(NameString::from_string("_STA"_sv))));
|
||||
if (child_ref == nullptr)
|
||||
return 0x0F;
|
||||
return TRY(convert_node(TRY(evaluate_node(child_path, child_ref->node)), ConvInteger, sizeof(uint64_t))).as.integer.value;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::evaluate_ini(const Scope& scope)
|
||||
{
|
||||
auto [child_path, child_ref] = TRY(find_named_object(scope, TRY(NameString::from_string("_INI"_sv))));
|
||||
if (child_ref == nullptr)
|
||||
return {};
|
||||
TRY(evaluate_node(child_path, child_ref->node));
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::post_load_initialize()
|
||||
{
|
||||
BAN::Vector<Scope> to_init;
|
||||
TRY(to_init.push_back({}));
|
||||
|
||||
while (!to_init.empty())
|
||||
{
|
||||
BAN::Vector<Scope> to_init_next;
|
||||
|
||||
for (const Scope& current : to_init)
|
||||
{
|
||||
TRY(for_each_child(current,
|
||||
[&](const Scope& child_path, Reference* child_ref) -> BAN::Iteration
|
||||
{
|
||||
if (m_aliases.contains(child_path))
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
switch (child_ref->node.type)
|
||||
{
|
||||
case Node::Type::Device:
|
||||
case Node::Type::Processor:
|
||||
case Node::Type::ThermalZone:
|
||||
case Node::Type::PredefinedScope:
|
||||
break;
|
||||
default:
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
|
||||
auto sta_ret = evaluate_sta(child_path);
|
||||
if (sta_ret.is_error())
|
||||
return BAN::Iteration::Continue;
|
||||
|
||||
if (sta_ret.value() & STA_PRESENT)
|
||||
(void)evaluate_ini(child_path);
|
||||
|
||||
if ((sta_ret.value() & STA_PRESENT) || (sta_ret.value() & STA_FUNCTION))
|
||||
{
|
||||
auto child_path_copy = child_path.copy();
|
||||
if (!child_path_copy.is_error())
|
||||
(void)to_init_next.push_back(child_path_copy.release_value());
|
||||
}
|
||||
|
||||
(void)for_each_child(current,
|
||||
[&](const Scope& opregion_path, Reference* opregion_ref) -> BAN::Iteration
|
||||
{
|
||||
if (opregion_ref->node.type == Node::Type::OpRegion)
|
||||
(void)opregion_call_reg(opregion_path, opregion_ref->node);
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
);
|
||||
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
to_init = BAN::move(to_init_next);
|
||||
}
|
||||
|
||||
m_has_initialized_namespace = true;
|
||||
|
||||
if (auto ret = initialize_op_regions(); ret.is_error())
|
||||
dwarnln("Failed to initialize all opregions: {}", ret.error());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::initialize_op_regions()
|
||||
{
|
||||
for (const auto& [obj_path, obj_ref] : m_named_objects)
|
||||
{
|
||||
if (obj_ref->node.type != Node::Type::OpRegion)
|
||||
|
@ -247,15 +330,15 @@ namespace Kernel::ACPI::AML
|
|||
|
||||
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
|
||||
|
||||
if (m_has_parsed_namespace && reference->node.type == Node::Type::OpRegion)
|
||||
if (m_has_initialized_namespace && reference->node.type == Node::Type::OpRegion)
|
||||
(void)opregion_call_reg(resolved_path, reference->node);
|
||||
|
||||
return resolved_path;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Reference* reference)
|
||||
BAN::ErrorOr<Scope> Namespace::add_alias(const Scope& scope, const NameString& name_string, Reference* reference)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, reference->node);
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_alias('{}', '{}', {})", scope, name_string, reference->node);
|
||||
|
||||
auto resolved_path = TRY(resolve_path(scope, name_string));
|
||||
if (m_named_objects.contains(resolved_path))
|
||||
|
@ -265,6 +348,8 @@ namespace Kernel::ACPI::AML
|
|||
reference->ref_count++;
|
||||
|
||||
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
|
||||
TRY(m_aliases.insert(TRY(resolved_path.copy())));
|
||||
|
||||
return resolved_path;
|
||||
}
|
||||
|
||||
|
@ -384,6 +469,80 @@ namespace Kernel::ACPI::AML
|
|||
return {};
|
||||
}
|
||||
|
||||
static bool is_valid_eisa_id(BAN::StringView eisa_id)
|
||||
{
|
||||
if (eisa_id.size() != 7)
|
||||
return false;
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
if (!isupper(eisa_id[i]))
|
||||
return false;
|
||||
for (size_t i = 3; i < 7; i++)
|
||||
if (!isxdigit(eisa_id[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t encode_eisa_id(BAN::StringView eisa_id)
|
||||
{
|
||||
constexpr auto char_to_hex =
|
||||
[](char ch) -> uint32_t
|
||||
{
|
||||
if (isdigit(ch))
|
||||
return ch - '0';
|
||||
return tolower(ch) - 'a' + 10;
|
||||
};
|
||||
|
||||
uint16_t code = 0;
|
||||
code |= (eisa_id[2] - 0x40) << 0;
|
||||
code |= (eisa_id[1] - 0x40) << 5;
|
||||
code |= (eisa_id[0] - 0x40) << 10;
|
||||
|
||||
uint32_t encoded = 0;
|
||||
encoded |= code >> 8;
|
||||
encoded |= (code & 0xFF) << 8;
|
||||
encoded |= (char_to_hex(eisa_id[3]) << 20) | (char_to_hex(eisa_id[4]) << 16);
|
||||
encoded |= (char_to_hex(eisa_id[5]) << 28) | (char_to_hex(eisa_id[6]) << 24);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::Vector<Scope>> Namespace::find_device_with_eisa_id(BAN::StringView eisa_id)
|
||||
{
|
||||
if (!is_valid_eisa_id(eisa_id))
|
||||
{
|
||||
dwarnln("Invalid EISA id '{}'", eisa_id);
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
const uint32_t encoded = encode_eisa_id(eisa_id);
|
||||
|
||||
BAN::Vector<Scope> result;
|
||||
|
||||
for (const auto& [obj_path, obj_ref] : m_named_objects)
|
||||
{
|
||||
if (obj_ref->node.type != Node::Type::Device)
|
||||
continue;
|
||||
|
||||
auto [_, hid] = TRY(find_named_object(obj_path, TRY(NameString::from_string("_HID"_sv)), true));
|
||||
if (hid == nullptr)
|
||||
continue;
|
||||
|
||||
uint32_t device_hid = 0;
|
||||
if (hid->node.type == Node::Type::Integer)
|
||||
device_hid = hid->node.as.integer.value;
|
||||
else if (hid->node.type == Node::Type::String && is_valid_eisa_id(hid->node.as.str_buf->as_sv()))
|
||||
device_hid = encode_eisa_id(hid->node.as.str_buf->as_sv());
|
||||
else
|
||||
continue;
|
||||
|
||||
if (device_hid != encoded)
|
||||
continue;
|
||||
|
||||
TRY(result.push_back(TRY(obj_path.copy())));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<Node> Namespace::evaluate(BAN::StringView path)
|
||||
{
|
||||
Scope root_scope;
|
||||
|
@ -429,10 +588,4 @@ namespace Kernel::ACPI::AML
|
|||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Namespace::initialize_devices()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -803,7 +803,7 @@ namespace Kernel::ACPI::AML
|
|||
if (pkg_element.value.node->type == Node::Type::Reference)
|
||||
*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), pkg_element.value.node->as.reference->node));
|
||||
else
|
||||
*pkg_element.value.node = BAN::move(source_copy);
|
||||
*pkg_element.value.node = TRY(convert_node(BAN::move(source_copy), *pkg_element.value.node));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -816,7 +816,11 @@ namespace Kernel::ACPI::AML
|
|||
if (target_type == TargetType::Reference)
|
||||
target->node = TRY(convert_node(BAN::move(source_copy), target->node));
|
||||
else
|
||||
{
|
||||
if (source_copy.type == Node::Type::FieldUnit)
|
||||
source_copy = TRY(convert_from_field_unit(source_copy, ConvInteger | ConvBuffer, ONES));
|
||||
target->node = BAN::move(source_copy);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -1507,10 +1511,7 @@ namespace Kernel::ACPI::AML
|
|||
return {};
|
||||
};
|
||||
|
||||
auto source_sv = BAN::StringView(
|
||||
reinterpret_cast<const char*>(source.as.str_buf->bytes),
|
||||
source.as.str_buf->size
|
||||
);
|
||||
auto source_sv = source.as.str_buf->as_sv();
|
||||
|
||||
while (!source_sv.empty() && !isdigit(source_sv[0]) && source_sv[0] != '-' && source_sv[0] != '+')
|
||||
source_sv = source_sv.substring(1);
|
||||
|
@ -1567,6 +1568,42 @@ namespace Kernel::ACPI::AML
|
|||
return result;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<Node> parse_to_string_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_to_string_op");
|
||||
|
||||
ASSERT(!context.aml_data.empty());
|
||||
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ToStringOp);
|
||||
context.aml_data = context.aml_data.slice(1);
|
||||
|
||||
auto source = TRY(convert_node(TRY(parse_node(context)), ConvBuffer, ONES));
|
||||
auto length = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t))).as.integer.value;
|
||||
|
||||
if (source.as.str_buf->size < length)
|
||||
length = source.as.str_buf->size;
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
{
|
||||
if (source.as.str_buf->bytes[i] != 0x00)
|
||||
continue;
|
||||
length = i;
|
||||
break;
|
||||
}
|
||||
|
||||
Node result;
|
||||
result.type = Node::Type::String;
|
||||
result.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + length));
|
||||
if (result.as.str_buf == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
memcpy(result.as.str_buf->bytes, source.as.str_buf->bytes, length);
|
||||
result.as.str_buf->size = length;
|
||||
result.as.str_buf->ref_count = 1;
|
||||
|
||||
TRY(store_into_target(context, source));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<void> parse_alias_op(ParseContext& context)
|
||||
{
|
||||
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_alias_op");
|
||||
|
@ -1585,7 +1622,7 @@ namespace Kernel::ACPI::AML
|
|||
return {};
|
||||
}
|
||||
|
||||
TRY(Namespace::root_namespace().add_named_object(context.scope, object_name_string, source_ref));
|
||||
TRY(Namespace::root_namespace().add_alias(context.scope, object_name_string, source_ref));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -2629,6 +2666,8 @@ namespace Kernel::ACPI::AML
|
|||
case AML::Byte::ToHexStringOp:
|
||||
case AML::Byte::ToIntegerOp:
|
||||
return TRY(parse_explicit_conversion(context));
|
||||
case AML::Byte::ToStringOp:
|
||||
return TRY(parse_to_string_op(context));
|
||||
case AML::Byte::IncrementOp:
|
||||
case AML::Byte::DecrementOp:
|
||||
return TRY(parse_inc_dec_op(context));
|
||||
|
@ -2914,8 +2953,11 @@ namespace Kernel::ACPI::AML
|
|||
name = name.substring(result.base);
|
||||
}
|
||||
|
||||
ASSERT((name.size() % 4) == 0);
|
||||
TRY(result.parts.reserve(name.size() / 4));
|
||||
if (name.empty())
|
||||
return result;
|
||||
|
||||
ASSERT((name.size() % 5) == 4);
|
||||
TRY(result.parts.reserve((name.size() / 5) + 1));
|
||||
|
||||
while (!name.empty())
|
||||
{
|
||||
|
@ -2927,6 +2969,11 @@ namespace Kernel::ACPI::AML
|
|||
};
|
||||
TRY(result.parts.push_back(name_seg));
|
||||
name = name.substring(4);
|
||||
if (!name.empty())
|
||||
{
|
||||
ASSERT(name.front() == '.');
|
||||
name = name.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
#include <kernel/ACPI/AML/Namespace.h>
|
||||
#include <kernel/ACPI/AML/Scope.h>
|
||||
|
||||
namespace Kernel::ACPI::AML
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> initialize_scope(const Scope& scope)
|
||||
{
|
||||
bool run_ini = true;
|
||||
bool init_children = true;
|
||||
|
||||
if (auto [sta_path, sta_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_STA"_sv)), true)); sta_obj)
|
||||
{
|
||||
auto sta_result = TRY(evaluate_node(sta_path, sta_obj->node));
|
||||
if (sta_result.type != Node::Type::Integer)
|
||||
{
|
||||
dwarnln("Object {} evaluated to {}", sta_path, sta_result);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
run_ini = (sta_result.as.integer.value & 0x01);
|
||||
init_children = run_ini || (sta_result.as.integer.value & 0x02);
|
||||
}
|
||||
|
||||
if (run_ini)
|
||||
{
|
||||
if (auto [ini_path, ini_obj] = TRY(Namespace::root_namespace().find_named_object(scope, TRY(NameString::from_string("_INI"_sv)), true)); ini_obj)
|
||||
{
|
||||
auto& ini_node = ini_obj->node;
|
||||
|
||||
if (ini_node.type != Node::Type::Method)
|
||||
{
|
||||
dwarnln("Object {} is not a method", ini_path);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
if (ini_node.as.method.arg_count != 0)
|
||||
{
|
||||
dwarnln("Method {} takes {} arguments, expected 0", ini_path, ini_node.as.method.arg_count);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
TRY(method_call(ini_path, ini_node, {}));
|
||||
}
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> result {};
|
||||
if (init_children)
|
||||
{
|
||||
TRY(Namespace::root_namespace().for_each_child(scope,
|
||||
[&result](const Scope& child_path, Reference* child) -> BAN::Iteration
|
||||
{
|
||||
if (!child->node.is_scope())
|
||||
return BAN::Iteration::Continue;
|
||||
if (auto ret = initialize_scope(child_path); ret.is_error())
|
||||
result = ret.release_error();
|
||||
return BAN::Iteration::Continue;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
#include <kernel/ACPI/BatterySystem.h>
|
||||
#include <kernel/FS/DevFS//FileSystem.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel::ACPI
|
||||
{
|
||||
|
||||
static BAN::UniqPtr<BatterySystem> s_instance;
|
||||
|
||||
class BatteryInfoInode final : public TmpInode
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<BatteryInfoInode>> create_new(
|
||||
AML::Namespace& acpi_namespace,
|
||||
const AML::Scope& battery_path,
|
||||
BAN::StringView method,
|
||||
size_t index,
|
||||
mode_t mode, uid_t uid, gid_t gid)
|
||||
{
|
||||
auto inode_info = create_inode_info(mode | Mode::IFREG, uid, gid);
|
||||
auto ino = TRY(DevFileSystem::get().allocate_inode(inode_info));
|
||||
|
||||
auto battery_path_copy = TRY(battery_path.copy());
|
||||
auto method_copy = TRY(AML::NameString::from_string(method));
|
||||
|
||||
auto* inode_ptr = new BatteryInfoInode(acpi_namespace, BAN::move(battery_path_copy), BAN::move(method_copy), index, ino, inode_info);
|
||||
if (inode_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return BAN::RefPtr<BatteryInfoInode>::adopt(inode_ptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
BAN::ErrorOr<size_t> read_impl(off_t offset, BAN::ByteSpan buffer) override
|
||||
{
|
||||
if (offset < 0)
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
|
||||
if (SystemTimer::get().ms_since_boot() > m_last_read_ms + 1000)
|
||||
{
|
||||
auto [method_path, method_ref] = TRY(m_acpi_namespace.find_named_object(m_battery_path, m_method_name));
|
||||
if (method_ref == nullptr)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
auto result = TRY(AML::method_call(method_path, method_ref->node, {}));
|
||||
if (result.type != AML::Node::Type::Package || result.as.package->num_elements < m_result_index)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
auto& target_elem = result.as.package->elements[m_result_index];
|
||||
if (!target_elem.resolved || !target_elem.value.node)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
auto target_conv = AML::convert_node(TRY(target_elem.value.node->copy()), AML::ConvInteger, sizeof(uint64_t));
|
||||
if (target_conv.is_error())
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
m_last_read_ms = SystemTimer::get().ms_since_boot();
|
||||
m_last_value = target_conv.value().as.integer.value;
|
||||
}
|
||||
|
||||
auto target_str = TRY(BAN::String::formatted("{}", m_last_value));
|
||||
|
||||
if (static_cast<size_t>(offset) >= target_str.size())
|
||||
return 0;
|
||||
|
||||
const size_t ncopy = BAN::Math::min(buffer.size(), target_str.size() - offset);
|
||||
memcpy(buffer.data(), target_str.data() + offset, ncopy);
|
||||
return ncopy;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
|
||||
BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(EINVAL); }
|
||||
|
||||
bool can_read_impl() const override { return true; }
|
||||
bool can_write_impl() const override { return false; }
|
||||
bool has_error_impl() const override { return false; }
|
||||
|
||||
private:
|
||||
BatteryInfoInode(AML::Namespace& acpi_namespace, AML::Scope&& battery_path, AML::NameString&& method, size_t index, ino_t ino, const TmpInodeInfo& info)
|
||||
: TmpInode(DevFileSystem::get(), ino, info)
|
||||
, m_acpi_namespace(acpi_namespace)
|
||||
, m_battery_path(BAN::move(battery_path))
|
||||
, m_method_name(BAN::move(method))
|
||||
, m_result_index(index)
|
||||
{ }
|
||||
|
||||
private:
|
||||
AML::Namespace& m_acpi_namespace;
|
||||
AML::Scope m_battery_path;
|
||||
AML::NameString m_method_name;
|
||||
size_t m_result_index;
|
||||
|
||||
uint64_t m_last_read_ms = 0;
|
||||
uint64_t m_last_value = 0;
|
||||
};
|
||||
|
||||
BAN::ErrorOr<void> BatterySystem::initialize(AML::Namespace& acpi_namespace)
|
||||
{
|
||||
ASSERT(!s_instance);
|
||||
|
||||
auto* battery_system = new BatterySystem(acpi_namespace);
|
||||
if (battery_system == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
s_instance = BAN::UniqPtr<BatterySystem>::adopt(battery_system);
|
||||
|
||||
TRY(s_instance->initialize_impl());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BatterySystem::BatterySystem(AML::Namespace& acpi_namespace)
|
||||
: m_acpi_namespace(acpi_namespace)
|
||||
{ }
|
||||
|
||||
BAN::ErrorOr<void> BatterySystem::initialize_impl()
|
||||
{
|
||||
auto base_inode = TRY(TmpDirectoryInode::create_new(DevFileSystem::get(), 0555, 0, 0, static_cast<TmpInode&>(*DevFileSystem::get().root_inode())));
|
||||
DevFileSystem::get().add_inode("batteries", base_inode);
|
||||
|
||||
auto batteries = TRY(m_acpi_namespace.find_device_with_eisa_id("PNP0C0A"_sv));
|
||||
for (const auto& battery : batteries)
|
||||
{
|
||||
auto [_1, bif_ref] = TRY(m_acpi_namespace.find_named_object(battery, TRY(AML::NameString::from_string("_BIF"_sv))));
|
||||
if (!bif_ref || bif_ref->node.type != AML::Node::Type::Method || bif_ref->node.as.method.arg_count != 0)
|
||||
{
|
||||
dwarnln("Battery {} does not have _BIF or it is invalid", battery);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto [_2, bst_ref] = TRY(m_acpi_namespace.find_named_object(battery, TRY(AML::NameString::from_string("_BST"_sv))));
|
||||
if (!bst_ref || bst_ref->node.type != AML::Node::Type::Method || bst_ref->node.as.method.arg_count != 0)
|
||||
{
|
||||
dwarnln("Battery {} does not have _BST or it is invalid", battery);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto battery_name = BAN::StringView(reinterpret_cast<const char*>(&battery.parts.back()), 4);
|
||||
auto battery_inode = TRY(TmpDirectoryInode::create_new(DevFileSystem::get(), 0555, 0, 0, *base_inode));
|
||||
TRY(base_inode->link_inode(*battery_inode, battery_name));
|
||||
|
||||
auto cap_full_inode = TRY(BatteryInfoInode::create_new(m_acpi_namespace, battery, "_BIF"_sv, 2, 0444, 0, 0));
|
||||
TRY(battery_inode->link_inode(*cap_full_inode, "capacity_full"_sv));
|
||||
|
||||
auto cap_now_inode = TRY(BatteryInfoInode::create_new(m_acpi_namespace, battery, "_BST"_sv, 2, 0444, 0, 0));
|
||||
TRY(battery_inode->link_inode(*cap_now_inode, "capacity_now"_sv));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -60,7 +60,9 @@ namespace Kernel
|
|||
ret = sys_fork_trampoline();
|
||||
else
|
||||
#pragma GCC diagnostic push
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic warning "-Wmaybe-uninitialized"
|
||||
#endif
|
||||
ret = (Process::current().*s_syscall_handlers[syscall])(arg1, arg2, arg3, arg4, arg5);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
|
|
|
@ -231,6 +231,9 @@ static void init2(void*)
|
|||
if (!cmdline.disable_acpi && ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()).is_error())
|
||||
dprintln("Failed to enter ACPI mode");
|
||||
|
||||
if (auto ret = ACPI::ACPI::get().initialize_acpi_devices(); ret.is_error())
|
||||
dwarnln("Could not initialize ACPI devices: {}", ret.error());
|
||||
|
||||
DevFileSystem::get().initialize_device_updater();
|
||||
|
||||
#if 0
|
||||
|
|
|
@ -15,6 +15,8 @@ set(AOC2024_PROJECTS
|
|||
day14
|
||||
day15
|
||||
day16
|
||||
day17
|
||||
day18
|
||||
full
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2024_day17 ${SOURCES})
|
||||
banan_include_headers(aoc2024_day17 ban)
|
||||
banan_link_library(aoc2024_day17 libc)
|
||||
|
||||
install(TARGETS aoc2024_day17 OPTIONAL)
|
|
@ -0,0 +1,223 @@
|
|||
#include <BAN/Vector.h>
|
||||
#include <BAN/String.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using isize = ssize_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using usize = size_t;
|
||||
|
||||
struct Registers
|
||||
{
|
||||
u64 a, b, c;
|
||||
usize ip;
|
||||
};
|
||||
|
||||
enum Opcodes
|
||||
{
|
||||
ADV = 0,
|
||||
BXL = 1,
|
||||
BST = 2,
|
||||
JNZ = 3,
|
||||
BXC = 4,
|
||||
OUT = 5,
|
||||
BDV = 6,
|
||||
CDV = 7,
|
||||
};
|
||||
|
||||
using Program = BAN::Vector<u8>;
|
||||
|
||||
struct ParseInputResult
|
||||
{
|
||||
Registers registers;
|
||||
Program program;
|
||||
};
|
||||
|
||||
static ParseInputResult parse_input(FILE* fp)
|
||||
{
|
||||
Registers registers;
|
||||
ASSERT(fscanf(fp,
|
||||
"Register A: %" SCNu64 "\n"
|
||||
"Register B: %" SCNu64 "\n"
|
||||
"Register C: %" SCNu64 "\n"
|
||||
"\n"
|
||||
"Program: ",
|
||||
®isters.a, ®isters.b, ®isters.c
|
||||
) == 3);
|
||||
registers.ip = 0;
|
||||
|
||||
char buffer[128];
|
||||
ASSERT(fgets(buffer, sizeof(buffer), fp));
|
||||
|
||||
Program program;
|
||||
for (usize i = 0; buffer[i]; i++)
|
||||
if (isdigit(buffer[i]))
|
||||
MUST(program.push_back(buffer[i] - '0'));
|
||||
|
||||
return ParseInputResult {
|
||||
.registers = BAN::move(registers),
|
||||
.program = BAN::move(program)
|
||||
};
|
||||
}
|
||||
|
||||
BAN::Vector<u64> emulate_program(Registers registers, const Program& program)
|
||||
{
|
||||
const auto combo =
|
||||
[®isters](u8 combo) -> u64
|
||||
{
|
||||
switch (combo)
|
||||
{
|
||||
case 0: case 1: case 2: case 3: return combo;
|
||||
case 4: return registers.a;
|
||||
case 5: return registers.b;
|
||||
case 6: return registers.c;
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
};
|
||||
|
||||
BAN::Vector<u64> output;
|
||||
while (registers.ip < program.size())
|
||||
{
|
||||
const u8 opcode = program[registers.ip + 0];
|
||||
const u8 operand = program[registers.ip + 1];
|
||||
registers.ip += 2;
|
||||
|
||||
switch (opcode)
|
||||
{
|
||||
case Opcodes::ADV:
|
||||
registers.a = registers.a >> combo(operand);
|
||||
break;
|
||||
case Opcodes::BXL:
|
||||
registers.b = registers.b ^ operand;
|
||||
break;
|
||||
case Opcodes::BST:
|
||||
registers.b = combo(operand) & 0x07;
|
||||
break;
|
||||
case Opcodes::JNZ:
|
||||
if (registers.a != 0)
|
||||
registers.ip = operand;
|
||||
break;
|
||||
case Opcodes::BXC:
|
||||
registers.b = registers.b ^ registers.c;
|
||||
break;
|
||||
case Opcodes::OUT:
|
||||
MUST(output.push_back(combo(operand) & 0x07));
|
||||
break;
|
||||
case Opcodes::BDV:
|
||||
registers.b = registers.a >> combo(operand);
|
||||
break;
|
||||
case Opcodes::CDV:
|
||||
registers.c = registers.a >> combo(operand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
BAN::String part1(FILE* fp)
|
||||
{
|
||||
auto [registers, program] = parse_input(fp);
|
||||
auto output = emulate_program(registers, program);
|
||||
|
||||
BAN::String result;
|
||||
MUST(result.resize(output.size() * 2 - 1));
|
||||
|
||||
for (usize i = 0; i < result.size(); i++)
|
||||
result[i] = (i % 2) ? ',' : output[i / 2] + '0';
|
||||
return result;
|
||||
}
|
||||
|
||||
static BAN::Optional<u64> recurse_part2(Registers initial_registers, u64 curr_a, usize curr_bits, usize output_done, const Program& program)
|
||||
{
|
||||
if (output_done >= program.size())
|
||||
return {};
|
||||
|
||||
BAN::Optional<u64> result;
|
||||
for (u64 val = 0; val < 8; val++)
|
||||
{
|
||||
const u64 next_a = curr_a | (val << curr_bits);
|
||||
|
||||
auto registers = initial_registers;
|
||||
registers.a = next_a;
|
||||
|
||||
auto output = emulate_program(registers, program);
|
||||
if (output.size() < output_done + 1)
|
||||
continue;
|
||||
if (output.size() > program.size())
|
||||
continue;
|
||||
|
||||
bool match = true;
|
||||
for (usize i = 0; i < output_done + 1 && match; i++)
|
||||
if (output[i] != program[i])
|
||||
match = false;
|
||||
if (!match)
|
||||
continue;
|
||||
|
||||
if (output_done + 1 == program.size())
|
||||
return next_a;
|
||||
|
||||
auto temp = recurse_part2(registers, next_a, curr_bits + 3, output_done + 1, program);
|
||||
if (temp.has_value())
|
||||
result = BAN::Math::min(result.value_or(BAN::numeric_limits<u64>::max()), temp.value());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 part2(FILE* fp)
|
||||
{
|
||||
auto [initial_registers, program] = parse_input(fp);
|
||||
|
||||
BAN::Optional<u64> result;
|
||||
for (u64 val = 0; val < 1024; val++)
|
||||
{
|
||||
auto registers = initial_registers;
|
||||
registers.a = val;
|
||||
|
||||
auto output = emulate_program(registers, program);
|
||||
if (output.empty() || output.size() > program.size())
|
||||
continue;
|
||||
if (output[0] != program[0])
|
||||
continue;
|
||||
|
||||
auto temp = recurse_part2(registers, val, 10, 1, program);
|
||||
if (temp.has_value())
|
||||
result = BAN::Math::min(result.value_or(BAN::numeric_limits<u64>::max()), temp.value());
|
||||
}
|
||||
|
||||
return result.value();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2024/day17_input.txt";
|
||||
|
||||
if (argc >= 2)
|
||||
file_path = argv[1];
|
||||
|
||||
FILE* fp = fopen(file_path, "r");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("part1: %s\n", part1(fp).data());
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
printf("part2: %" PRId64 "\n", part2(fp));
|
||||
|
||||
fclose(fp);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
add_executable(aoc2024_day18 ${SOURCES})
|
||||
banan_include_headers(aoc2024_day18 ban)
|
||||
banan_link_library(aoc2024_day18 libc)
|
||||
|
||||
install(TARGETS aoc2024_day18 OPTIONAL)
|
|
@ -0,0 +1,125 @@
|
|||
#include <BAN/Array.h>
|
||||
#include <BAN/String.h>
|
||||
#include <BAN/Vector.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
using i8 = int8_t;
|
||||
using i16 = int16_t;
|
||||
using i32 = int32_t;
|
||||
using i64 = int64_t;
|
||||
using isize = ssize_t;
|
||||
|
||||
using u8 = uint8_t;
|
||||
using u16 = uint16_t;
|
||||
using u32 = uint32_t;
|
||||
using u64 = uint64_t;
|
||||
using usize = size_t;
|
||||
|
||||
template<typename T>
|
||||
struct Vec2
|
||||
{
|
||||
T x, y;
|
||||
};
|
||||
|
||||
template<usize W, usize H>
|
||||
static BAN::Optional<usize> steps_to_finnish(BAN::Array<BAN::Array<bool, W>, H>& map)
|
||||
{
|
||||
BAN::Vector<Vec2<usize>> pending;
|
||||
MUST(pending.push_back({ 0, 0 }));
|
||||
|
||||
for (usize step = 0;; step++)
|
||||
{
|
||||
BAN::Vector<Vec2<usize>> next;
|
||||
for (auto pos : pending)
|
||||
{
|
||||
if (map[pos.y][pos.x])
|
||||
continue;
|
||||
map[pos.y][pos.x] = true;
|
||||
|
||||
if (pos.x == W - 1 && pos.y == H - 1)
|
||||
return step;
|
||||
|
||||
if (pos.y > 0 && !map[pos.y - 1][pos.x ]) MUST(next.push_back({ pos.x, pos.y - 1 }));
|
||||
if (pos.x > 0 && !map[pos.y ][pos.x - 1]) MUST(next.push_back({ pos.x - 1, pos.y }));
|
||||
if (pos.y < H - 1 && !map[pos.y + 1][pos.x ]) MUST(next.push_back({ pos.x, pos.y + 1 }));
|
||||
if (pos.x < W - 1 && !map[pos.y ][pos.x + 1]) MUST(next.push_back({ pos.x + 1, pos.y }));
|
||||
}
|
||||
|
||||
if (next.empty())
|
||||
break;
|
||||
pending = BAN::move(next);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
i64 part1(FILE* fp)
|
||||
{
|
||||
BAN::Array<BAN::Array<bool, 71>, 71> map(false);
|
||||
for (usize i = 0; i < 1024; i++)
|
||||
{
|
||||
usize x, y;
|
||||
ASSERT(fscanf(fp, "%zu,%zu\n", &x, &y) == 2);
|
||||
map[y][x] = true;
|
||||
}
|
||||
|
||||
return steps_to_finnish(map).value();
|
||||
}
|
||||
|
||||
BAN::String part2(FILE* fp)
|
||||
{
|
||||
BAN::Vector<Vec2<usize>> bytes;
|
||||
for (;;)
|
||||
{
|
||||
usize x, y;
|
||||
if (fscanf(fp, "%zu,%zu\n", &x, &y) != 2)
|
||||
break;
|
||||
MUST(bytes.push_back({ x, y }));
|
||||
}
|
||||
|
||||
usize l = 0;
|
||||
usize r = bytes.size() - 1;
|
||||
|
||||
while (l <= r)
|
||||
{
|
||||
const usize mid = l + (r - l) / 2;
|
||||
|
||||
BAN::Array<BAN::Array<bool, 71>, 71> map(false);
|
||||
for (usize i = 0; i <= mid; i++)
|
||||
map[bytes[i].y][bytes[i].x] = true;
|
||||
|
||||
if (steps_to_finnish(map).has_value())
|
||||
l = mid + 1;
|
||||
else if (l == mid)
|
||||
return MUST(BAN::String::formatted("{},{}", bytes[mid].x, bytes[mid].y));
|
||||
else
|
||||
r = mid;
|
||||
}
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* file_path = "/usr/share/aoc2024/day18_input.txt";
|
||||
|
||||
if (argc >= 2)
|
||||
file_path = argv[1];
|
||||
|
||||
FILE* fp = fopen(file_path, "r");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
perror("fopen");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("part1: %" PRId64 "\n", part1(fp));
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
printf("part2: %s\n", part2(fp).data());
|
||||
|
||||
fclose(fp);
|
||||
}
|
|
@ -1,15 +1,70 @@
|
|||
#include <LibFont/Font.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
|
||||
static BAN::String get_task_bar_string()
|
||||
static BAN::ErrorOr<long long> read_integer_from_file(const char* file)
|
||||
{
|
||||
char buffer[128];
|
||||
|
||||
int fd = open(file, O_RDONLY);
|
||||
if (fd == -1)
|
||||
return BAN::Error::from_errno(errno);
|
||||
const ssize_t nread = read(fd, buffer, sizeof(buffer));
|
||||
close(fd);
|
||||
|
||||
if (nread < 0)
|
||||
return BAN::Error::from_errno(errno);
|
||||
if (nread == 0)
|
||||
return BAN::Error::from_errno(ENODATA);
|
||||
|
||||
buffer[nread] = '\0';
|
||||
return atoll(buffer);
|
||||
}
|
||||
|
||||
static BAN::String get_battery_percentage()
|
||||
{
|
||||
DIR* dirp = opendi |