Compare commits

...

37 Commits

Author SHA1 Message Date
Bananymous c25a95f5c2 Kernel: save and load sse context in more spaces 2025-04-02 12:51:27 +03:00
Bananymous a1ba4a07cc Kernel/LibC: Implement pthread_join 2025-04-02 12:50:56 +03:00
Bananymous 8be2f15f27 LibC: Fix pthread stack alignment 2025-04-02 12:49:45 +03:00
Bananymous cdae749ce2 LibC: Implement pthread_spin_* functions 2025-04-02 12:49:20 +03:00
Bananymous 8a7b8e21ec fixup pthread_t 2025-04-02 12:48:30 +03:00
Bananymous 587095e04e fixup sched_yield 2025-04-02 12:47:51 +03:00
Bananymous 2935e2c8a5 LibC: Make pthread_t pid_t 2025-04-02 12:46:43 +03:00
Bananymous 2777ec423c LibC: Implement stdio FILE locking 2025-04-02 12:45:44 +03:00
Bananymous 4fb119ad47 Kernel/LibC: Implement sched_yield 2025-04-02 02:44:31 +03:00
Bananymous 5549696c3a Kernel/LibC: Implement pthread_self 2025-04-02 02:39:29 +03:00
Bananymous 691c9fe8e0 LibC: Fix bsearch
bsearch had unsigned integer underflow which was UB and returned false
positives
2025-04-02 00:03:50 +03:00
Bananymous 04463675c0 LibC: Lock file during gets 2025-04-01 23:26:13 +03:00
Bananymous b9da6a4a5d LibC: Implement getdelim and getline 2025-04-01 23:26:13 +03:00
Bananymous 9fb161c320 Kernel: Move thread stacks 1 MiB back 2025-04-01 23:26:13 +03:00
Bananymous 2ba25b4c28 LibC: Add non standard types/values used by ports 2025-04-01 23:26:13 +03:00
Bananymous c1618e2b5d Kernel/LibC: Add basic support for pthread_{create,exit} 2025-04-01 23:26:13 +03:00
Bananymous 788f5429e1 LibC: Implement spec compliant abort() 2025-04-01 23:09:30 +03:00
Bananymous 36026d4ec6 Kernel: Cleanup TCP and add basic window scaling support 2025-04-01 23:09:30 +03:00
Bananymous 96767f5ca8 Kernel: Implement PCI interrupt routing
This does not really work but I have no idea what I'm doing wrong
2025-04-01 23:09:30 +03:00
Bananymous 76bad31dd5 Kernel: Reorder locking with VirtualRange 2025-04-01 23:09:30 +03:00
Bananymous 2e3b917192 Kernel: Cleanup PS2 mouse button event generation 2025-04-01 23:09:30 +03:00
Bananymous 95f262b3e6 Kernel: Print process pid through /dev/debug 2025-04-01 23:09:30 +03:00
Bananymous aebacb6b6a Kernel: Bypass PS2 detection if scancode set is forced 2025-04-01 23:09:30 +03:00
Bananymous 27613da5ea Kernel: Route PCI Interrupt Link Device interrupts 2025-04-01 23:09:30 +03:00
Bananymous 28ac6c2267 Kernel: Implement AML FindSet{Left,Right}Bit 2025-04-01 23:09:30 +03:00
Bananymous 1c1fc65c7c Kernel: Add API to reserve GSI instead of irq
This also fixes a bug with IOAPIC gsi_base != 0
2025-04-01 23:09:30 +03:00
Bananymous ba74b352bd Kernel: Make BufferField actually access the buffer and not copy 2025-04-01 23:09:30 +03:00
Bananymous 0474ac4262 Kernel: Expose ACPI resolve_package_element to outside 2025-04-01 23:09:30 +03:00
Bananymous aba49cc93f Kernel: Add API to get ACPI reference paths 2025-04-01 23:09:30 +03:00
Bananymous b3b5b40163 Kernel: Add _OS an _REV to ACPI namespace 2025-04-01 23:09:30 +03:00
Bananymous 0e085b30cc Kernel: ACPI add easier API for calling methods with arguments 2025-04-01 23:09:30 +03:00
Bananymous 35149b6960 Kernel: Add helper functions to create AML String and Buffer 2025-04-01 23:09:30 +03:00
Bananymous 3800d5420e Kernel: Collect created AML nodes in add_{named,alias}...
This makes it harder to miss adding scoped objects. Before I was not
deleting all types of nodes on method return
2025-04-01 23:09:25 +03:00
Bananymous b145c1ab64 Kernel: Add easier api for evaluating ACPI nodes 2025-04-01 23:09:20 +03:00
Bananymous b8a3439219 Kernel: Add getter for ACPI namespace 2025-04-01 23:09:15 +03:00
Bananymous 1a153b835c BAN: Nullterminate encoded UTF8 data 2025-04-01 22:39:56 +03:00
Bananymous 73c292c4e9 BAN: Fix negative floating point value printing 2025-04-01 22:39:21 +03:00
39 changed files with 1173 additions and 154 deletions

View File

@ -204,10 +204,14 @@ namespace BAN::Formatter
template<typename F, typename T>
inline void print_floating(F putc, T value, const ValueFormat& format)
{
if (value < 0)
{
putc('-');
return print_floating(putc, -value, format);
}
int64_t int_part = (int64_t)value;
T frac_part = value - (T)int_part;
if (frac_part < 0)
frac_part = -frac_part;
print_integer(putc, int_part, format);

View File

@ -76,6 +76,8 @@ namespace BAN::UTF8
}
}
*ptr = '\0';
return true;
}

View File

@ -36,6 +36,8 @@ start_kernel_thread:
.global start_userspace_thread
start_userspace_thread:
call load_thread_sse
call get_thread_start_sp
movl %eax, %esp

View File

@ -27,6 +27,8 @@ start_kernel_thread:
.global start_userspace_thread
start_userspace_thread:
call load_thread_sse
call get_thread_start_sp
movq %rax, %rsp

View File

@ -30,6 +30,8 @@ namespace Kernel::ACPI
BAN::ErrorOr<void> initialize_acpi_devices();
AML::Namespace* acpi_namespace() { return m_namespace; }
BAN::ErrorOr<void> poweroff();
BAN::ErrorOr<void> reset();
@ -46,6 +48,8 @@ namespace Kernel::ACPI
BAN::ErrorOr<void> load_aml_tables(BAN::StringView name, bool all);
BAN::ErrorOr<void> route_interrupt_link_device(const AML::Scope& device, uint64_t& routed_irq_mask);
private:
paddr_t m_header_table_paddr = 0;
vaddr_t m_header_table_vaddr = 0;

View File

@ -27,11 +27,12 @@ namespace Kernel::ACPI::AML
BAN::ErrorOr<void> parse(BAN::ConstByteSpan);
BAN::ErrorOr<Node> evaluate(BAN::StringView);
BAN::ErrorOr<Node> evaluate(BAN::StringView path) { return evaluate(Scope {}, path); }
BAN::ErrorOr<Node> evaluate(const Scope& scope, BAN::StringView);
// 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_alias(const Scope& scope, const NameString& name_string, Reference* reference);
BAN::ErrorOr<Scope> add_named_object(ParseContext& context, const NameString& name_string, Node&& node);
BAN::ErrorOr<Scope> add_alias(ParseContext& scope, const NameString& name_string, Reference* reference);
BAN::ErrorOr<void> remove_named_object(const Scope& absolute_path);
@ -43,6 +44,8 @@ namespace Kernel::ACPI::AML
};
BAN::ErrorOr<FindResult> find_named_object(const Scope& scope, const NameString& name_string, bool force_absolute = false);
BAN::ErrorOr<Scope> find_reference_scope(const Reference* reference);
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*)>&);

View File

@ -150,6 +150,27 @@ namespace Kernel::ACPI::AML
Node(Node&& other) { *this = BAN::move(other); }
Node& operator=(Node&&);
static BAN::ErrorOr<Node> create_string(BAN::StringView string)
{
const auto* u8_data = reinterpret_cast<const uint8_t*>(string.data());
auto result = TRY(create_buffer({ u8_data, string.size() }));
result.type = Node::Type::String;
return result;
}
static BAN::ErrorOr<Node> create_buffer(BAN::ConstByteSpan buffer)
{
Node node;
node.type = Node::Type::Buffer;
node.as.str_buf = static_cast<Buffer*>(kmalloc(sizeof(Buffer) + buffer.size()));
if (node.as.str_buf == nullptr)
return BAN::Error::from_errno(ENOMEM);
node.as.str_buf->ref_count = 1;
node.as.str_buf->size = buffer.size();
memcpy(node.as.str_buf->bytes, buffer.data(), buffer.size());
return node;
}
enum class Type
{
Uninitialized,
@ -285,6 +306,10 @@ namespace Kernel::ACPI::AML
// If method has no return, it will return <integer 0>
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method, BAN::Array<Reference*, 7>&& args, uint32_t call_depth = 0);
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method,
Node&& arg0 = {}, Node&& arg1 = {}, Node&& arg2 = {}, Node&& arg3 = {}, Node&& arg4 = {}, Node&& arg5 = {}, Node&& arg6 = {});
BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists);
}

View File

@ -23,6 +23,8 @@ namespace Kernel
virtual void broadcast_ipi() override;
virtual void enable() override;
BAN::ErrorOr<uint8_t> reserve_gsi(uint32_t gsi);
void initialize_timer();
private:

View File

@ -43,7 +43,7 @@ namespace Kernel
class TCPSocket final : public NetworkSocket
{
public:
static constexpr size_t m_tcp_options_bytes = 4;
static constexpr size_t m_tcp_options_bytes = 8;
public:
static BAN::ErrorOr<BAN::RefPtr<TCPSocket>> create(NetworkLayer&, const Info&);
@ -91,6 +91,7 @@ namespace Kernel
bool has_ghost_byte { false };
uint32_t data_size { 0 }; // number of bytes in this buffer
uint8_t scale { 1 }; // window scale
BAN::UniqPtr<VirtualRange> buffer;
};

View File

@ -2,6 +2,7 @@
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/Memory/Types.h>
#include <kernel/Storage/StorageController.h>
@ -119,6 +120,9 @@ namespace Kernel::PCI
void enable_pin_interrupts();
void disable_pin_interrupts();
BAN::ErrorOr<uint8_t> route_prt_entry(const ACPI::AML::Node& routing_entry);
BAN::ErrorOr<uint8_t> find_intx_interrupt();
private:
bool m_is_valid { false };
uint8_t m_bus { 0 };

View File

@ -182,6 +182,12 @@ namespace Kernel
BAN::ErrorOr<long> sys_sigpending(sigset_t* set);
BAN::ErrorOr<long> sys_sigprocmask(int how, const sigset_t* set, sigset_t* oset);
BAN::ErrorOr<long> sys_yield();
BAN::ErrorOr<long> sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg);
BAN::ErrorOr<long> sys_pthread_exit(void* value);
BAN::ErrorOr<long> sys_pthread_join(pthread_t thread, void** value);
BAN::ErrorOr<long> sys_pthread_self();
BAN::ErrorOr<long> sys_tcgetpgrp(int fd);
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
@ -290,6 +296,14 @@ namespace Kernel
BAN::Vector<Thread*> m_threads;
struct pthread_info_t
{
pthread_t thread;
void* value;
};
BAN::Vector<pthread_info_t> m_exited_pthreads;
ThreadBlocker m_pthread_exit_blocker;
uint64_t m_alarm_interval_ns { 0 };
uint64_t m_alarm_wake_time_ns { 0 };

View File

@ -38,6 +38,8 @@ namespace Kernel
static BAN::ErrorOr<Thread*> create_userspace(Process*, PageTable&);
~Thread();
BAN::ErrorOr<Thread*> pthread_create(entry_t, void*);
BAN::ErrorOr<Thread*> clone(Process*, uintptr_t sp, uintptr_t ip);
void setup_exec();
void setup_process_cleanup();
@ -98,6 +100,8 @@ namespace Kernel
private:
Thread(pid_t tid, Process*);
void setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
static void on_exit_trampoline(Thread*);
void on_exit();

View File

@ -626,6 +626,127 @@ acpi_release_global_lock:
return result;
}
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wstack-usage="
#endif
BAN::ErrorOr<void> ACPI::route_interrupt_link_device(const AML::Scope& device, uint64_t& routed_irq_mask)
{
ASSERT(m_namespace);
auto prs_node = TRY(AML::convert_node(TRY(m_namespace->evaluate(device, "_PRS"_sv)), AML::ConvBuffer, -1));
auto prs_span = BAN::ConstByteSpan(prs_node.as.str_buf->bytes, prs_node.as.str_buf->size);
auto [srs_path, srs_node] = TRY(m_namespace->find_named_object(device, TRY(AML::NameString::from_string("_SRS"_sv))));
if (srs_node == nullptr || srs_node->node.type != AML::Node::Type::Method)
{
dwarnln("interrupt link device does not have _SRS method");
return BAN::Error::from_errno(EINVAL);
}
while (!prs_span.empty())
{
if (!(prs_span[0] & 0x80))
{
const uint8_t name = (prs_span[0] >> 3) & 0x0F;
const uint8_t length = prs_span[0] & 0x07;
if (prs_span.size() < static_cast<size_t>(1 + length))
return BAN::Error::from_errno(EINVAL);
if (name == 0x04)
{
if (length < 2)
return BAN::Error::from_errno(EINVAL);
const uint16_t irq_mask = prs_span[1] | (prs_span[2] << 8);
for (uint8_t pass = 0; pass < 2; pass++)
{
for (uint8_t irq = 0; irq < 16; irq++)
{
if (!(irq_mask & (1 << irq)))
continue;
if (pass == 0 && (routed_irq_mask & (static_cast<uint64_t>(1) << irq)))
continue;
BAN::Array<uint8_t, 4> setting;
setting[0] = 0x22 | (length > 2); // small, irq, data len
setting[1] = (1 << irq) >> 0; // irq low
setting[2] = (1 << irq) >> 8; // irq high
if (length > 2)
setting[3] = prs_span[3]; // flags
auto setting_span = BAN::ConstByteSpan(setting.data(), (length > 2) ? 4 : 3);
TRY(AML::method_call(srs_path, srs_node->node, TRY(AML::Node::create_buffer(setting_span))));
dprintln("routed {} -> irq {}", device, irq);
routed_irq_mask |= static_cast<uint64_t>(1) << irq;
return {};
}
}
}
prs_span = prs_span.slice(1 + length);
}
else
{
if (prs_span.size() < 3)
return BAN::Error::from_errno(EINVAL);
const uint8_t name = prs_span[0] & 0x7F;
const uint16_t length = (prs_span[2] << 8) | prs_span[1];
if (prs_span.size() < static_cast<size_t>(3 + length))
return BAN::Error::from_errno(EINVAL);
// Extended Interrupt Descriptor
if (name == 0x09)
{
const uint8_t irq_count = prs_span[4];
if (irq_count == 0 || length < 2 + 4*irq_count)
return BAN::Error::from_errno(EINVAL);
for (uint8_t pass = 0; pass < 2; pass++)
{
for (uint32_t i = 0; i < irq_count; i++)
{
// TODO: support irq over 64 irqs?
if (prs_span[6 + 4*i] || prs_span[7 + 4*i] || prs_span[8 + 4*i])
continue;
const uint8_t irq = prs_span[5 + 4*i];
if (irq >= 64)
continue;
if (pass == 0 && (routed_irq_mask & (static_cast<uint64_t>(1) << irq)))
continue;
BAN::Array<uint8_t, 9> setting;
setting[0] = 0x89; // large, irq
setting[1] = 0x06; // data len
setting[2] = 0x00;
setting[3] = prs_span[3]; // flags
setting[4] = 0x01; // table size
setting[5] = irq; // irq
setting[6] = 0x00;
setting[7] = 0x00;
setting[8] = 0x00;
TRY(AML::method_call(srs_path, srs_node->node, TRY(AML::Node::create_buffer(setting.span()))));
dprintln("routed {} -> irq {}", device, irq);
routed_irq_mask |= static_cast<uint64_t>(1) << irq;
return {};
}
}
}
prs_span = prs_span.slice(3 + length);
}
}
dwarnln("No routable interrupt found in _PRS");
return {};
}
#pragma GCC diagnostic pop
BAN::ErrorOr<void> ACPI::enter_acpi_mode(uint8_t mode)
{
ASSERT(!m_namespace);
@ -769,6 +890,16 @@ acpi_release_global_lock:
dprintln("Initialized ACPI interrupts");
if (auto interrupt_link_devices_or_error = m_namespace->find_device_with_eisa_id("PNP0C0F"_sv); !interrupt_link_devices_or_error.is_error())
{
uint64_t routed_irq_mask = 0;
auto interrupt_link_devices = interrupt_link_devices_or_error.release_value();
for (const auto& device : interrupt_link_devices)
if (auto ret = route_interrupt_link_device(device, routed_irq_mask); ret.is_error())
dwarnln("failed to route interrupt link device: {}", ret.error());
dprintln("Routed interrupt link devices");
}
return {};
}
@ -819,7 +950,7 @@ acpi_release_global_lock:
auto index = i * 8 + (pending & ~(pending - 1));
if (m_gpe_methods[index])
if (auto ret = AML::method_call(m_gpe_scope, m_gpe_methods[index]->node, {}); ret.is_error())
if (auto ret = AML::method_call(m_gpe_scope, m_gpe_methods[index]->node, BAN::Array<AML::Reference*, 7>{}); ret.is_error())
dwarnln("Failed to evaluate _GPE {}: ", index, ret.error());
handled_event = true;

View File

@ -53,9 +53,11 @@ namespace Kernel::ACPI::AML
const auto add_predefined_root_namespace =
[](const char* name) -> BAN::ErrorOr<void>
{
ParseContext dummy_context {};
Node predefined {};
predefined.type = Node::Type::PredefinedScope;
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string(name)), BAN::move(predefined)));
TRY(s_root_namespace.add_named_object(dummy_context, TRY(NameString::from_string(name)), BAN::move(predefined)));
return {};
};
@ -66,11 +68,13 @@ namespace Kernel::ACPI::AML
TRY(add_predefined_root_namespace("\\_SI_"));
TRY(add_predefined_root_namespace("\\_TZ_"));
ParseContext dummy_context {};
{
Node revision;
revision.type = Node::Type::Integer;
revision.as.integer.value = 2;
TRY(s_root_namespace.add_named_object({}, TRY(NameString::from_string("_REV")), BAN::move(revision)));
TRY(s_root_namespace.add_named_object(dummy_context, TRY(NameString::from_string("_REV")), BAN::move(revision)));
}
{
@ -107,7 +111,25 @@ namespace Kernel::ACPI::AML
return result;
};
TRY(s_root_namespace.add_named_object({}, osi_string, BAN::move(method)));
TRY(s_root_namespace.add_named_object(dummy_context, osi_string, BAN::move(method)));
}
{
auto os_string = TRY(NameString::from_string("\\_OS_"));
Node os_node = TRY(Node::create_string("Microsoft Windows NT"_sv));
TRY(s_root_namespace.add_named_object(dummy_context, os_string, BAN::move(os_node)));
}
{
auto rev_string = TRY(NameString::from_string("\\_REV"));
Node rev_node {};
rev_node.type = Node::Type::Integer;
rev_node.as.integer.value = 2;
TRY(s_root_namespace.add_named_object(dummy_context, rev_string, BAN::move(rev_node)));
}
{
@ -120,7 +142,7 @@ namespace Kernel::ACPI::AML
mutex.as.mutex->sync_level = 0;
mutex.as.mutex->global_lock = true;
TRY(s_root_namespace.add_named_object({}, gl_string, BAN::move(mutex)));
TRY(s_root_namespace.add_named_object(dummy_context, gl_string, BAN::move(mutex)));
}
return {};
@ -318,11 +340,11 @@ namespace Kernel::ACPI::AML
return resolved_path;
}
BAN::ErrorOr<Scope> Namespace::add_named_object(const Scope& scope, const NameString& name_string, Node&& node)
BAN::ErrorOr<Scope> Namespace::add_named_object(ParseContext& context, const NameString& name_string, Node&& node)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", scope, name_string, node);
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_named_object('{}', '{}', {})", context.scope, name_string, node);
auto resolved_path = TRY(resolve_path(scope, name_string));
auto resolved_path = TRY(resolve_path(context.scope, name_string));
if (m_named_objects.contains(resolved_path))
return Scope();
@ -337,14 +359,16 @@ namespace Kernel::ACPI::AML
if (m_has_initialized_namespace && reference->node.type == Node::Type::OpRegion)
(void)opregion_call_reg(resolved_path, reference->node);
TRY(context.created_nodes.push_back(TRY(resolved_path.copy())));
return resolved_path;
}
BAN::ErrorOr<Scope> Namespace::add_alias(const Scope& scope, const NameString& name_string, Reference* reference)
BAN::ErrorOr<Scope> Namespace::add_alias(ParseContext& context, const NameString& name_string, Reference* reference)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_alias('{}', '{}', {})", scope, name_string, reference->node);
dprintln_if(AML_DUMP_FUNCTION_CALLS, "add_alias('{}', '{}', {})", context.scope, name_string, reference->node);
auto resolved_path = TRY(resolve_path(scope, name_string));
auto resolved_path = TRY(resolve_path(context.scope, name_string));
if (m_named_objects.contains(resolved_path))
return Scope();
@ -354,6 +378,8 @@ namespace Kernel::ACPI::AML
TRY(m_named_objects.insert(TRY(resolved_path.copy()), reference));
TRY(m_aliases.insert(TRY(resolved_path.copy())));
TRY(context.created_nodes.push_back(TRY(resolved_path.copy())));
return resolved_path;
}
@ -428,6 +454,18 @@ namespace Kernel::ACPI::AML
};
}
BAN::ErrorOr<Scope> Namespace::find_reference_scope(const Reference* reference)
{
for (const auto& [obj_path, obj_ref] : m_named_objects)
{
if (obj_ref != reference)
continue;
return obj_path.copy();
}
return BAN::Error::from_errno(ENOENT);
}
BAN::ErrorOr<void> Namespace::for_each_child(const Scope& scope, const BAN::Function<BAN::Iteration(BAN::StringView, Reference*)>& callback)
{
for (const auto& [obj_path, obj_ref] : m_named_objects)
@ -547,11 +585,10 @@ namespace Kernel::ACPI::AML
return result;
}
BAN::ErrorOr<Node> Namespace::evaluate(BAN::StringView path)
BAN::ErrorOr<Node> Namespace::evaluate(const Scope& scope, BAN::StringView path)
{
Scope root_scope;
auto name_string = TRY(NameString::from_string(path));
auto [object_path, object] = TRY(find_named_object(root_scope, name_string));
auto [object_path, object] = TRY(find_named_object(scope, name_string));
if (object == nullptr)
return BAN::Error::from_errno(ENOENT);
return evaluate_node(object_path, object->node);

View File

@ -210,7 +210,7 @@ namespace Kernel::ACPI::AML
return result;
}
static BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists)
BAN::ErrorOr<void> resolve_package_element(Package::Element& element, bool error_if_not_exists)
{
if (element.resolved)
{
@ -1167,17 +1167,18 @@ namespace Kernel::ACPI::AML
}
}
auto buffer_node = TRY(parse_node(context));
if (buffer_node.type != Node::Type::Buffer)
auto buffer_node_ref = TRY(parse_node(context, true));
auto* buffer_node = &buffer_node_ref;
if (buffer_node->type == Node::Type::Reference)
buffer_node = &buffer_node->as.reference->node;
if (buffer_node->type != Node::Type::Buffer)
{
dwarnln("CreateField buffer is {}", buffer_node);
return BAN::Error::from_errno(EINVAL);
}
Node dummy_integer {};
dummy_integer.type = Node::Type::Integer;
dummy_integer.as.integer.value = 0;
auto index_node = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
if (bit_count == 0)
@ -1196,12 +1197,17 @@ namespace Kernel::ACPI::AML
Node buffer_field;
buffer_field.type = Node::Type::BufferField;
buffer_field.as.buffer_field.buffer = buffer_node.as.str_buf;
buffer_field.as.buffer_field.buffer = buffer_node->as.str_buf;
buffer_field.as.buffer_field.buffer->ref_count++;
buffer_field.as.buffer_field.bit_count = bit_count;
buffer_field.as.buffer_field.bit_offset = index_in_bits ? index_node.as.integer.value : index_node.as.integer.value * 8;
TRY(Namespace::root_namespace().add_named_object(context.scope, field_name_string, BAN::move(buffer_field)));
auto absolte_path = TRY(Namespace::root_namespace().add_named_object(context, field_name_string, BAN::move(buffer_field)));
if (absolte_path.parts.empty())
{
dwarnln("Could not add Buffer Field '{}'.'{}' to namespace", context.scope, field_name_string);
return {};
}
return {};
}
@ -1419,6 +1425,49 @@ namespace Kernel::ACPI::AML
return result;
}
static BAN::ErrorOr<Node> parse_find_set_bit_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_find_set_bit_op");
ASSERT(!context.aml_data.empty());
const auto opcode = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
uint64_t (*function)(uint64_t) = nullptr;
switch (static_cast<AML::Byte>(opcode))
{
case AML::Byte::FindSetLeftBitOp:
function = [](uint64_t a) -> uint64_t {
for (int i = 63; i >= 0; i--)
if ((a >> i) & 1)
return i + 1;
return 0;
};
break;
case AML::Byte::FindSetRightBitOp:
function = [](uint64_t a) -> uint64_t {
for (int i = 0; i < 64; i++)
if ((a >> i) & 1)
return i + 1;
return 0;
};
break;
default:
ASSERT_NOT_REACHED();
}
auto value = TRY(convert_node(TRY(parse_node(context)), ConvInteger, sizeof(uint64_t)));
Node result {};
result.type = Node::Type::Integer;
result.as.integer.value = function(value.as.integer.value);
TRY(store_into_target(context, result));
return result;
}
static BAN::ErrorOr<Node> parse_mid_op(ParseContext& context)
{
dprintln_if(AML_DUMP_FUNCTION_CALLS, "parse_mid_op");
@ -1695,7 +1744,7 @@ namespace Kernel::ACPI::AML
return {};
}
TRY(Namespace::root_namespace().add_alias(context.scope, object_name_string, source_ref));
TRY(Namespace::root_namespace().add_alias(context, object_name_string, source_ref));
return {};
}
@ -1711,9 +1760,12 @@ namespace Kernel::ACPI::AML
auto name_string = TRY(parse_name_string(context.aml_data));
auto object = TRY(parse_node(context));
auto path = TRY(Namespace::root_namespace().add_named_object(context.scope, name_string, BAN::move(object)));
if (!path.parts.empty())
TRY(context.created_nodes.push_back(BAN::move(path)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, name_string, BAN::move(object)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Name '{}'.'{}' to namespace", context.scope, name_string);
return {};
}
return {};
}
@ -1892,10 +1944,10 @@ namespace Kernel::ACPI::AML
event_node.type = Node::Type::Event;
event_node.as.event.signal_count = 0;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, event_name, BAN::move(event_node)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, event_name, BAN::move(event_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Device '{}'.'{}' to namespace", context.scope, event_name);
dwarnln("Could not add Event '{}'.'{}' to namespace", context.scope, event_name);
return {};
}
@ -2045,7 +2097,7 @@ namespace Kernel::ACPI::AML
Node device_node {};
device_node.type = Node::Type::Device;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, device_name, BAN::move(device_node)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, device_name, BAN::move(device_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Device '{}'.'{}' to namespace", context.scope, device_name);
@ -2076,7 +2128,7 @@ namespace Kernel::ACPI::AML
Node processor_node {};
processor_node.type = Node::Type::Processor;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, processor_name, BAN::move(processor_node)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, processor_name, BAN::move(processor_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Processor '{}'.'{}' to namespace", context.scope, processor_name);
@ -2107,10 +2159,10 @@ namespace Kernel::ACPI::AML
Node power_resource_node {};
power_resource_node.type = Node::Type::PowerResource;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, power_resource_name, BAN::move(power_resource_node)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, power_resource_name, BAN::move(power_resource_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Processor '{}'.'{}' to namespace", context.scope, power_resource_name);
dwarnln("Could not add Power Resource '{}'.'{}' to namespace", context.scope, power_resource_name);
return {};
}
@ -2133,7 +2185,7 @@ namespace Kernel::ACPI::AML
Node thermal_zone_node {};
thermal_zone_node.type = Node::Type::ThermalZone;
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context.scope, thermal_zone_name, BAN::move(thermal_zone_node)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, thermal_zone_name, BAN::move(thermal_zone_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Thermal Zone '{}'.'{}' to namespace", context.scope, thermal_zone_name);
@ -2173,7 +2225,13 @@ namespace Kernel::ACPI::AML
mutex.as.mutex->sync_level = mutex_flags;
mutex.as.mutex->global_lock = false;
TRY(Namespace::root_namespace().add_named_object(context.scope, mutex_name, BAN::move(mutex)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, mutex_name, BAN::move(mutex)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Mutex '{}'.'{}' to namespace", context.scope, mutex);
return {};
}
return {};
}
@ -2321,9 +2379,12 @@ namespace Kernel::ACPI::AML
method_node.as.method.mutex->ref_count = 1;
}
auto path = TRY(Namespace::root_namespace().add_named_object(context.scope, method_name, BAN::move(method_node)));
if (!path.parts.empty())
TRY(context.created_nodes.push_back(BAN::move(path)));
auto absolute_path = TRY(Namespace::root_namespace().add_named_object(context, method_name, BAN::move(method_node)));
if (absolute_path.parts.empty())
{
dwarnln("Could not add Method '{}'.'{}' to namespace", context.scope, method_name);
return {};
}
return {};
}
@ -2554,7 +2615,7 @@ namespace Kernel::ACPI::AML
case Node::Type::Method:
if (node.as.method.arg_count != 0)
return BAN::Error::from_errno(EFAULT);
return TRY(method_call(node_path, node, {}));
return TRY(method_call(node_path, node, BAN::Array<Reference*, 7>{}));
}
dwarnln("evaluate {}", node);
@ -2663,6 +2724,33 @@ namespace Kernel::ACPI::AML
return result;
}
BAN::ErrorOr<Node> method_call(const Scope& scope, const Node& method, Node&& arg0, Node&& arg1, Node&& arg2, Node&& arg3, Node&& arg4, Node&& arg5, Node&& arg6)
{
BAN::Array<Reference*, 7> args(nullptr);
#define INIT_ARGn(n) \
if (arg ## n.type != Node::Type::Uninitialized) \
{ \
args[n] = new Reference(); \
if (args[n] == nullptr) \
return BAN::Error::from_errno(ENOMEM); \
args[n]->ref_count = 1; \
args[n]->node = BAN::move(arg ## n); \
}
INIT_ARGn(0);
INIT_ARGn(1);
INIT_ARGn(2);
INIT_ARGn(3);
INIT_ARGn(4);
INIT_ARGn(5);
INIT_ARGn(6);
#undef INIT_ARGn
return method_call(scope, method, BAN::move(args));
}
BAN::ErrorOr<Node> parse_node(ParseContext& context, bool return_ref)
{
if (context.aml_data.empty())
@ -2771,6 +2859,9 @@ namespace Kernel::ACPI::AML
case AML::Byte::LNotOp:
case AML::Byte::LOrOp:
return TRY(parse_logical_op(context));
case AML::Byte::FindSetLeftBitOp:
case AML::Byte::FindSetRightBitOp:
return TRY(parse_find_set_bit_op(context));
case AML::Byte::Local0:
case AML::Byte::Local1:
case AML::Byte::Local2:

View File

@ -92,13 +92,13 @@ namespace Kernel::ACPI::AML
opregion.as.opregion.offset = region_offset.as.integer.value;
opregion.as.opregion.length = region_length.as.integer.value;
TRY(Namespace::root_namespace().add_named_object(context.scope, region_name, BAN::move(opregion)));
TRY(Namespace::root_namespace().add_named_object(context, region_name, BAN::move(opregion)));
return {};
}
template<typename F>
static BAN::ErrorOr<void> parse_field_list(const Scope& scope, BAN::ConstByteSpan field_list_pkg, const F& create_element, uint8_t field_flags)
static BAN::ErrorOr<void> parse_field_list(ParseContext& context, BAN::ConstByteSpan field_list_pkg, const F& create_element, uint8_t field_flags)
{
uint64_t offset = 0;
while (!field_list_pkg.empty())
@ -144,7 +144,7 @@ namespace Kernel::ACPI::AML
Node field_node = create_element(offset, field_length, field_flags);
TRY(Namespace::root_namespace().add_named_object(scope, field_name, BAN::move(field_node)));
TRY(Namespace::root_namespace().add_named_object(context, field_name, BAN::move(field_node)));
offset += field_length;
@ -197,7 +197,7 @@ namespace Kernel::ACPI::AML
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
TRY(parse_field_list(context, field_pkg, create_element, default_flags));
return {};
}
@ -260,7 +260,7 @@ namespace Kernel::ACPI::AML
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
TRY(parse_field_list(context, field_pkg, create_element, default_flags));
return {};
}
@ -331,7 +331,7 @@ namespace Kernel::ACPI::AML
return field_node;
};
TRY(parse_field_list(context.scope, field_pkg, create_element, default_flags));
TRY(parse_field_list(context, field_pkg, create_element, default_flags));
return {};
}

View File

@ -41,7 +41,7 @@ namespace Kernel::ACPI
if (method_ref == nullptr)
return BAN::Error::from_errno(EFAULT);
auto result = TRY(AML::method_call(method_path, method_ref->node, {}));
auto result = TRY(AML::method_call(method_path, method_ref->node, BAN::Array<AML::Reference*, 7>{}));
if (result.type != AML::Node::Type::Package || result.as.package->num_elements < m_result_index)
return BAN::Error::from_errno(EFAULT);

View File

@ -458,7 +458,7 @@ namespace Kernel
{
SpinLockGuard _(m_lock);
uint32_t gsi = m_irq_overrides[irq];
const uint32_t gsi = m_irq_overrides[irq];
{
int byte = gsi / 8;
@ -477,9 +477,11 @@ namespace Kernel
}
ASSERT(ioapic);
const uint32_t pin = gsi - ioapic->gsi_base;
RedirectionEntry redir;
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2);
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + gsi * 2 + 1);
redir.lo_dword = ioapic->read(IOAPIC_REDIRS + pin * 2);
redir.hi_dword = ioapic->read(IOAPIC_REDIRS + pin * 2 + 1);
ASSERT(redir.mask); // TODO: handle overlapping interrupts
redir.vector = IRQ_VECTOR_BASE + irq;
@ -487,8 +489,8 @@ namespace Kernel
// FIXME: distribute IRQs more evenly?
redir.destination = Kernel::Processor::bsb_id().as_u32();
ioapic->write(IOAPIC_REDIRS + gsi * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + gsi * 2 + 1, redir.hi_dword);
ioapic->write(IOAPIC_REDIRS + pin * 2, redir.lo_dword);
ioapic->write(IOAPIC_REDIRS + pin * 2 + 1, redir.hi_dword);
}
bool APIC::is_in_service(uint8_t irq)
@ -504,35 +506,59 @@ namespace Kernel
{
SpinLockGuard _(m_lock);
uint32_t gsi = m_irq_overrides[irq];
const uint32_t gsi = m_irq_overrides[irq];
IOAPIC* ioapic = nullptr;
for (IOAPIC& io : m_io_apics)
bool found_ioapic = false;
for (const auto& io : m_io_apics)
{
if (io.gsi_base <= gsi && gsi <= io.gsi_base + io.max_redirs)
{
ioapic = &io;
found_ioapic = true;
break;
}
}
if (!ioapic)
if (!found_ioapic)
{
dwarnln("Cannot enable irq {} for APIC", irq);
return BAN::Error::from_errno(EFAULT);
dwarnln("No IOAPIC for GSI {}", gsi);
return BAN::Error::from_errno(EINVAL);
}
int byte = gsi / 8;
int bit = gsi % 8;
if (m_reserved_gsis[byte] & (1 << bit))
{
dwarnln("irq {} is already reserved", irq);
dwarnln("GSI {} is already reserved", gsi);
return BAN::Error::from_errno(EFAULT);
}
m_reserved_gsis[byte] |= 1 << bit;
return {};
}
// FIXME: rewrite gsi and vector reserving
// this is a hack to allow direct GSI reservation
BAN::ErrorOr<uint8_t> APIC::reserve_gsi(uint32_t gsi)
{
dwarnln("TRYING TO RESERVE GSI {}", gsi);
size_t irq = 0;
for (; irq < 0x100; irq++)
if (m_irq_overrides[irq] == gsi)
break;
if (irq == 0x100)
{
dwarnln("TODO: reserve GSI not accessible through overrides");
return BAN::Error::from_errno(ENOTSUP);
}
dwarnln(" matches IRQ {}", irq);
TRY(reserve_irq(irq));
return irq;
}
BAN::Optional<uint8_t> APIC::get_free_irq()
{
SpinLockGuard _(m_lock);

View File

@ -21,9 +21,10 @@ namespace Kernel
{
auto ms_since_boot = SystemTimer::get().ms_since_boot();
SpinLockGuard _(Debug::s_debug_lock);
BAN::Formatter::print(Debug::putchar, "[{5}.{3}] {}: ",
BAN::Formatter::print(Debug::putchar, "[{5}.{3}] {} {}: ",
ms_since_boot / 1000,
ms_since_boot % 1000,
Kernel::Process::current().pid(),
Kernel::Process::current().name()
);
for (size_t i = 0; i < buffer.size(); i++)

View File

@ -354,6 +354,8 @@ namespace Kernel
asm volatile("cli; 1: hlt; jmp 1b");
}
Thread::current().save_sse();
ASSERT(InterruptController::get().is_in_service(IRQ_TIMER - IRQ_VECTOR_BASE));
InterruptController::get().eoi(IRQ_TIMER - IRQ_VECTOR_BASE);
@ -365,6 +367,8 @@ namespace Kernel
auto& current_thread = Thread::current();
if (current_thread.can_add_signal_to_execute())
current_thread.handle_signal();
Thread::current().load_sse();
}
extern "C" void cpp_irq_handler(uint32_t irq)

View File

@ -240,12 +240,21 @@ namespace Kernel::Input
BAN::ErrorOr<void> PS2Controller::initialize_impl(uint8_t scancode_set)
{
// Determine if the PS/2 Controller Exists
auto* fadt = static_cast<const ACPI::FADT*>(ACPI::ACPI::get().get_header("FACP"_sv, 0));
if (fadt && fadt->revision > 1 && !(fadt->iapc_boot_arch & (1 << 1)))
constexpr size_t iapc_flag_off = offsetof(ACPI::FADT, iapc_boot_arch);
constexpr size_t iapc_flag_end = iapc_flag_off + sizeof(ACPI::FADT::iapc_boot_arch);
// If user provided scan code set, skip FADT detection
if (scancode_set == 0xFF)
scancode_set = 0;
else if (scancode_set == 0)
{
dwarnln_if(DEBUG_PS2, "No PS/2 available");
return BAN::Error::from_errno(ENODEV);
// Determine if the PS/2 Controller Exists
auto* fadt = static_cast<const ACPI::FADT*>(ACPI::ACPI::get().get_header("FACP"_sv, 0));
if (fadt && fadt->revision >= 3 && fadt->length >= iapc_flag_end && !(fadt->iapc_boot_arch & (1 << 1)))
{
dwarnln_if(DEBUG_PS2, "No PS/2 available");
return BAN::Error::from_errno(ENODEV);
}
}
// Disable Devices

View File

@ -108,21 +108,13 @@ namespace Kernel::Input
m_byte_index = 0;
auto button_index_to_button =
[](int index) -> MouseButton
{
if (index == 0)
return MouseButton::Left;
if (index == 1)
return MouseButton::Right;
if (index == 2)
return MouseButton::Middle;
if (index == 3)
return MouseButton::Extra1;
if (index == 4)
return MouseButton::Extra2;
ASSERT_NOT_REACHED();
};
constexpr MouseButton bit_to_button[] {
MouseButton::Left,
MouseButton::Right,
MouseButton::Middle,
MouseButton::Extra1,
MouseButton::Extra2,
};
if (new_button_mask != m_button_mask)
{
@ -133,7 +125,7 @@ namespace Kernel::Input
MouseEvent event;
event.type = MouseEventType::MouseButtonEvent;
event.button_event.button = button_index_to_button(i);
event.button_event.button = bit_to_button[i];
event.button_event.pressed = !!(new_button_mask & (1 << i));
add_event(BAN::ConstByteSpan::from(event));
}

View File

@ -106,10 +106,10 @@ namespace Kernel
ASSERT(&PageTable::current() == &m_page_table);
ASSERT(&m_page_table != &page_table);
auto result = TRY(create_to_vaddr(page_table, m_vaddr, m_size, m_flags, m_preallocated));
SpinLockGuard _(m_lock);
auto result = TRY(create_to_vaddr(page_table, m_vaddr, m_size, m_flags, m_preallocated));
const size_t page_count = m_size / PAGE_SIZE;
for (size_t i = 0; i < page_count; i++)
{

View File

@ -18,8 +18,8 @@ namespace Kernel
WindowScale = 0x03,
};
static constexpr size_t s_window_buffer_size = 15 * PAGE_SIZE;
static_assert(s_window_buffer_size <= UINT16_MAX);
static constexpr size_t s_recv_window_buffer_size = 16 * PAGE_SIZE;
static constexpr size_t s_send_window_buffer_size = 16 * PAGE_SIZE;
BAN::ErrorOr<BAN::RefPtr<TCPSocket>> TCPSocket::create(NetworkLayer& network_layer, const Info& info)
{
@ -28,15 +28,16 @@ namespace Kernel
PageTable::kernel(),
KERNEL_OFFSET,
~(vaddr_t)0,
s_window_buffer_size,
s_recv_window_buffer_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
socket->m_recv_window.scale = 12; // use PAGE_SIZE windows
socket->m_send_window.buffer = TRY(VirtualRange::create_to_vaddr_range(
PageTable::kernel(),
KERNEL_OFFSET,
~(vaddr_t)0,
s_window_buffer_size,
s_send_window_buffer_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
@ -73,7 +74,7 @@ namespace Kernel
while (m_pending_connections.empty())
{
LockFreeGuard _(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
TRY(Thread::current().block_or_eintr_or_timeout_ms(m_thread_blocker, 100, false));
}
auto connection = m_pending_connections.front();
@ -205,7 +206,7 @@ namespace Kernel
if (m_state != State::Established)
return return_with_maybe_zero();
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
TRY(Thread::current().block_or_eintr_or_timeout_ms(m_thread_blocker, 100, false));
}
const uint32_t to_recv = BAN::Math::min<uint32_t>(buffer.size(), m_recv_window.data_size);
@ -247,7 +248,7 @@ namespace Kernel
if (m_send_window.data_size + message.size() <= m_send_window.buffer->size())
break;
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
TRY(Thread::current().block_or_eintr_or_timeout_ms(m_thread_blocker, 100, false));
}
{
@ -264,7 +265,7 @@ namespace Kernel
if (m_state != State::Established)
return return_with_maybe_zero();
LockFreeGuard free(m_mutex);
TRY(Thread::current().block_or_eintr_indefinite(m_thread_blocker));
TRY(Thread::current().block_or_eintr_or_timeout_ms(m_thread_blocker, 100, false));
}
return message.size();
@ -376,18 +377,18 @@ namespace Kernel
header.seq_number = m_send_window.current_seq + m_send_window.has_ghost_byte;
header.ack_number = m_recv_window.start_seq + m_recv_window.data_size + m_recv_window.has_ghost_byte;
header.data_offset = (sizeof(TCPHeader) + m_tcp_options_bytes) / sizeof(uint32_t);
header.window_size = m_recv_window.buffer->size();
header.window_size = BAN::Math::min<size_t>(0xFFFF, m_recv_window.buffer->size() >> m_recv_window.scale);
header.flags = m_next_flags;
if (header.flags & FIN)
m_send_window.has_ghost_byte = true;
m_next_flags = 0;
ASSERT(m_recv_window.buffer->size() < (1 << (8 * sizeof(header.window_size))));
if (m_state == State::Closed)
{
add_tcp_header_option<0, TCPOption::MaximumSeqmentSize>(header, m_interface->payload_mtu() - m_network_layer.header_size());
add_tcp_header_option<4, TCPOption::WindowScale>(header, 0);
add_tcp_header_option<4, TCPOption::WindowScale>(header, m_recv_window.scale);
header.window_size = BAN::Math::min<size_t>(0xFFFF, m_recv_window.buffer->size());
m_send_window.mss = 1440;
m_send_window.start_seq++;
m_send_window.current_seq = m_send_window.start_seq;
@ -463,6 +464,8 @@ namespace Kernel
m_send_window.mss = *options.maximum_seqment_size;
if (options.window_scale.has_value())
m_send_window.scale = *options.window_scale;
else
m_recv_window.scale = 1;
m_send_window.start_seq = m_send_window.current_seq;
m_send_window.current_ack = m_send_window.current_seq;

View File

@ -1,3 +1,6 @@
#include <BAN/ScopeGuard.h>
#include <kernel/APIC.h>
#include <kernel/ACPI/ACPI.h>
#include <kernel/IDT.h>
#include <kernel/IO.h>
@ -379,7 +382,7 @@ namespace Kernel::PCI
uint8_t PCI::Device::get_interrupt(uint8_t index) const
{
ASSERT(m_offset_msi.has_value() || m_offset_msi_x.has_value() || !InterruptController::get().is_using_apic());
ASSERT(m_interrupt_mechanism != InterruptMechanism::NONE);
ASSERT(index < m_reserved_interrupt_count);
uint8_t count_found = 0;
@ -418,7 +421,8 @@ namespace Kernel::PCI
ASSERT_NOT_REACHED();
case InterruptMechanism::PIN:
enable_pin_interrupts();
write_byte(PCI_REG_IRQ_LINE, irq);
if (!InterruptController::get().is_using_apic())
write_byte(PCI_REG_IRQ_LINE, irq);
InterruptController::get().enable_irq(irq);
break;
case InterruptMechanism::MSI:
@ -476,13 +480,229 @@ namespace Kernel::PCI
}
}
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wstack-usage="
#endif
BAN::ErrorOr<uint8_t> PCI::Device::route_prt_entry(const ACPI::AML::Node& prt_entry)
{
ASSERT(prt_entry.type == ACPI::AML::Node::Type::Package);
ASSERT(prt_entry.as.package->num_elements == 4);
for (size_t i = 0; i < 4; i++)
ASSERT(prt_entry.as.package->elements[i].value.node);
auto& prt_entry_fields = prt_entry.as.package->elements;
auto& source_node = *prt_entry_fields[2].value.node;
if (source_node.type != ACPI::AML::Node::Type::Reference || source_node.as.reference->node.type != ACPI::AML::Node::Type::Device)
{
BAN::ScopeGuard debug_guard([] { dwarnln("unknown or invalid _PRT format"); });
const auto source_value = TRY(ACPI::AML::convert_node(TRY(source_node.copy()), ACPI::AML::ConvInteger, -1)).as.integer.value;
if (source_value != 0x00)
return BAN::Error::from_errno(EINVAL);
auto& gsi_node = *prt_entry_fields[3].value.node;
const auto gsi_value = TRY(ACPI::AML::convert_node(TRY(gsi_node.copy()), ACPI::AML::ConvInteger, -1)).as.integer.value;
debug_guard.disable();
auto& apic = static_cast<APIC&>(InterruptController::get());
return TRY(apic.reserve_gsi(gsi_value));
}
else
{
BAN::ScopeGuard debug_guard([] { dwarnln("unknown or invalid _PRT format"); });
auto& acpi_namespace = *ACPI::ACPI::get().acpi_namespace();
auto source_scope = TRY(acpi_namespace.find_reference_scope(source_node.as.reference));
auto crs_node = TRY(ACPI::AML::convert_node(TRY(acpi_namespace.evaluate(source_scope, "_CRS"_sv)), ACPI::AML::ConvBuffer, -1));
auto crs_buffer = BAN::ConstByteSpan(crs_node.as.str_buf->bytes, crs_node.as.str_buf->size);
while (!crs_buffer.empty())
{
if (!(crs_buffer[0] & 0x80))
{
const uint8_t name = ((crs_buffer[0] >> 3) & 0x0F);
const uint8_t length = (crs_buffer[0] & 0x07);
if (crs_buffer.size() < static_cast<size_t>(1 + length))
return BAN::Error::from_errno(EINVAL);
// IRQ Format Descriptor
if (name == 0x04)
{
if (length < 2)
return BAN::Error::from_errno(EINVAL);
const uint16_t irq_mask = crs_buffer[1] | (crs_buffer[2] << 8);
if (irq_mask == 0)
return BAN::Error::from_errno(EINVAL);
debug_guard.disable();
uint8_t irq;
for (irq = 0; irq < 16; irq++)
if (irq_mask & (1 << irq))
break;
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
{
dwarnln("FIXME: irq sharing");
return ret.release_error();
}
return irq;
}
crs_buffer = crs_buffer.slice(1 + length);
}
else
{
if (crs_buffer.size() < 3)
return BAN::Error::from_errno(EINVAL);
const uint8_t name = (crs_buffer[0] & 0x7F);
const uint16_t length = (crs_buffer[2] << 8) | crs_buffer[1];
if (crs_buffer.size() < static_cast<size_t>(3 + length))
return BAN::Error::from_errno(EINVAL);
// Extended Interrupt Descriptor
if (name == 0x09)
{
if (length < 6 || crs_buffer[4] != 1)
return BAN::Error::from_errno(EINVAL);
const uint32_t irq =
(static_cast<uint32_t>(crs_buffer[5]) << 0) |
(static_cast<uint32_t>(crs_buffer[6]) << 8) |
(static_cast<uint32_t>(crs_buffer[7]) << 16) |
(static_cast<uint32_t>(crs_buffer[8]) << 24);
debug_guard.disable();
if (auto ret = InterruptController::get().reserve_irq(irq); ret.is_error())
{
dwarnln("FIXME: irq sharing");
return ret.release_error();
}
return irq;
}
crs_buffer = crs_buffer.slice(3 + length);
}
}
}
return BAN::Error::from_errno(EFAULT);
}
#pragma GCC diagnostic pop
static BAN::ErrorOr<ACPI::AML::Scope> find_pci_bus(uint16_t seg, uint8_t bus)
{
constexpr BAN::StringView pci_root_bus_ids[] {
"PNP0A03"_sv, // PCI
"PNP0A08"_sv, // PCIe
};
ASSERT(ACPI::ACPI::get().acpi_namespace());
auto& acpi_namespace = *ACPI::ACPI::get().acpi_namespace();
for (const auto eisa_id : pci_root_bus_ids)
{
auto root_buses = TRY(acpi_namespace.find_device_with_eisa_id(eisa_id));
for (const auto& root_bus : root_buses)
{
uint64_t bbn_value = 0;
if (auto bbn_node_or_error = acpi_namespace.evaluate(root_bus, "_BBN"_sv); !bbn_node_or_error.is_error())
bbn_value = TRY(ACPI::AML::convert_node(bbn_node_or_error.release_value(), ACPI::AML::ConvInteger, -1)).as.integer.value;
uint64_t seg_value = 0;
if (auto seg_node_or_error = acpi_namespace.evaluate(root_bus, "_SEG"_sv); !seg_node_or_error.is_error())
seg_value = TRY(ACPI::AML::convert_node(seg_node_or_error.release_value(), ACPI::AML::ConvInteger, -1)).as.integer.value;
if (seg_value == seg && bbn_value == bus)
return TRY(root_bus.copy());
}
}
return BAN::Error::from_errno(ENOENT);
}
#pragma GCC diagnostic push
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic ignored "-Wstack-usage="
#endif
// TODO: maybe move this code to ACPI related file?
BAN::ErrorOr<uint8_t> PCI::Device::find_intx_interrupt()
{
ASSERT(InterruptController::get().is_using_apic());
const uint32_t acpi_device_id = (static_cast<uint32_t>(m_dev) << 16) | 0xFFFF;
const uint8_t acpi_pin = read_byte(0x3D) - 1;
if (acpi_pin > 0x03)
{
dwarnln("PCI device is not using PIN interrupts");
return BAN::Error::from_errno(EINVAL);
}
if (ACPI::ACPI::get().acpi_namespace() == nullptr)
return BAN::Error::from_errno(EFAULT);
auto& acpi_namespace = *ACPI::ACPI::get().acpi_namespace();
// FIXME: support segments
auto pci_root_bus = TRY(find_pci_bus(0, m_bus));
auto prt_node = TRY(acpi_namespace.evaluate(pci_root_bus, "_PRT"));
if (prt_node.type != ACPI::AML::Node::Type::Package)
{
dwarnln("{}\\_PRT did not evaluate to package");
return BAN::Error::from_errno(EINVAL);
}
for (size_t i = 0; i < prt_node.as.package->num_elements; i++)
{
if (ACPI::AML::resolve_package_element(prt_node.as.package->elements[i], true).is_error())
continue;
auto& prt_entry = *prt_node.as.package->elements[i].value.node;
if (prt_entry.type != ACPI::AML::Node::Type::Package)
continue;
if (prt_entry.as.package->num_elements != 4)
continue;
bool resolved = true;
for (size_t j = 0; j < 4 && resolved; j++)
if (ACPI::AML::resolve_package_element(prt_entry.as.package->elements[j], true).is_error())
resolved = false;
if (!resolved)
continue;
auto& prt_entry_fields = prt_entry.as.package->elements;
if (TRY(ACPI::AML::convert_node(TRY(prt_entry_fields[0].value.node->copy()), ACPI::AML::ConvInteger, -1)).as.integer.value != acpi_device_id)
return BAN::Error::from_errno(ENOENT);
if (TRY(ACPI::AML::convert_node(TRY(prt_entry_fields[1].value.node->copy()), ACPI::AML::ConvInteger, -1)).as.integer.value != acpi_pin)
return BAN::Error::from_errno(ENOENT);
auto ret = route_prt_entry(prt_entry);
if (!ret.is_error())
return ret;
}
dwarnln("No routable PCI interrupt found");
return BAN::Error::from_errno(EFAULT);
}
#pragma GCC diagnostic pop
BAN::ErrorOr<void> PCI::Device::reserve_interrupts(uint8_t count)
{
// FIXME: Allow "late" interrupt reserving
ASSERT(m_reserved_interrupt_count == 0);
const auto mechanism =
[&]() -> InterruptMechanism
[this, count]() -> InterruptMechanism
{
if (!InterruptController::get().is_using_apic())
{
@ -494,34 +714,46 @@ namespace Kernel::PCI
return InterruptMechanism::NONE;
}
if (m_offset_msi_x.has_value())
const bool is_xhci = false && m_class_code == 0x0C && m_subclass == 0x03 && m_prog_if == 0x30;
if (!is_xhci && m_offset_msi_x.has_value())
{
const uint16_t msg_ctrl = read_word(*m_offset_msi_x + 0x02);
if (count <= (msg_ctrl & 0x7FF) + 1)
return InterruptMechanism::MSIX;
}
if (m_offset_msi.has_value())
if (!is_xhci && m_offset_msi.has_value())
{
if (count == 1)
return InterruptMechanism::MSI;
// FIXME: support multiple message
// FIXME: support multiple MSIs
}
// FIXME: support ioapic
if (count == 1)
return InterruptMechanism::PIN;
return InterruptMechanism::NONE;
}();
if (mechanism == InterruptMechanism::NONE)
{
dwarnln("No supported interrupt mechanism available");
return BAN::Error::from_errno(ENOTSUP);
}
auto get_interrupt_func =
[mechanism]() -> BAN::Optional<uint8_t>
[this, mechanism]() -> BAN::Optional<uint8_t>
{
switch (mechanism)
{
case InterruptMechanism::NONE:
return {};
ASSERT_NOT_REACHED();
case InterruptMechanism::PIN:
return InterruptController::get().get_free_irq();
if (!InterruptController::get().is_using_apic())
return InterruptController::get().get_free_irq();
if (auto ret = find_intx_interrupt(); !ret.is_error())
return ret.release_value();
return {};
case InterruptMechanism::MSI:
case InterruptMechanism::MSIX:
return PCIManager::get().reserve_msi();
@ -534,7 +766,7 @@ namespace Kernel::PCI
const auto irq = get_interrupt_func();
if (!irq.has_value())
{
dwarnln("Could not reserve {} MSI(-X) interrupts", count);
dwarnln("Could not reserve {} interrupts", count);
return BAN::Error::from_errno(EFAULT);
}
const uint8_t byte = irq.value() / 8;

View File

@ -185,6 +185,8 @@ namespace Kernel
process->m_userspace_info.envp = nullptr;
auto* thread = MUST(Thread::create_userspace(process, process->page_table()));
thread->setup_exec();
process->add_thread(thread);
process->register_to_scheduler();
return process;
@ -208,6 +210,7 @@ namespace Kernel
Process::~Process()
{
ASSERT(m_threads.empty());
ASSERT(m_exited_pthreads.empty());
ASSERT(m_mapped_regions.empty());
ASSERT(!m_page_table);
}
@ -238,6 +241,8 @@ namespace Kernel
}
}
m_exited_pthreads.clear();
ProcFileSystem::get().on_process_delete(*this);
m_process_lock.lock();
@ -2079,6 +2084,107 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_yield()
{
Processor::yield();
return 0;
}
BAN::ErrorOr<long> Process::sys_pthread_create(const pthread_attr_t* __restrict attr, void (*entry)(void*), void* arg)
{
if (attr != nullptr)
{
dwarnln("pthread attr not supported");
return BAN::Error::from_errno(ENOTSUP);
}
LockGuard _(m_process_lock);
auto* new_thread = TRY(Thread::current().pthread_create(entry, arg));
MUST(m_threads.push_back(new_thread));
MUST(Processor::scheduler().add_thread(new_thread));
return new_thread->tid();
}
BAN::ErrorOr<long> Process::sys_pthread_exit(void* value)
{
LockGuard _(m_process_lock);
// main thread cannot call pthread_exit
if (&Thread::current() == m_threads.front())
return BAN::Error::from_errno(EINVAL);
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
for (auto* thread : m_threads)
{
if (thread != &Thread::current())
continue;
m_pthread_exit_blocker.unblock();
m_process_lock.unlock();
thread->on_exit();
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_pthread_join(pthread_t thread, void** value)
{
LockGuard _(m_process_lock);
if (value)
TRY(validate_pointer_access(value, sizeof(void*), true));
if (thread == Thread::current().tid())
return BAN::Error::from_errno(EINVAL);
const auto wait_thread =
[&]() -> bool
{
for (size_t i = 0; i < m_exited_pthreads.size(); i++)
{
if (m_exited_pthreads[i].thread != thread)
continue;
if (value)
*value = m_exited_pthreads[i].value;
m_exited_pthreads.remove(i);
return true;
}
return false;
};
if (wait_thread())
return 0;
{
bool found = false;
for (auto* _thread : m_threads)
if (_thread->tid() == thread)
found = true;
if (!found)
return BAN::Error::from_errno(EINVAL);
}
for (;;)
{
{
LockFreeGuard _(m_process_lock);
m_pthread_exit_blocker.block_with_timeout_ms(100);
}
if (wait_thread())
return 0;
}
}
BAN::ErrorOr<long> Process::sys_pthread_self()
{
return Thread::current().tid();
}
BAN::ErrorOr<long> Process::sys_tcgetpgrp(int fd)
{
LockGuard _(m_process_lock);

View File

@ -34,6 +34,11 @@ namespace Kernel
return Thread::current().userspace_stack_top() - 4 * sizeof(uintptr_t);
}
extern "C" void load_thread_sse()
{
Thread::current().load_sse();
}
static pid_t s_next_tid = 1;
alignas(16) static uint8_t s_default_sse_storage[512];
@ -103,7 +108,7 @@ namespace Kernel
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table,
0x300000, KERNEL_OFFSET,
0x200000, KERNEL_OFFSET,
kernel_stack_size,
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
@ -111,14 +116,12 @@ namespace Kernel
thread->m_userspace_stack = TRY(VirtualRange::create_to_vaddr_range(
page_table,
0x300000, KERNEL_OFFSET,
0x200000, KERNEL_OFFSET,
userspace_stack_size,
PageTable::Flags::UserSupervisor | PageTable::Flags::ReadWrite | PageTable::Flags::Present,
true
));
thread->setup_exec();
thread_deleter.disable();
return thread;
@ -166,6 +169,22 @@ namespace Kernel
}
}
BAN::ErrorOr<Thread*> Thread::pthread_create(entry_t entry, void* arg)
{
auto* thread = TRY(create_userspace(m_process, m_process->page_table()));
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
thread->setup_exec_impl(
reinterpret_cast<uintptr_t>(entry),
reinterpret_cast<uintptr_t>(arg),
0, 0, 0
);
return thread;
}
BAN::ErrorOr<Thread*> Thread::clone(Process* new_process, uintptr_t sp, uintptr_t ip)
{
ASSERT(m_is_userspace);
@ -189,6 +208,9 @@ namespace Kernel
thread->m_interrupt_stack.sp = sp;
thread->m_interrupt_stack.ss = 0x10;
save_sse();
memcpy(thread->m_sse_storage, m_sse_storage, sizeof(m_sse_storage));
#if ARCH(x86_64)
thread->m_interrupt_registers.rax = 0;
#elif ARCH(i686)
@ -201,23 +223,34 @@ namespace Kernel
}
void Thread::setup_exec()
{
const auto& userspace_info = process().userspace_info();
ASSERT(userspace_info.entry);
setup_exec_impl(
userspace_info.entry,
userspace_info.argc,
reinterpret_cast<uintptr_t>(userspace_info.argv),
reinterpret_cast<uintptr_t>(userspace_info.envp),
userspace_info.file_fd
);
}
void Thread::setup_exec_impl(uintptr_t entry, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
{
ASSERT(is_userspace());
m_state = State::NotStarted;
// Signal mask is inherited
const auto& userspace_info = process().userspace_info();
ASSERT(userspace_info.entry);
// Initialize stack for returning
PageTable::with_fast_page(process().page_table().physical_address_of(kernel_stack_top() - PAGE_SIZE), [&] {
uintptr_t sp = PageTable::fast_page() + PAGE_SIZE;
write_to_stack(sp, userspace_info.entry);
write_to_stack(sp, userspace_info.file_fd);
write_to_stack(sp, userspace_info.envp);
write_to_stack(sp, userspace_info.argv);
write_to_stack(sp, userspace_info.argc);
write_to_stack(sp, entry);
write_to_stack(sp, arg3);
write_to_stack(sp, arg2);
write_to_stack(sp, arg1);
write_to_stack(sp, arg0);
});
m_interrupt_stack.ip = reinterpret_cast<vaddr_t>(start_userspace_thread);

View File

@ -91,9 +91,14 @@ static void parse_command_line()
g_disable_debug = true;
else if (argument.starts_with("ps2="))
{
if (argument.size() != 5 || !isdigit(argument[4]))
dprintln("Invalid ps2= command line argument format '{}'", argument);
cmdline.ps2_override = argument[4] - '0';
if (argument == "ps2=auto"_sv)
cmdline.ps2_override = 0xFF;
else
{
if (argument.size() != 5 || !isdigit(argument[4]))
dprintln("Invalid ps2= command line argument format '{}'", argument);
cmdline.ps2_override = argument[4] - '0';
}
}
else if (argument.size() > 5 && argument.substring(0, 5) == "root=")
cmdline.root = argument.substring(5);

View File

@ -17,8 +17,10 @@ set(LIBC_SOURCES
netdb.cpp
poll.cpp
printf_impl.cpp
pthread.cpp
pwd.cpp
scanf_impl.cpp
sched.cpp
setjmp.cpp
signal.cpp
stdio.cpp

View File

@ -70,16 +70,16 @@ __BEGIN_DECLS
#endif
#undef __need_pthread_rwlockattr_t
#if !defined(__pthread_spinlock_t_defined) && (defined(__need_all_types) || defined(__need_pthread_spinlock_t))
#define __pthread_spinlock_t_defined 1
typedef int pthread_spinlock_t;
#endif
#undef __need_pthread_spinlock_t
#if !defined(__pthread_t_defined) && (defined(__need_all_types) || defined(__need_pthread_t))
#define __pthread_t_defined 1
typedef int pthread_t;
typedef pid_t pthread_t;
#endif
#undef __need_pthread_t
#if !defined(__pthread_spinlock_t_defined) && (defined(__need_all_types) || defined(__need_pthread_spinlock_t))
#define __pthread_spinlock_t_defined 1
typedef pthread_t pthread_spinlock_t;
#endif
#undef __need_pthread_spinlock_t
__END_DECLS

View File

@ -26,6 +26,7 @@ __BEGIN_DECLS
#define IPV6_V6ONLY 7
#define INADDR_ANY 0
#define INADDR_NONE 0xFFFFFFFF
#define INADDR_BROADCAST 0xFFFFFFFF
#define INADDR_LOOPBACK 0x7F000001

View File

@ -90,6 +90,11 @@ __BEGIN_DECLS
O(SYS_FSYNC, fsync) \
O(SYS_SYMLINKAT, symlinkat) \
O(SYS_HARDLINKAT, hardlinkat) \
O(SYS_YIELD, yield) \
O(SYS_PTHREAD_CREATE, pthread_create) \
O(SYS_PTHREAD_EXIT, pthread_exit) \
O(SYS_PTHREAD_JOIN, pthread_join) \
O(SYS_PTHREAD_SELF, pthread_self) \
enum Syscall
{

View File

@ -132,6 +132,10 @@ __BEGIN_DECLS
#endif
#undef __need_off_t
#ifdef __need_pthread_t
#define __need_pid_t
#endif
#if !defined(__pid_t_defined) && (defined(__need_all_types) || defined(__need_pid_t))
#define __pid_t_defined 1
typedef int pid_t;
@ -185,6 +189,14 @@ __BEGIN_DECLS
#ifdef __need_all_types
#include <stdint.h>
typedef short bits16_t;
typedef unsigned short u_bits16_t;
typedef int bits32_t;
typedef unsigned int u_bits32_t;
typedef char* bits64_t;
typedef unsigned int u_int;
typedef unsigned long u_long;
#endif
#undef __need_all_types

View File

@ -0,0 +1,125 @@
#include <BAN/Assert.h>
#include <BAN/Atomic.h>
#include <BAN/PlacementNew.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
struct pthread_trampoline_info_t
{
void* (*start_routine)(void*);
void* arg;
};
// stack is 16 byte aligned on entry, this `call` is used to align it
extern "C" void pthread_trampoline(void*);
asm("pthread_trampoline: call pthread_trampoline_cpp");
extern "C" void pthread_trampoline_cpp(void* arg)
{
pthread_trampoline_info_t info;
memcpy(&info, arg, sizeof(pthread_trampoline_info_t));
free(arg);
pthread_exit(info.start_routine(info.arg));
ASSERT_NOT_REACHED();
}
int pthread_create(pthread_t* __restrict thread, const pthread_attr_t* __restrict attr, void* (*start_routine)(void*), void* __restrict arg)
{
auto* info = static_cast<pthread_trampoline_info_t*>(malloc(sizeof(pthread_trampoline_info_t)));
if (info == nullptr)
return -1;
info->start_routine = start_routine;
info->arg = arg;
const auto ret = syscall(SYS_PTHREAD_CREATE, attr, pthread_trampoline, info);
if (ret == -1)
{
free(info);
return -1;
}
if (thread)
*thread = ret;
return 0;
}
void pthread_exit(void* value_ptr)
{
syscall(SYS_PTHREAD_EXIT, value_ptr);
ASSERT_NOT_REACHED();
}
int pthread_join(pthread_t thread, void** value_ptr)
{
return syscall(SYS_PTHREAD_JOIN, thread, value_ptr);
}
pthread_t pthread_self(void)
{
return syscall(SYS_PTHREAD_SELF);
}
static inline BAN::Atomic<pthread_t>& pthread_spin_get_atomic(pthread_spinlock_t* lock)
{
static_assert(sizeof(pthread_spinlock_t) <= sizeof(BAN::Atomic<pthread_t>));
static_assert(alignof(pthread_spinlock_t) <= alignof(BAN::Atomic<pthread_t>));
return *reinterpret_cast<BAN::Atomic<pthread_t>*>(lock);
}
int pthread_spin_destroy(pthread_spinlock_t* lock)
{
pthread_spin_get_atomic(lock).~Atomic<pthread_t>();
return 0;
}
int pthread_spin_init(pthread_spinlock_t* lock, int pshared)
{
(void)pshared;
new (lock) BAN::Atomic<pthread_t>();
pthread_spin_get_atomic(lock) = false;
return 0;
}
int pthread_spin_lock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
const pthread_t tid = pthread_self();
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) != tid);
pthread_t expected = 0;
while (!atomic.compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acquire))
{
sched_yield();
expected = 0;
}
return 0;
}
int pthread_spin_trylock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
const pthread_t tid = pthread_self();
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) != tid);
pthread_t expected = 0;
if (atomic.compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acquire))
return 0;
return EBUSY;
}
int pthread_spin_unlock(pthread_spinlock_t* lock)
{
auto& atomic = pthread_spin_get_atomic(lock);
ASSERT(atomic.load(BAN::MemoryOrder::memory_order_relaxed) == pthread_self());
atomic.store(0, BAN::MemoryOrder::memory_order_release);
return 0;
}

View File

@ -0,0 +1,8 @@
#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
int sched_yield(void)
{
return syscall(SYS_YIELD);
}

View File

@ -1,10 +1,12 @@
#include <BAN/Assert.h>
#include <BAN/Debug.h>
#include <BAN/Atomic.h>
#include <BAN/Math.h>
#include <BAN/PlacementNew.h>
#include <bits/printf.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <scanf_impl.h>
#include <stdarg.h>
#include <stdio.h>
@ -31,7 +33,14 @@ struct FILE
unsigned char unget_buffer[12];
uint32_t unget_buf_idx;
// TODO: use recursive pthread_mutex when implemented?
// this storage hack is to keep FILE pod (init order)
BAN::Atomic<pthread_t>& locker() { return *reinterpret_cast<BAN::Atomic<pthread_t>*>(locker_storage); }
unsigned char locker_storage[sizeof(pthread_t)];
uint32_t lock_depth;
};
static_assert(BAN::is_pod_v<FILE>);
struct ScopeLock
{
@ -88,8 +97,14 @@ static int drop_read_buffer(FILE* file)
void _init_stdio()
{
for (size_t i = 0; i < FOPEN_MAX; i++)
{
init_closed_file(&s_files[i]);
new (&s_files[i].locker()) BAN::Atomic<pthread_t>();
s_files[i].locker() = -1;
s_files[i].lock_depth = 0;
}
s_files[STDIN_FILENO].fd = STDIN_FILENO;
s_files[STDIN_FILENO].mode = O_RDONLY;
s_files[STDIN_FILENO].buffer_type = _IOLBF;
@ -170,7 +185,6 @@ FILE* fdopen(int fd, const char* mode_str)
return nullptr;
}
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);
@ -292,9 +306,20 @@ int fileno(FILE* fp)
return fp->fd;
}
void flockfile(FILE*)
void flockfile(FILE* fp)
{
// FIXME: when threads are implemented
const pthread_t tid = pthread_self();
pthread_t expected = -1;
while (!fp->locker().compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acq_rel))
{
if (expected == tid)
break;
sched_yield();
expected = -1;
}
fp->lock_depth++;
}
FILE* fopen(const char* pathname, const char* mode_str)
@ -310,7 +335,6 @@ FILE* fopen(const char* pathname, const char* mode_str)
if (fd == -1)
return nullptr;
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);
@ -458,15 +482,25 @@ off_t ftello(FILE* file)
return ret;
}
int ftrylockfile(FILE*)
int ftrylockfile(FILE* fp)
{
// FIXME: when threads are implemented
const pthread_t tid = pthread_self();
pthread_t expected = -1;
if (!fp->locker().compare_exchange(expected, tid, BAN::MemoryOrder::memory_order_acq_rel))
if (expected != tid)
return 1;
fp->lock_depth++;
return 0;
}
void funlockfile(FILE*)
void funlockfile(FILE* fp)
{
// FIXME: when threads are implemented
ASSERT(fp->locker() == pthread_self());
ASSERT(fp->lock_depth > 0);
if (--fp->lock_depth == 0)
fp->locker().store(-1, BAN::MemoryOrder::memory_order_release);
}
size_t fwrite(const void* buffer, size_t size, size_t nitems, FILE* file)
@ -558,8 +592,60 @@ int getchar_unlocked(void)
return getc_unlocked(stdin);
}
ssize_t getdelim(char** __restrict lineptr, size_t* __restrict n, int delimeter, FILE* __restrict stream)
{
if (n == nullptr || lineptr == nullptr)
{
errno = EINVAL;
return -1;
}
ScopeLock _(stream);
size_t capacity = *lineptr ? *n : 0;
size_t length = 0;
for (;;)
{
if (length + 2 > capacity)
{
const size_t new_capacity = BAN::Math::max(capacity * 2, length + 2);
void* temp = realloc(*lineptr, new_capacity);
if (temp == nullptr)
return -1;
*lineptr = static_cast<char*>(temp);
capacity = new_capacity;
}
int ch = getc_unlocked(stream);
if (ch == EOF)
{
(*lineptr)[length] = '\0';
*n = length;
if (ferror(stream) || length == 0)
return -1;
return length;
}
(*lineptr)[length++] = ch;
if (ch == delimeter)
{
(*lineptr)[length] = '\0';
*n = length;
return length;
}
}
}
ssize_t getline(char** __restrict lineptr, size_t* __restrict n, FILE* __restrict stream)
{
return getdelim(lineptr, n, '\n', stream);
}
char* gets(char* buffer)
{
ScopeLock _(stdin);
if (stdin->eof)
return nullptr;
@ -654,7 +740,6 @@ FILE* popen(const char* command, const char* mode_str)
close(read ? fds[1] : fds[0]);
// FIXME: when threads are implemented
for (int i = 0; i < FOPEN_MAX; i++)
{
ScopeLock _(&s_files[i]);

View File

@ -27,9 +27,15 @@ static uint32_t at_exit_funcs_count = 0;
void abort(void)
{
fflush(nullptr);
fprintf(stderr, "abort()\n");
exit(1);
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGABRT);
sigprocmask(SIG_UNBLOCK, &set, nullptr);
raise(SIGABRT);
signal(SIGABRT, SIG_DFL);
raise(SIGABRT);
ASSERT_NOT_REACHED();
}
@ -707,24 +713,26 @@ void* bsearch(const void* key, const void* base, size_t nel, size_t width, int (
if (nel == 0)
return nullptr;
const uint8_t* base_u8 = reinterpret_cast<const uint8_t*>(base);
const uint8_t* base_u8 = static_cast<const uint8_t*>(base);
size_t l = 0;
size_t r = nel - 1;
while (l <= r)
while (l < r)
{
const size_t mid = (l + r) / 2;
const size_t mid = l + (r - l) / 2;
int res = compar(key, base_u8 + mid * width);
if (res == 0)
return const_cast<uint8_t*>(base_u8 + mid * width);
if (res < 0)
r = mid - 1;
else
if (res > 0)
l = mid + 1;
else
r = mid ? mid - 1 : 0;
}
if (l < nel && compar(key, base_u8 + l * width) == 0)
return const_cast<uint8_t*>(base_u8 + l * width);
return nullptr;
}

View File

@ -0,0 +1,8 @@
set(SOURCES
main.cpp
)
add_executable(test-pthread ${SOURCES})
banan_link_library(test-pthread libc)
install(TARGETS test-pthread OPTIONAL)

View File

@ -0,0 +1,28 @@
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void* thread_func(void*)
{
printf("hello from thread\n");
return nullptr;
}
int main(int argc, char** argv)
{
pthread_t tid;
printf("creating thread\n");
if (pthread_create(&tid, nullptr, &thread_func, nullptr) == -1)
{
perror("pthread_create");
return 1;
}
sleep(1);
printf("exiting\n");
return 0;
}