Compare commits

...

12 Commits

Author SHA1 Message Date
Bananymous 3a6cdfff45 Kernel: Fix ACPI namespace initialization
Now _REG, _STA, _INI are called in the order my laptop expects them to
be called. This was kinda weird because what uACPI was doing did not
work.

\_SB_.PCI0.LPC0.EC0_.BAT0._STA required \_SB_.PCI0.LPC0.EC0_._REG to be
called
\_SB_.PCI0.LPC0.EC0_._REG required \_SB_.PCI0._STA to be called

Now I call all the _REG methods of a device after calling _STA/_INI and
after performing the whole _STA/_INI sequence i call rest of missing
_REG functions
2024-12-18 18:30:45 +02:00
Bananymous c26e347e91 Kernel: Fix AML FieldUnit conversion and NameString construction 2024-12-18 18:29:04 +02:00
Bananymous 1b94957b07 Kernel: Copy AML NameString base when moving it :) 2024-12-18 18:28:16 +02:00
Bananymous 2e39668605 aoc2024: Implement day18 solution 2024-12-18 14:06:32 +02:00
Bananymous 0642c569b4 TaskBar: Show battery percentage if available 2024-12-18 14:06:32 +02:00
Bananymous 4e364bd2f6 Kernel: Add support for ACPI Control Method Batteries
The implementation is kinda weird but it exposes some battery
information to userspace!
2024-12-18 14:06:32 +02:00
Bananymous b9fe564d78 Kernel: Check for GCC when ignoring Wmaybe-uninitialized 2024-12-18 01:45:36 +02:00
Bananymous 437fa45ca2 Kernel: Add helper to find ACPI devices with EISA id 2024-12-18 01:45:00 +02:00
Bananymous 85e215650c Kernel: Implement AML ToStringOp 2024-12-18 01:44:33 +02:00
Bananymous 7eb186dad4 Kernel: Add to_sv() helper for AML String 2024-12-18 01:43:46 +02:00
Bananymous 2259614640 Kernel: Remove unused function from ACPI namespace 2024-12-18 01:43:04 +02:00
Bananymous a584e1a4ec aoc2024: Implement day17 solution 2024-12-17 23:30:37 +02:00
19 changed files with 875 additions and 128 deletions

View File

@ -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

View File

@ -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();

View File

@ -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;
};
}

View File

@ -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);

View File

@ -37,8 +37,6 @@ namespace Kernel::ACPI::AML
}
};
BAN::ErrorOr<void> initialize_scope(const Scope&);
}
namespace BAN

View File

@ -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;
};
}

View File

@ -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 {};
}

View File

@ -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 {};
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 {};
}
}

View File

@ -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

View File

@ -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

View File

@ -15,6 +15,8 @@ set(AOC2024_PROJECTS
day14
day15
day16
day17
day18
full
)

View File

@ -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)

View File

@ -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: ",
&registers.a, &registers.b, &registers.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 =
[&registers](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);
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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