Compare commits

..

1 Commits

Author SHA1 Message Date
3bdcd8f1fb Kernel: Rework kernel-side ELF loading
ELFs are now loaded as MemoryRegions so they don't need special handling
anywhere. This also allows file backed COW optimizations to work. This
was not the case before.

This patch removes now obsolete LoadableELF and unused ELF files from
LibElf.
2024-09-15 23:20:32 +03:00
1008 changed files with 19651 additions and 76476 deletions

13
.clangd
View File

@@ -1,13 +0,0 @@
Diagnostics:
Suppress: target_unsupported_type
CompileFlags:
Remove: [
-fstrict-volatile-bitfields,
-fno-tree-loop-distribute-patterns
]
Add: [
-D__banan_os__,
-D__arch__=x86_64,
-D__x86_64__
]

1
.gitignore vendored
View File

@@ -3,4 +3,3 @@
build/
base/
script/fakeroot-context
tools/update-image-perms

View File

@@ -2,9 +2,5 @@
"cmake.configureOnOpen": false,
"editor.tabSize": 4,
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"clangd.arguments": [
"--compile-commands-dir=${workspaceFolder}/build",
"-header-insertion=never"
]
}
"editor.detectIndentation": false
}

View File

@@ -11,8 +11,5 @@ banan_link_library(ban libc)
set_target_properties(ban PROPERTIES OUTPUT_NAME libban)
# set SONAME as cmake doesn't set it for some reason??
set_target_properties(ban PROPERTIES LINK_FLAGS "-Wl,-soname,libban.so")
banan_install_headers(ban)
install(TARGETS ban OPTIONAL)

View File

@@ -18,78 +18,78 @@ namespace BAN
using const_iterator = ConstIteratorSimple<T, Array>;
public:
constexpr Array() = default;
constexpr Array(const T&);
Array() = default;
Array(const T&);
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + size()); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + size()); }
constexpr const T& operator[](size_type) const;
constexpr T& operator[](size_type);
const T& operator[](size_type) const;
T& operator[](size_type);
constexpr const T& back() const;
constexpr T& back();
constexpr const T& front() const;
constexpr T& front();
const T& back() const;
T& back();
const T& front() const;
T& front();
Span<T> span() { return Span(m_data, size()); }
Span<const T> span() const { return Span(m_data, size()); }
const Span<T> span() const { return Span(m_data, size()); }
constexpr size_type size() const;
constexpr const T* data() const { return m_data; }
constexpr T* data() { return m_data; }
const T* data() const { return m_data; }
T* data() { return m_data; }
private:
T m_data[S] {};
};
template<typename T, size_t S>
constexpr Array<T, S>::Array(const T& value)
Array<T, S>::Array(const T& value)
{
for (size_type i = 0; i < S; i++)
m_data[i] = value;
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::operator[](size_type index) const
const T& Array<T, S>::operator[](size_type index) const
{
ASSERT(index < S);
return m_data[index];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::operator[](size_type index)
T& Array<T, S>::operator[](size_type index)
{
ASSERT(index < S);
return m_data[index];
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::back() const
const T& Array<T, S>::back() const
{
ASSERT(S != 0);
return m_data[S - 1];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::back()
T& Array<T, S>::back()
{
ASSERT(S != 0);
return m_data[S - 1];
}
template<typename T, size_t S>
constexpr const T& Array<T, S>::front() const
const T& Array<T, S>::front() const
{
ASSERT(S != 0);
return m_data[0];
}
template<typename T, size_t S>
constexpr T& Array<T, S>::front()
T& Array<T, S>::front()
{
ASSERT(S != 0);
return m_data[0];

View File

@@ -1,7 +1,5 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN
{
@@ -15,36 +13,8 @@ namespace BAN
memory_order_seq_cst = __ATOMIC_SEQ_CST,
};
template<typename T> concept atomic_c = is_integral_v<T> || is_pointer_v<T>;
template<typename T> concept atomic_lockfree_c = (is_integral_v<T> || is_pointer_v<T>) && __atomic_always_lock_free(sizeof(T), 0);
template<atomic_lockfree_c T, atomic_c U>
inline void atomic_store(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { __atomic_store_n(&obj, value, mem_order); }
template<atomic_lockfree_c T>
inline T atomic_load(T& obj, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_load_n(&obj, mem_order); }
template<atomic_lockfree_c T, atomic_c U>
inline T atomic_exchange(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_exchange_n(&obj, value, mem_order); }
template<atomic_lockfree_c T, atomic_lockfree_c U, atomic_c V>
inline bool atomic_compare_exchange(T& obj, U& expected, V value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_compare_exchange_n(&obj, &expected, value, false, mem_order, mem_order); }
#define DECL_ATOMIC_INLINE template<atomic_lockfree_c T, atomic_c U> inline
DECL_ATOMIC_INLINE T atomic_add_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_add_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_sub_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_sub_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_and_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_and_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_xor_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_xor_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_or_fetch (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_or_fetch (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_nand_fetch(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_nand_fetch(&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_add (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_add (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_sub (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_sub (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_and (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_and (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_xor (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_xor (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_or (T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_or (&obj, value, mem_order); }
DECL_ATOMIC_INLINE T atomic_fetch_nand(T& obj, U value, MemoryOrder mem_order = MemoryOrder::memory_order_seq_cst) { return __atomic_fetch_nand(&obj, value, mem_order); }
#undef DECL_ATOMIC_INLINE
template<atomic_lockfree_c T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
template<typename T, MemoryOrder MEM_ORDER = MemoryOrder::memory_order_seq_cst>
requires requires { __atomic_always_lock_free(sizeof(T), 0); }
class Atomic
{
Atomic(const Atomic&) = delete;
@@ -56,41 +26,41 @@ namespace BAN
constexpr Atomic() : m_value(0) {}
constexpr Atomic(T val) : m_value(val) {}
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return atomic_load(m_value, mem_order); }
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { atomic_store(m_value, val, mem_order); }
inline T load(MemoryOrder mem_order = MEM_ORDER) const volatile { return __atomic_load_n(&m_value, mem_order); }
inline void store(T val, MemoryOrder mem_order = MEM_ORDER) volatile { __atomic_store_n(&m_value, val, mem_order); }
inline T operator=(T val) volatile { store(val); return val; }
inline operator T() const volatile { return load(); }
inline T operator+=(T val) volatile { return atomic_add_fetch(m_value, val, MEM_ORDER); }
inline T operator-=(T val) volatile { return atomic_sub_fetch(m_value, val, MEM_ORDER); }
inline T operator&=(T val) volatile { return atomic_and_fetch(m_value, val, MEM_ORDER); }
inline T operator^=(T val) volatile { return atomic_xor_fetch(m_value, val, MEM_ORDER); }
inline T operator|=(T val) volatile { return atomic_or_fetch(m_value, val, MEM_ORDER); }
inline T operator+=(T val) volatile { return __atomic_add_fetch(&m_value, val, MEM_ORDER); }
inline T operator-=(T val) volatile { return __atomic_sub_fetch(&m_value, val, MEM_ORDER); }
inline T operator&=(T val) volatile { return __atomic_and_fetch(&m_value, val, MEM_ORDER); }
inline T operator^=(T val) volatile { return __atomic_xor_fetch(&m_value, val, MEM_ORDER); }
inline T operator|=(T val) volatile { return __atomic_or_fetch(&m_value, val, MEM_ORDER); }
inline T operator--() volatile { return atomic_sub_fetch(m_value, 1, MEM_ORDER); }
inline T operator++() volatile { return atomic_add_fetch(m_value, 1, MEM_ORDER); }
inline T operator--() volatile { return __atomic_sub_fetch(&m_value, 1, MEM_ORDER); }
inline T operator++() volatile { return __atomic_add_fetch(&m_value, 1, MEM_ORDER); }
inline T operator--(int) volatile { return atomic_fetch_sub(m_value, 1, MEM_ORDER); }
inline T operator++(int) volatile { return atomic_fetch_add(m_value, 1, MEM_ORDER); }
inline T operator--(int) volatile { return __atomic_fetch_sub(&m_value, 1, MEM_ORDER); }
inline T operator++(int) volatile { return __atomic_fetch_add(&m_value, 1, MEM_ORDER); }
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_compare_exchange(m_value, expected, desired, mem_order); }
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_exchange(m_value, desired, mem_order); };
inline bool compare_exchange(T& expected, T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_compare_exchange_n(&m_value, &expected, desired, false, mem_order, mem_order); }
inline T exchange(T desired, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_exchange_n(&m_value, desired, mem_order); };
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_add_fetch (m_value, val, mem_order); }
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_sub_fetch (m_value, val, mem_order); }
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_and_fetch (m_value, val, mem_order); }
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_xor_fetch (m_value, val, mem_order); }
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_or_fetch (m_value, val, mem_order); }
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_nand_fetch(m_value, val, mem_order); }
inline T add_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_add_fetch (&m_value, val, mem_order); }
inline T sub_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_sub_fetch (&m_value, val, mem_order); }
inline T and_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_and_fetch (&m_value, val, mem_order); }
inline T xor_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_xor_fetch (&m_value, val, mem_order); }
inline T or_fetch (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_or_fetch (&m_value, val, mem_order); }
inline T nand_fetch(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_nand_fetch(&m_value, val, mem_order); }
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_add (m_value, val, mem_order); }
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_sub (m_value, val, mem_order); }
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_and (m_value, val, mem_order); }
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_xor (m_value, val, mem_order); }
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_or (m_value, val, mem_order); }
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return atomic_fetch_nand(m_value, val, mem_order); }
inline T fetch_add (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_add (&m_value, val, mem_order); }
inline T fetch_sub (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_sub (&m_value, val, mem_order); }
inline T fetch_and (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_and (&m_value, val, mem_order); }
inline T fetch_xor (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch_xor (&m_value, val, mem_order); }
inline T fetch_or (T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_fetch__or (&m_value, val, mem_order); }
inline T fetch_nand(T val, MemoryOrder mem_order = MEM_ORDER) volatile { return __atomic_nfetch_and(&m_value, val, mem_order); }
private:
T m_value;

View File

@@ -21,56 +21,75 @@ namespace BAN
, m_size(size)
{ }
template<bool SRC_CONST>
ByteSpanGeneral(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
ByteSpanGeneral(ByteSpanGeneral& other)
: m_data(other.data())
, m_size(other.size())
{ }
template<bool SRC_CONST>
ByteSpanGeneral(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
ByteSpanGeneral(ByteSpanGeneral&& other)
: m_data(other.data())
, m_size(other.size())
{
other.clear();
other.m_data = nullptr;
other.m_size = 0;
}
template<typename T>
ByteSpanGeneral(const Span<T>& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
template<bool C2>
ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(CONST)
: m_data(other.data())
, m_size(other.size())
{ }
template<typename T>
ByteSpanGeneral(Span<T>&& other) requires(is_same_v<T, uint8_t> || (is_same_v<T, const uint8_t> && CONST))
template<bool C2>
ByteSpanGeneral(ByteSpanGeneral<C2>&& other) requires(CONST)
: m_data(other.data())
, m_size(other.size())
{
other.clear();
other.m_data = nullptr;
other.m_size = 0;
}
ByteSpanGeneral(Span<uint8_t> other)
: m_data(other.data())
, m_size(other.size())
{ }
ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
: m_data(other.data())
, m_size(other.size())
{ }
template<bool SRC_CONST>
ByteSpanGeneral& operator=(const ByteSpanGeneral<SRC_CONST>& other) requires(CONST || !SRC_CONST)
ByteSpanGeneral& operator=(ByteSpanGeneral other)
{
m_data = other.data();
m_size = other.size();
return *this;
}
template<bool SRC_CONST>
ByteSpanGeneral& operator=(ByteSpanGeneral<SRC_CONST>&& other) requires(CONST || !SRC_CONST)
template<bool C2>
ByteSpanGeneral& operator=(const ByteSpanGeneral<C2>& other) requires(CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(Span<uint8_t> other)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(const Span<const uint8_t>& other) requires(CONST)
{
m_data = other.data();
m_size = other.size();
other.clear();
return *this;
}
template<typename S>
static ByteSpanGeneral from(S& value) requires(CONST || !is_const_v<S>)
requires(CONST || !is_const_v<S>)
static ByteSpanGeneral from(S& value)
{
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
}
template<typename S>
S& as() const requires(!CONST || is_const_v<S>)
requires(!CONST && !is_const_v<S>)
S& as()
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
@@ -78,13 +97,30 @@ namespace BAN
}
template<typename S>
Span<S> as_span() const requires(!CONST || is_const_v<S>)
requires(is_const_v<S>)
S& as() const
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
requires(!CONST && !is_const_v<S>)
Span<S> as_span()
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
[[nodiscard]] ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1)) const
template<typename S>
const Span<S> as_span() const
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
{
ASSERT(m_data);
ASSERT(m_size >= offset);
@@ -94,23 +130,22 @@ namespace BAN
return ByteSpanGeneral(m_data + offset, length);
}
value_type& operator[](size_type offset) const
value_type& operator[](size_type offset)
{
ASSERT(offset < m_size);
return m_data[offset];
}
const value_type& operator[](size_type offset) const
{
ASSERT(offset < m_size);
return m_data[offset];
}
value_type* data() const { return m_data; }
value_type* data() { return m_data; }
const value_type* data() const { return m_data; }
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
void clear()
{
m_data = nullptr;
m_size = 0;
}
private:
value_type* m_data { nullptr };
size_type m_size { 0 };

View File

@@ -24,7 +24,7 @@ namespace BAN
void push(const T&);
void push(T&&);
template<typename... Args>
void emplace(Args&&... args) requires is_constructible_v<T, Args...>;
void emplace(Args&&... args);
void pop();
@@ -34,11 +34,6 @@ namespace BAN
const T& back() const;
T& back();
const T& operator[](size_t index) const;
T& operator[](size_t index);
void clear();
size_type size() const { return m_size; }
bool empty() const { return size() == 0; }
bool full() const { return size() == capacity(); }
@@ -58,7 +53,8 @@ namespace BAN
template<typename T, size_t S>
CircularQueue<T, S>::~CircularQueue()
{
clear();
for (size_type i = 0; i < m_size; i++)
element_at((m_first + i) % capacity())->~T();
}
template<typename T, size_t S>
@@ -75,7 +71,7 @@ namespace BAN
template<typename T, size_t S>
template<typename... Args>
void CircularQueue<T, S>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
void CircularQueue<T, S>::emplace(Args&&... args)
{
ASSERT(!full());
new (element_at(((m_first + m_size) % capacity()))) T(BAN::forward<Args>(args)...);
@@ -119,28 +115,6 @@ namespace BAN
return *element_at((m_first + m_size - 1) % capacity());
}
template<typename T, size_t S>
const T& CircularQueue<T, S>::operator[](size_t index) const
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
T& CircularQueue<T, S>::operator[](size_t index)
{
ASSERT(index < m_size);
return *element_at((m_first + index) % capacity());
}
template<typename T, size_t S>
void CircularQueue<T, S>::clear()
{
for (size_type i = 0; i < m_size; i++)
element_at((m_first + i) % capacity())->~T();
m_size = 0;
}
template<typename T, size_t S>
const T* CircularQueue<T, S>::element_at(size_type index) const
{

View File

@@ -9,35 +9,29 @@
#include <BAN/Formatter.h>
#include <stdio.h>
#define __debug_putchar [](int c) { putc_unlocked(c, stddbg); }
#define __debug_putchar [](int c) { putc(c, stddbg); }
#define dprintln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar,"\n"); \
BAN::Formatter::print(__debug_putchar,"\r\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while (false)
#define dwarnln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, "\e[33m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while(false)
#define derrorln(...) \
do { \
flockfile(stddbg); \
BAN::Formatter::print(__debug_putchar, "\e[31m"); \
BAN::Formatter::print(__debug_putchar, __VA_ARGS__); \
BAN::Formatter::print(__debug_putchar, "\e[m\n"); \
BAN::Formatter::print(__debug_putchar, "\e[m\r\n"); \
fflush(stddbg); \
funlockfile(stddbg); \
} while(false)
#define dprintln_if(cond, ...) \

View File

@@ -70,19 +70,15 @@ namespace BAN
template<integral T>
struct LittleEndian
{
constexpr LittleEndian()
: raw(0)
{ }
constexpr LittleEndian(T value)
: raw(host_to_little_endian(value))
{ }
{
raw = host_to_little_endian(value);
}
constexpr operator T() const
{
return host_to_little_endian(raw);
}
private:
T raw;
};
@@ -90,19 +86,15 @@ namespace BAN
template<integral T>
struct BigEndian
{
constexpr BigEndian()
: raw(0)
{ }
constexpr BigEndian(T value)
: raw(host_to_big_endian(value))
{ }
{
raw = host_to_big_endian(value);
}
constexpr operator T() const
{
return host_to_big_endian(raw);
}
private:
T raw;
};

View File

@@ -1,7 +1,6 @@
#pragma once
#include <BAN/Formatter.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Variant.h>
#include <errno.h>
@@ -10,16 +9,16 @@
#ifdef __is_kernel
#include <kernel/Panic.h>
#include <kernel/Errors.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
#define MUST(expr) ({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); e.release_value(); })
#define MUST_REF(expr) *({ auto&& e = expr; if (e.is_error()) Kernel::panic("{}", e.error()); &e.release_value(); })
#else
#include <BAN/Debug.h>
#define MUST(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } e.release_value(); })
#define MUST_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) { derrorln("MUST(" #__VA_ARGS__ "): {}", e.error()); __builtin_trap(); } &e.release_value(); })
#include <assert.h>
#define MUST(expr) ({ auto&& e = expr; assert(!e.is_error()); e.release_value(); })
#define MUST_REF(expr) *({ auto&& e = expr; assert(!e.is_error()); &e.release_value(); })
#endif
#define TRY(...) ({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); e.release_value(); })
#define TRY_REF(...) *({ auto&& e = (__VA_ARGS__); if (e.is_error()) return e.release_error(); &e.release_value(); })
#define TRY(expr) ({ auto&& e = expr; if (e.is_error()) return e.release_error(); e.release_value(); })
#define TRY_REF(expr) *({ auto&& e = expr; if (e.is_error()) return e.release_error(); &e.release_value(); })
namespace BAN
{
@@ -37,14 +36,7 @@ namespace BAN
{
return Error((uint64_t)error | kernel_error_mask);
}
#else
template<size_t N>
consteval static Error from_literal(const char (&message)[N])
{
return Error(message);
}
#endif
static Error from_errno(int error)
{
return Error(error);
@@ -62,15 +54,12 @@ namespace BAN
}
#endif
constexpr uint64_t get_error_code() const { return m_error_code; }
uint64_t get_error_code() const { return m_error_code; }
const char* get_message() const
{
#ifdef __is_kernel
if (m_error_code & kernel_error_mask)
return Kernel::error_string(kernel_error());
#else
if (m_message)
return m_message;
#endif
if (auto* desc = strerrordesc_np(m_error_code))
return desc;
@@ -78,27 +67,16 @@ namespace BAN
}
private:
constexpr Error(uint64_t error)
Error(uint64_t error)
: m_error_code(error)
{}
#ifndef __is_kernel
constexpr Error(const char* message)
: m_message(message)
{}
#endif
uint64_t m_error_code { 0 };
#ifndef __is_kernel
const char* m_message { nullptr };
#endif
uint64_t m_error_code;
};
template<typename T>
class [[nodiscard]] ErrorOr
{
BAN_NON_COPYABLE(ErrorOr);
public:
ErrorOr(const T& value)
: m_data(value)
@@ -112,14 +90,6 @@ namespace BAN
ErrorOr(Error&& error)
: m_data(move(error))
{}
ErrorOr(ErrorOr&& other)
: m_data(move(other.m_data))
{}
ErrorOr& operator=(ErrorOr&& other)
{
m_data = move(other.m_data);
return *this;
}
bool is_error() const { return m_data.template has<Error>(); }
const Error& error() const { return m_data.template get<Error>(); }

View File

@@ -10,11 +10,14 @@ namespace BAN::Formatter
struct ValueFormat;
template<typename F>
inline void print(F putc, const char* format);
template<typename F, typename Arg, typename... Args>
inline void print(F putc, const char* format, Arg&& arg, Args&&... args);
template<typename F, typename... Args>
concept PrintableArguments = requires(F putc, Args&&... args, const ValueFormat& format)
{
(print_argument(putc, BAN::forward<Args>(args), format), ...);
};
inline void println(F putc, const char* format, Args&&... args);
template<typename F, typename T>
inline void print_argument(F putc, T value, const ValueFormat& format);
@@ -50,7 +53,7 @@ namespace BAN::Formatter
}
}
template<typename F, typename Arg, typename... Args> requires PrintableArguments<F, Arg, Args...>
template<typename F, typename Arg, typename... Args>
inline void print(F putc, const char* format, Arg&& arg, Args&&... args)
{
while (*format && *format != '{')
@@ -204,14 +207,10 @@ 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

@@ -20,13 +20,13 @@ namespace BAN
new (m_storage) CallablePointer(function);
}
template<typename Own>
Function(Ret(Own::*function)(Args...), Own& owner)
Function(Ret(Own::*function)(Args...), Own* owner)
{
static_assert(sizeof(CallableMember<Own>) <= m_size);
new (m_storage) CallableMember<Own>(function, owner);
}
template<typename Own>
Function(Ret(Own::*function)(Args...) const, const Own& owner)
Function(Ret(Own::*function)(Args...) const, const Own* owner)
{
static_assert(sizeof(CallableMemberConst<Own>) <= m_size);
new (m_storage) CallableMemberConst<Own>(function, owner);
@@ -91,36 +91,36 @@ namespace BAN
template<typename Own>
struct CallableMember : public CallableBase
{
CallableMember(Ret(Own::*function)(Args...), Own& owner)
CallableMember(Ret(Own::*function)(Args...), Own* owner)
: m_owner(owner)
, m_function(function)
{ }
virtual Ret call(Args... args) const override
{
return (m_owner.*m_function)(forward<Args>(args)...);
return (m_owner->*m_function)(forward<Args>(args)...);
}
private:
Own& m_owner;
Own* m_owner = nullptr;
Ret(Own::*m_function)(Args...) = nullptr;
};
template<typename Own>
struct CallableMemberConst : public CallableBase
{
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own& owner)
CallableMemberConst(Ret(Own::*function)(Args...) const, const Own* owner)
: m_owner(owner)
, m_function(function)
{ }
virtual Ret call(Args... args) const override
{
return (m_owner.*m_function)(forward<Args>(args)...);
return (m_owner->*m_function)(forward<Args>(args)...);
}
private:
const Own& m_owner;
const Own* m_owner = nullptr;
Ret(Own::*m_function)(Args...) const = nullptr;
};

View File

@@ -1,261 +1,287 @@
#pragma once
#include <BAN/HashSet.h>
#include <BAN/Hash.h>
#include <BAN/LinkedList.h>
#include <BAN/Vector.h>
namespace BAN
{
template<typename HashSetIt, typename HashMap, typename Entry>
class HashMapIterator
{
public:
HashMapIterator() = default;
Entry& operator*()
{
return const_cast<Entry&>(m_iterator.operator*());
}
const Entry& operator*() const
{
return m_iterator.operator*();
}
Entry* operator->()
{
return const_cast<Entry*>(m_iterator.operator->());
}
const Entry* operator->() const
{
return m_iterator.operator->();
}
HashMapIterator& operator++()
{
++m_iterator;
return *this;
}
HashMapIterator operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
bool operator==(HashMapIterator other) const
{
return m_iterator == other.m_iterator;
}
bool operator!=(HashMapIterator other) const
{
return m_iterator != other.m_iterator;
}
private:
explicit HashMapIterator(HashSetIt it)
: m_iterator(it)
{ }
private:
HashSetIt m_iterator;
friend HashMap;
};
namespace detail
{
template<typename T, typename Key, typename HASH, typename COMP>
concept HashMapFindable = requires(const Key& a, const T& b) { COMP()(a, b); HASH()(b); };
}
template<typename Key, typename T, typename HASH = BAN::hash<Key>, typename COMP = BAN::equal<Key>>
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
class HashMap
{
public:
struct Entry
{
const Key key;
T value;
Entry() = delete;
Entry& operator=(const Entry&) = delete;
Entry& operator=(Entry&&) = delete;
Entry(const Entry& other)
: key(other.key)
, value(other.value)
{ }
Entry(Entry&& other)
: key(BAN::move(const_cast<Key&>(other.key)))
, value(BAN::move(other.value))
{ }
template<typename... Args>
Entry(Key&& key, Args&&... args)
: key(BAN::move(key))
, value(BAN::forward<Args>(args)...)
{ }
};
Entry(const Key& key, Args&&... args)
: key(key)
, value(forward<Args>(args)...)
{}
struct EntryHash
{
constexpr bool operator()(const Key& a)
{
return HASH()(a);
}
constexpr bool operator()(const Entry& a)
{
return HASH()(a.key);
}
};
struct EntryComp
{
constexpr bool operator()(const Entry& a, const Key& b)
{
return COMP()(a.key, b);
}
constexpr bool operator()(const Entry& a, const Entry& b)
{
return COMP()(a.key, b.key);
}
Key key;
T value;
};
public:
using size_type = size_t;
using key_type = Key;
using value_type = T;
using iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::iterator, HashMap, Entry>;
using const_iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::const_iterator, HashMap, const Entry>;
using size_type = size_t;
using key_type = Key;
using value_type = T;
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
public:
HashMap() = default;
~HashMap() { clear(); }
HashMap(const HashMap<Key, T, HASH>&);
HashMap(HashMap<Key, T, HASH>&&);
~HashMap();
HashMap(const HashMap& other) { *this = other; }
HashMap& operator=(const HashMap& other)
{
m_hash_set = other.m_hash_set;
return *this;
}
HashMap(HashMap&& other) { *this = BAN::move(other); }
HashMap& operator=(HashMap&& other)
{
m_hash_set = BAN::move(other.m_hash_set);
return *this;
}
iterator begin() { return iterator(m_hash_set.begin()); }
iterator end() { return iterator(m_hash_set.end()); }
const_iterator begin() const { return const_iterator(m_hash_set.begin()); }
const_iterator end() const { return const_iterator(m_hash_set.end()); }
ErrorOr<iterator> insert(const Key& key, const T& value) { return emplace(key, value); }
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
ErrorOr<iterator> insert(Key&& key, const T& value) { return emplace(move(key), value); }
ErrorOr<iterator> insert(Key&& key, T&& value) { return emplace(move(key), move(value)); }
ErrorOr<iterator> insert_or_assign(const Key& key, const T& value) { return emplace_or_assign(key, value); }
ErrorOr<iterator> insert_or_assign(const Key& key, T&& value) { return emplace_or_assign(key, move(value)); }
ErrorOr<iterator> insert_or_assign(Key&& key, const T& value) { return emplace_or_assign(move(key), value); }
ErrorOr<iterator> insert_or_assign(Key&& key, T&& value) { return emplace_or_assign(move(key), move(value)); }
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
ErrorOr<void> insert(const Key&, const T&);
ErrorOr<void> insert(const Key&, T&&);
template<typename... Args>
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace(Key(key), BAN::forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
ASSERT(!contains(key));
auto it = TRY(m_hash_set.insert(Entry { BAN::move(key), T(BAN::forward<Args>(args)...) }));
return iterator(it);
}
ErrorOr<void> emplace(const Key&, Args&&...);
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
{ return emplace_or_assign(Key(key), BAN::forward<Args>(args)...); }
template<typename... Args>
ErrorOr<iterator> emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
{
if (auto it = m_hash_set.find(key); it != m_hash_set.end())
{
const_cast<T&>(it->value) = T(BAN::forward<Args>(args)...);
return iterator(it);
}
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
auto it = TRY(m_hash_set.insert(Entry { BAN::move(key), T(BAN::forward<Args>(args)...) }));
return iterator(it);
}
ErrorOr<void> reserve(size_type);
template<detail::HashMapFindable<Key, HASH, COMP> U>
void remove(const U& key)
{
if (auto it = find(key); it != end())
remove(it);
}
void remove(const Key&);
void remove(iterator it);
void clear();
iterator remove(iterator it)
{
return iterator(m_hash_set.remove(it.m_iterator));
}
T& operator[](const Key&);
const T& operator[](const Key&) const;
template<detail::HashMapFindable<Key, HASH, COMP> U>
iterator find(const U& key)
{
return iterator(m_hash_set.find(key));
}
iterator find(const Key& key);
const_iterator find(const Key& key) const;
bool contains(const Key&) const;
template<detail::HashMapFindable<Key, HASH, COMP> U>
const_iterator find(const U& key) const
{
return const_iterator(m_hash_set.find(key));
}
void clear()
{
m_hash_set.clear();
}
ErrorOr<void> reserve(size_type size)
{
return m_hash_set.reserve(size);
}
template<detail::HashMapFindable<Key, HASH, COMP> U>
T& operator[](const U& key)
{
return find(key)->value;
}
template<detail::HashMapFindable<Key, HASH, COMP> U>
const T& operator[](const U& key) const
{
return find(key)->value;
}
template<detail::HashMapFindable<Key, HASH, COMP> U>
bool contains(const U& key) const
{
return find(key) != end();
}
size_type capacity() const
{
return m_hash_set.capacity();
}
size_type size() const
{
return m_hash_set.size();
}
bool empty() const
{
return m_hash_set.empty();
}
bool empty() const;
size_type size() const;
private:
HashSet<Entry, EntryHash, EntryComp> m_hash_set;
ErrorOr<void> rebucket(size_type);
LinkedList<Entry>& get_bucket(const Key&);
const LinkedList<Entry>& get_bucket(const Key&) const;
Vector<LinkedList<Entry>>::iterator get_bucket_iterator(const Key&);
Vector<LinkedList<Entry>>::const_iterator get_bucket_iterator(const Key&) const;
private:
Vector<LinkedList<Entry>> m_buckets;
size_type m_size = 0;
friend iterator;
};
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(const HashMap<Key, T, HASH>& other)
{
*this = other;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::HashMap(HashMap<Key, T, HASH>&& other)
{
*this = move(other);
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>::~HashMap()
{
clear();
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(const HashMap<Key, T, HASH>& other)
{
clear();
m_buckets = other.m_buckets;
m_size = other.m_size;
return *this;
}
template<typename Key, typename T, typename HASH>
HashMap<Key, T, HASH>& HashMap<Key, T, HASH>::operator=(HashMap<Key, T, HASH>&& other)
{
clear();
m_buckets = move(other.m_buckets);
m_size = other.m_size;
other.m_size = 0;
return *this;
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, const T& value)
{
return insert(key, move(T(value)));
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::insert(const Key& key, T&& value)
{
return emplace(key, move(value));
}
template<typename Key, typename T, typename HASH>
template<typename... Args>
ErrorOr<void> HashMap<Key, T, HASH>::emplace(const Key& key, Args&&... args)
{
ASSERT(!contains(key));
TRY(rebucket(m_size + 1));
auto& bucket = get_bucket(key);
TRY(bucket.emplace_back(key, forward<Args>(args)...));
m_size++;
return {};
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::reserve(size_type size)
{
TRY(rebucket(size));
return {};
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(const Key& key)
{
auto it = find(key);
if (it != end())
remove(it);
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::remove(iterator it)
{
it.outer_current()->remove(it.inner_current());
m_size--;
}
template<typename Key, typename T, typename HASH>
void HashMap<Key, T, HASH>::clear()
{
m_buckets.clear();
m_size = 0;
}
template<typename Key, typename T, typename HASH>
T& HashMap<Key, T, HASH>::operator[](const Key& key)
{
ASSERT(!empty());
auto& bucket = get_bucket(key);
for (Entry& entry : bucket)
if (entry.key == key)
return entry.value;
ASSERT(false);
}
template<typename Key, typename T, typename HASH>
const T& HashMap<Key, T, HASH>::operator[](const Key& key) const
{
ASSERT(!empty());
const auto& bucket = get_bucket(key);
for (const Entry& entry : bucket)
if (entry.key == key)
return entry.value;
ASSERT(false);
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::iterator HashMap<Key, T, HASH>::find(const Key& key)
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return iterator(m_buckets.end(), bucket_it, it);
return end();
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::const_iterator HashMap<Key, T, HASH>::find(const Key& key) const
{
if (empty())
return end();
auto bucket_it = get_bucket_iterator(key);
for (auto it = bucket_it->begin(); it != bucket_it->end(); it++)
if (it->key == key)
return const_iterator(m_buckets.end(), bucket_it, it);
return end();
}
template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::contains(const Key& key) const
{
return find(key) != end();
}
template<typename Key, typename T, typename HASH>
bool HashMap<Key, T, HASH>::empty() const
{
return m_size == 0;
}
template<typename Key, typename T, typename HASH>
typename HashMap<Key, T, HASH>::size_type HashMap<Key, T, HASH>::size() const
{
return m_size;
}
template<typename Key, typename T, typename HASH>
ErrorOr<void> HashMap<Key, T, HASH>::rebucket(size_type bucket_count)
{
if (m_buckets.size() >= bucket_count)
return {};
size_type new_bucket_count = BAN::Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<Entry>> new_buckets;
TRY(new_buckets.resize(new_bucket_count));
for (auto& bucket : m_buckets)
{
for (auto it = bucket.begin(); it != bucket.end();)
{
size_type new_bucket_index = HASH()(it->key) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
}
}
m_buckets = move(new_buckets);
return {};
}
template<typename Key, typename T, typename HASH>
LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key)
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
const LinkedList<typename HashMap<Key, T, HASH>::Entry>& HashMap<Key, T, HASH>::get_bucket(const Key& key) const
{
return *get_bucket_iterator(key);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key)
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
template<typename Key, typename T, typename HASH>
Vector<LinkedList<typename HashMap<Key, T, HASH>::Entry>>::const_iterator HashMap<Key, T, HASH>::get_bucket_iterator(const Key& key) const
{
ASSERT(!m_buckets.empty());
auto index = HASH()(key) % m_buckets.size();
return next(m_buckets.begin(), index);
}
}

View File

@@ -2,358 +2,198 @@
#include <BAN/Errors.h>
#include <BAN/Hash.h>
#include <BAN/Iterators.h>
#include <BAN/LinkedList.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/Vector.h>
namespace BAN
{
template<typename HashSet, typename Bucket, typename T>
class HashSetIterator
{
public:
HashSetIterator() = default;
const T& operator*() const
{
ASSERT(m_bucket);
return *m_bucket->element();
}
const T* operator->() const
{
ASSERT(m_bucket);
return m_bucket->element();
}
HashSetIterator& operator++()
{
ASSERT(m_bucket);
m_bucket++;
skip_to_valid_bucket();
return *this;
}
HashSetIterator operator++(int)
{
auto temp = *this;
++(*this);
return temp;
}
bool operator==(HashSetIterator other) const
{
return m_bucket == other.m_bucket;
}
bool operator!=(HashSetIterator other) const
{
return m_bucket != other.m_bucket;
}
private:
explicit HashSetIterator(Bucket* bucket)
: m_bucket(bucket)
{
if (m_bucket != nullptr)
skip_to_valid_bucket();
}
void skip_to_valid_bucket()
{
while (m_bucket->state != Bucket::USED && !m_bucket->end)
m_bucket++;
if (m_bucket->end)
m_bucket = nullptr;
}
private:
Bucket* m_bucket { nullptr };
friend HashSet;
};
namespace detail
{
template<typename T, typename U, typename HASH, typename COMP>
concept HashSetFindable = requires(const U& a, const T& b) { COMP()(a, b); HASH()(b); };
}
template<typename T, typename HASH = BAN::hash<T>, typename COMP = BAN::equal<T>>
template<typename T, typename HASH = hash<T>>
class HashSet
{
private:
struct Bucket
{
static constexpr uint8_t UNUSED = 0;
static constexpr uint8_t USED = 1;
static constexpr uint8_t REMOVED = 2;
alignas(T) uint8_t storage[sizeof(T)];
hash_t hash;
uint8_t state : 2;
uint8_t chain_start : 1;
uint8_t end : 1;
T* element() { return reinterpret_cast<T*>(storage); }
const T* element() const { return reinterpret_cast<const T*>(storage); }
};
public:
using value_type = T;
using size_type = size_t;
using iterator = HashSetIterator<HashSet, Bucket, T>;
using const_iterator = HashSetIterator<HashSet, const Bucket, const T>;
using value_type = T;
using size_type = size_t;
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
public:
HashSet() = default;
~HashSet() { clear(); }
HashSet(const HashSet&);
HashSet(HashSet&&);
HashSet(const HashSet& other) { *this = other; }
HashSet& operator=(const HashSet& other)
{
clear();
HashSet& operator=(const HashSet&);
HashSet& operator=(HashSet&&);
MUST(reserve(other.size()));
for (auto& bucket : other)
MUST(insert(bucket));
ErrorOr<void> insert(const T&);
ErrorOr<void> insert(T&&);
void remove(const T&);
void clear();
return *this;
}
ErrorOr<void> reserve(size_type);
HashSet(HashSet&& other) { *this = BAN::move(other); }
HashSet& operator=(HashSet&& other)
{
clear();
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
const_iterator begin() const { return const_iterator(m_buckets.end(), m_buckets.begin()); }
const_iterator end() const { return const_iterator(m_buckets.end(), m_buckets.end()); }
m_buckets = other.m_buckets;
m_capacity = other.m_capacity;
m_size = other.m_size;
m_removed = other.m_removed;
bool contains(const T&) const;
other.m_buckets = nullptr;
other.m_capacity = 0;
other.m_size = 0;
other.m_removed = 0;
return *this;
}
iterator begin() { return iterator(m_buckets); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(m_buckets); }
const_iterator end() const { return const_iterator(nullptr); }
ErrorOr<iterator> insert(const T& value)
{
return insert(T(value));
}
ErrorOr<iterator> insert(T&& value)
{
if (should_rehash_with_size(m_size + 1))
TRY(rehash(m_size * 2));
return insert_impl(BAN::move(value), HASH()(value));
}
template<detail::HashSetFindable<T, HASH, COMP> U>
void remove(const U& value)
{
if (auto it = find(value); it != end())
remove(it);
}
iterator remove(iterator it)
{
auto& bucket = *it.m_bucket;
bucket.element()->~T();
bucket.state = Bucket::REMOVED;
m_size--;
m_removed++;
return iterator(&bucket);
}
template<detail::HashSetFindable<T, HASH, COMP> U>
iterator find(const U& value)
{
return iterator(const_cast<Bucket*>(find_impl(value).m_bucket));
}
template<detail::HashSetFindable<T, HASH, COMP> U>
const_iterator find(const U& value) const
{
return find_impl(value);
}
void clear()
{
if (m_buckets == nullptr)
return;
for (size_type i = 0; i < m_capacity; i++)
if (m_buckets[i].state == Bucket::USED)
m_buckets[i].element()->~T();
BAN::deallocator(m_buckets);
m_buckets = nullptr;
m_capacity = 0;
m_size = 0;
m_removed = 0;
}
ErrorOr<void> reserve(size_type size)
{
if (should_rehash_with_size(size))
TRY(rehash(size * 2));
return {};
}
template<detail::HashSetFindable<T, HASH, COMP> U>
bool contains(const U& value) const
{
return find(value) != end();
}
size_type capacity() const
{
return m_capacity;
}
size_type size() const
{
return m_size;
}
bool empty() const
{
return m_size == 0;
}
size_type size() const;
bool empty() const;
private:
ErrorOr<void> rehash(size_type new_capacity)
{
new_capacity = BAN::Math::max<size_t>(16, BAN::Math::max(new_capacity, m_size + 1));
new_capacity = BAN::Math::round_up_to_power_of_two(new_capacity);
void* new_buckets = BAN::allocator((new_capacity + 1) * sizeof(Bucket));
if (new_buckets == nullptr)
return BAN::Error::from_errno(ENOMEM);
memset(new_buckets, 0, (new_capacity + 1) * sizeof(Bucket));
Bucket* old_buckets = m_buckets;
const size_type old_capacity = m_capacity;
m_buckets = static_cast<Bucket*>(new_buckets);
m_capacity = new_capacity;
m_size = 0;
m_removed = 0;
for (size_type i = 0; i < old_capacity; i++)
{
auto& old_bucket = old_buckets[i];
if (old_bucket.state != Bucket::USED)
continue;
insert_impl(BAN::move(*old_bucket.element()), old_bucket.hash);
old_bucket.element()->~T();
}
m_buckets[m_capacity].end = true;
BAN::deallocator(old_buckets);
return {};
}
template<detail::HashSetFindable<T, HASH, COMP> U>
const_iterator find_impl(const U& value) const
{
if (m_capacity == 0)
return end();
bool first = true;
const hash_t orig_hash = HASH()(value);
for (auto hash = orig_hash;; hash = get_next_hash_in_chain(hash, orig_hash), first = false)
{
auto& bucket = m_buckets[hash & (m_capacity - 1)];
if (bucket.state == Bucket::USED && bucket.hash == orig_hash && COMP()(*bucket.element(), value))
return const_iterator(&bucket);
if (bucket.state == Bucket::UNUSED)
return end();
if (!first && bucket.chain_start)
return end();
}
}
iterator insert_impl(T&& value, hash_t orig_hash)
{
ASSERT(!should_rehash_with_size(m_size + 1));
Bucket* target = nullptr;
bool first = true;
for (auto hash = orig_hash;; hash = get_next_hash_in_chain(hash, orig_hash), first = false)
{
auto& bucket = m_buckets[hash & (m_capacity - 1)];
if (!first)
bucket.chain_start = false;
if (bucket.state == Bucket::USED)
{
if (bucket.hash != orig_hash || !COMP()(*bucket.element(), value))
continue;
target = &bucket;
break;
}
if (target == nullptr)
target = &bucket;
if (bucket.state == Bucket::UNUSED)
break;
}
switch (target->state)
{
case Bucket::USED:
target->element()->~T();
break;
case Bucket::REMOVED:
m_removed--;
[[fallthrough]];
case Bucket::UNUSED:
m_size++;
break;
}
target->chain_start = first && target->state == Bucket::UNUSED;
target->hash = orig_hash;
target->state = Bucket::USED;
new (target->element()) T(BAN::move(value));
return iterator(target);
}
bool should_rehash_with_size(size_type size) const
{
if (m_capacity < 16)
return true;
if (size + m_removed > m_capacity / 4 * 3)
return true;
return false;
}
hash_t get_next_hash_in_chain(hash_t prev_hash, hash_t orig_hash) const
{
// TODO: does this even provide better performance than `return prev_hash + 1`
// when using "good" hash functions
return prev_hash * 1103515245 + (orig_hash | 1);
}
ErrorOr<void> rebucket(size_type);
LinkedList<T>& get_bucket(const T&);
const LinkedList<T>& get_bucket(const T&) const;
private:
Bucket* m_buckets { nullptr };
size_type m_capacity { 0 };
size_type m_size { 0 };
size_type m_removed { 0 };
Vector<LinkedList<T>> m_buckets;
size_type m_size = 0;
};
template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(const HashSet& other)
: m_buckets(other.m_buckets)
, m_size(other.m_size)
{
}
template<typename T, typename HASH>
HashSet<T, HASH>::HashSet(HashSet&& other)
: m_buckets(move(other.m_buckets))
, m_size(other.m_size)
{
other.clear();
}
template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(const HashSet& other)
{
clear();
m_buckets = other.m_buckets;
m_size = other.m_size;
return *this;
}
template<typename T, typename HASH>
HashSet<T, HASH>& HashSet<T, HASH>::operator=(HashSet&& other)
{
clear();
m_buckets = move(other.m_buckets);
m_size = other.m_size;
other.clear();
return *this;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::insert(const T& key)
{
return insert(move(T(key)));
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::insert(T&& key)
{
if (!empty() && get_bucket(key).contains(key))
return {};
TRY(rebucket(m_size + 1));
TRY(get_bucket(key).push_back(move(key)));
m_size++;
return {};
}
template<typename T, typename HASH>
void HashSet<T, HASH>::remove(const T& key)
{
if (empty()) return;
auto& bucket = get_bucket(key);
for (auto it = bucket.begin(); it != bucket.end(); it++)
{
if (*it == key)
{
bucket.remove(it);
m_size--;
break;
}
}
}
template<typename T, typename HASH>
void HashSet<T, HASH>::clear()
{
m_buckets.clear();
m_size = 0;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::reserve(size_type size)
{
TRY(rebucket(size));
return {};
}
template<typename T, typename HASH>
bool HashSet<T, HASH>::contains(const T& key) const
{
if (empty()) return false;
return get_bucket(key).contains(key);
}
template<typename T, typename HASH>
typename HashSet<T, HASH>::size_type HashSet<T, HASH>::size() const
{
return m_size;
}
template<typename T, typename HASH>
bool HashSet<T, HASH>::empty() const
{
return m_size == 0;
}
template<typename T, typename HASH>
ErrorOr<void> HashSet<T, HASH>::rebucket(size_type bucket_count)
{
if (m_buckets.size() >= bucket_count)
return {};
size_type new_bucket_count = Math::max<size_type>(bucket_count, m_buckets.size() * 2);
Vector<LinkedList<T>> new_buckets;
if (new_buckets.resize(new_bucket_count).is_error())
return Error::from_errno(ENOMEM);
for (auto& bucket : m_buckets)
{
for (auto it = bucket.begin(); it != bucket.end();)
{
size_type new_bucket_index = HASH()(*it) % new_buckets.size();
it = bucket.move_element_to_other_linked_list(new_buckets[new_bucket_index], new_buckets[new_bucket_index].end(), it);
}
}
m_buckets = move(new_buckets);
return {};
}
template<typename T, typename HASH>
LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key)
{
ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size();
return m_buckets[index];
}
template<typename T, typename HASH>
const LinkedList<T>& HashSet<T, HASH>::get_bucket(const T& key) const
{
ASSERT(!m_buckets.empty());
size_type index = HASH()(key) % m_buckets.size();
return m_buckets[index];
}
}

View File

@@ -1,89 +0,0 @@
#pragma once
#include <BAN/Iterators.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
namespace BAN
{
namespace detail
{
template<typename It, typename Comp>
void heapify_up(It begin, size_t index, Comp comp)
{
size_t parent = (index - 1) / 2;
while (parent < index)
{
if (comp(*(begin + index), *(begin + parent)))
break;
swap(*(begin + parent), *(begin + index));
index = parent;
parent = (index - 1) / 2;
}
}
template<typename It, typename Comp>
void heapify_down(It begin, size_t index, size_t len, Comp comp)
{
for (;;)
{
const size_t lchild = 2 * index + 1;
const size_t rchild = 2 * index + 2;
size_t child = 0;
if (lchild < len && !comp(*(begin + lchild), *(begin + index)))
{
if (rchild < len && !comp(*(begin + rchild), *(begin + lchild)))
child = rchild;
else
child = lchild;
}
else if (rchild < len && !comp(*(begin + rchild), *(begin + index)))
child = rchild;
else
break;
swap(*(begin + child), *(begin + index));
index = child;
}
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void make_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t index = (len - 2) / 2;
while (index < len)
detail::heapify_down(begin, index--, len, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void push_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
detail::heapify_up(begin, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void pop_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
swap(*begin, *(begin + len - 1));
detail::heapify_down(begin, 0, len - 1, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
void sort_heap(It begin, It end, Comp comp = {})
{
while (begin != end)
pop_heap(begin, end--, comp);
}
}

View File

@@ -9,10 +9,6 @@ namespace BAN
struct IPv4Address
{
constexpr IPv4Address()
: IPv4Address(0)
{ }
constexpr IPv4Address(uint32_t u32_address)
{
raw = u32_address;

View File

@@ -34,9 +34,9 @@ namespace BAN
ErrorOr<void> insert(iterator, const T&);
ErrorOr<void> insert(iterator, T&&);
template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> emplace_back(Args&&...);
template<typename... Args>
ErrorOr<void> emplace(iterator, Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> emplace(iterator, Args&&...);
void pop_back();
iterator remove(iterator);
@@ -196,14 +196,14 @@ namespace BAN
template<typename T>
template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
ErrorOr<void> LinkedList<T>::emplace_back(Args&&... args)
{
return emplace(end(), forward<Args>(args)...);
}
template<typename T>
template<typename... Args>
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args) requires is_constructible_v<T, Args...>
ErrorOr<void> LinkedList<T>::emplace(iterator iter, Args&&... args)
{
Node* new_node = TRY(allocate_node(forward<Args>(args)...));
insert_node(iter, new_node);

View File

@@ -1,46 +0,0 @@
#pragma once
#define _ban_count_args_impl(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define _ban_count_args(...) _ban_count_args_impl(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _ban_concat_impl(a, b) a##b
#define _ban_concat(a, b) _ban_concat_impl(a, b)
#define _ban_stringify_impl(x) #x
#define _ban_stringify(x) _ban_stringify_impl(x)
#define _ban_fe_0(f)
#define _ban_fe_1(f, _0) f(0, _0)
#define _ban_fe_2(f, _0, _1) f(0, _0) f(1, _1)
#define _ban_fe_3(f, _0, _1, _2) f(0, _0) f(1, _1) f(2, _2)
#define _ban_fe_4(f, _0, _1, _2, _3) f(0, _0) f(1, _1) f(2, _2) f(3, _3)
#define _ban_fe_5(f, _0, _1, _2, _3, _4) f(0, _0) f(1, _1) f(2, _2) f(3, _3) f(4, _4)
#define _ban_fe_6(f, _0, _1, _2, _3, _4, _5) f(0, _0) f(1, _1) f(2, _2) f(3, _3) f(4, _4) f(5, _5)
#define _ban_fe_7(f, _0, _1, _2, _3, _4, _5, _6) f(0, _0) f(1, _1) f(2, _2) f(3, _3) f(4, _4) f(5, _5) f(6, _6)
#define _ban_fe_8(f, _0, _1, _2, _3, _4, _5, _6, _7) f(0, _0) f(1, _1) f(2, _2) f(3, _3) f(4, _4) f(5, _5) f(6, _6) f(7, _7)
#define _ban_fe_9(f, _0, _1, _2, _3, _4, _5, _6, _7, _8) f(0, _0) f(1, _1) f(2, _2) f(3, _3) f(4, _4) f(5, _5) f(6, _6) f(7, _7) f(8, _8)
#define _ban_for_each(f, ...) _ban_concat(_ban_fe_, _ban_count_args(__VA_ARGS__))(f __VA_OPT__(,) __VA_ARGS__)
#define _ban_fe_comma_0(f)
#define _ban_fe_comma_1(f, _0) f(0, _0)
#define _ban_fe_comma_2(f, _0, _1) f(0, _0), f(1, _1)
#define _ban_fe_comma_3(f, _0, _1, _2) f(0, _0), f(1, _1), f(2, _2)
#define _ban_fe_comma_4(f, _0, _1, _2, _3) f(0, _0), f(1, _1), f(2, _2), f(3, _3)
#define _ban_fe_comma_5(f, _0, _1, _2, _3, _4) f(0, _0), f(1, _1), f(2, _2), f(3, _3), f(4, _4)
#define _ban_fe_comma_6(f, _0, _1, _2, _3, _4, _5) f(0, _0), f(1, _1), f(2, _2), f(3, _3), f(4, _4), f(5, _5)
#define _ban_fe_comma_7(f, _0, _1, _2, _3, _4, _5, _6) f(0, _0), f(1, _1), f(2, _2), f(3, _3), f(4, _4), f(5, _5), f(6, _6)
#define _ban_fe_comma_8(f, _0, _1, _2, _3, _4, _5, _6, _7) f(0, _0), f(1, _1), f(2, _2), f(3, _3), f(4, _4), f(5, _5), f(6, _6), f(7, _7)
#define _ban_fe_comma_9(f, _0, _1, _2, _3, _4, _5, _6, _7, _8) f(0, _0), f(1, _1), f(2, _2), f(3, _3), f(4, _4), f(5, _5), f(6, _6), f(7, _7), f(8, _8)
#define _ban_for_each_comma(f, ...) _ban_concat(_ban_fe_comma_, _ban_count_args(__VA_ARGS__))(f __VA_OPT__(,) __VA_ARGS__)
#define _ban_get_0(a0, ...) a0
#define _ban_get_1(a0, a1, ...) a1
#define _ban_get_2(a0, a1, a2, ...) a2
#define _ban_get_3(a0, a1, a2, a3, ...) a3
#define _ban_get_4(a0, a1, a2, a3, a4, ...) a4
#define _ban_get_5(a0, a1, a2, a3, a4, a5, ...) a5
#define _ban_get_6(a0, a1, a2, a3, a4, a5, a6, ...) a6
#define _ban_get_7(a0, a1, a2, a3, a4, a5, a6, a7, ...) a7
#define _ban_get_8(a0, a1, a2, a3, a4, a5, a6, a7, a8, ...) a8
#define _ban_get_9(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, ...) a9
#define _ban_get(n, ...) _ban_concat(_ban_get_, n)(__VA_ARGS__)

View File

@@ -1,18 +1,18 @@
#pragma once
#include <BAN/Limits.h>
#include <BAN/Numbers.h>
#include <BAN/Traits.h>
#include <float.h>
#include <stddef.h>
#include <stdint.h>
namespace BAN::Math
{
template<typename T>
inline constexpr T abs(T x)
inline constexpr T abs(T val)
{
return x < 0 ? -x : x;
return val < 0 ? -val : val;
}
template<typename T>
@@ -36,11 +36,12 @@ namespace BAN::Math
template<integral T>
inline constexpr T gcd(T a, T b)
{
T t;
while (b)
{
T temp = b;
t = b;
b = a % b;
a = temp;
a = t;
}
return a;
}
@@ -58,424 +59,86 @@ namespace BAN::Math
}
template<integral T>
inline constexpr bool is_power_of_two(T x)
inline constexpr bool is_power_of_two(T value)
{
if (x == 0)
if (value == 0)
return false;
return (x & (x - 1)) == 0;
return (value & (value - 1)) == 0;
}
template<integral T> requires(sizeof(T) <= 8)
inline constexpr T round_up_to_power_of_two(T x)
template<BAN::integral T>
static constexpr bool will_multiplication_overflow(T a, T b)
{
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
if constexpr(sizeof(T) >= 2)
x |= x >> 8;
if constexpr(sizeof(T) >= 4)
x |= x >> 16;
if constexpr(sizeof(T) >= 8)
x |= x >> 32;
return x + 1;
if (a == 0 || b == 0)
return false;
if ((a > 0) == (b > 0))
return a > BAN::numeric_limits<T>::max() / b;
else
return a < BAN::numeric_limits<T>::min() / b;
}
template<integral T>
__attribute__((always_inline))
inline constexpr bool will_multiplication_overflow(T a, T b)
template<BAN::integral T>
static constexpr bool will_addition_overflow(T a, T b)
{
T dummy;
return __builtin_mul_overflow(a, b, &dummy);
}
template<integral T>
__attribute__((always_inline))
inline constexpr bool will_addition_overflow(T a, T b)
{
T dummy;
return __builtin_add_overflow(a, b, &dummy);
if (a > 0 && b > 0)
return a > BAN::numeric_limits<T>::max() - b;
if (a < 0 && b < 0)
return a < BAN::numeric_limits<T>::min() - b;
return false;
}
template<typename T>
requires is_same_v<T, unsigned int> || is_same_v<T, unsigned long> || is_same_v<T, unsigned long long>
inline constexpr T ilog2(T x)
inline constexpr T ilog2(T value)
{
if constexpr(is_same_v<T, unsigned int>)
return sizeof(T) * 8 - __builtin_clz(x) - 1;
return sizeof(T) * 8 - __builtin_clz(value) - 1;
if constexpr(is_same_v<T, unsigned long>)
return sizeof(T) * 8 - __builtin_clzl(x) - 1;
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
}
// This is ugly but my clangd does not like including
// intrinsic headers at all
#if !defined(__SSE__) || !defined(__SSE2__)
#pragma GCC push_options
#ifndef __SSE__
#pragma GCC target("sse")
#endif
#ifndef __SSE2__
#pragma GCC target("sse2")
#endif
#define BAN_MATH_POP_OPTIONS
#endif
template<floating_point T>
inline constexpr T floor(T x)
{
if constexpr(is_same_v<T, float>)
return __builtin_floorf(x);
if constexpr(is_same_v<T, double>)
return __builtin_floor(x);
if constexpr(is_same_v<T, long double>)
return __builtin_floorl(x);
return sizeof(T) * 8 - __builtin_clzl(value) - 1;
return sizeof(T) * 8 - __builtin_clzll(value) - 1;
}
template<floating_point T>
inline constexpr T ceil(T x)
inline constexpr T log2(T value)
{
if constexpr(is_same_v<T, float>)
return __builtin_ceilf(x);
if constexpr(is_same_v<T, double>)
return __builtin_ceil(x);
if constexpr(is_same_v<T, long double>)
return __builtin_ceill(x);
T result;
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"((T)1.0) : "st(1)");
return result;
}
template<floating_point T>
inline constexpr T round(T x)
inline constexpr T log10(T value)
{
if (x == (T)0.0)
return x;
if (x > (T)0.0)
return floor<T>(x + (T)0.5);
return ceil<T>(x - (T)0.5);
constexpr T INV_LOG_2_10 = 0.3010299956639811952137388947244930267681898814621085413104274611;
T result;
asm volatile("fyl2x" : "=t"(result) : "0"(value), "u"(INV_LOG_2_10) : "st(1)");
return result;
}
template<floating_point T>
inline constexpr T trunc(T x)
inline constexpr T log(T value, T base)
{
if constexpr(is_same_v<T, float>)
return __builtin_truncf(x);
if constexpr(is_same_v<T, double>)
return __builtin_trunc(x);
if constexpr(is_same_v<T, long double>)
return __builtin_truncl(x);
return log2(value) / log2(base);
}
template<floating_point T>
inline constexpr T rint(T x)
inline constexpr T pow(T base, T exp)
{
asm("frndint" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T fmod(T a, T b)
{
asm(
"1:"
"fprem;"
"fnstsw %%ax;"
"testb $4, %%ah;"
"jne 1b;"
: "+t"(a)
: "u"(b)
: "ax", "cc"
);
return a;
}
template<floating_point T>
inline constexpr T remainder(T a, T b)
{
asm(
"1:"
"fprem1;"
"fnstsw %%ax;"
"testb $4, %%ah;"
"jne 1b;"
: "+t"(a)
: "u"(b)
: "ax", "cc"
);
return a;
}
template<floating_point T>
static T modf(T x, T* iptr)
{
const T frac = BAN::Math::fmod<T>(x, (T)1.0);
*iptr = x - frac;
return frac;
}
template<floating_point T>
inline constexpr T frexp(T num, int* exp)
{
if (num == (T)0.0)
{
*exp = 0;
return (T)0.0;
}
T e;
asm("fxtract" : "+t"(num), "=u"(e));
*exp = (int)e + 1;
return num / (T)2.0;
}
template<floating_point T>
inline constexpr T copysign(T x, T y)
{
if ((x < (T)0.0) != (y < (T)0.0))
x = -x;
return x;
}
namespace detail
{
template<floating_point T>
inline constexpr T fyl2x(T x, T y)
{
asm("fyl2x" : "+t"(x) : "u"(y) : "st(1)");
return x;
}
}
template<floating_point T>
inline constexpr T log(T x)
{
return detail::fyl2x<T>(x, numbers::ln2_v<T>);
}
template<floating_point T>
inline constexpr T log2(T x)
{
return detail::fyl2x<T>(x, 1.0);
}
template<floating_point T>
inline constexpr T log10(T x)
{
return detail::fyl2x<T>(x, numbers::lg2_v<T>);
}
template<floating_point T>
inline constexpr T logb(T x)
{
static_assert(FLT_RADIX == 2);
return log2<T>(x);
}
template<floating_point T>
inline constexpr T exp2(T x)
{
if (abs(x) <= (T)1.0)
{
asm("f2xm1" : "+t"(x));
return x + (T)1.0;
}
asm(
T result;
asm volatile(
"fyl2x;"
"fld1;"
"fld %%st(1);"
"fprem;"
"f2xm1;"
"faddp;"
"fscale;"
"fstp %%st(1);"
: "+t"(x)
"fxch %%st(1);"
"fstp %%st;"
: "=t"(result)
: "0"(base), "u"(exp)
);
return x;
return result;
}
template<floating_point T>
inline constexpr T exp(T x)
{
return exp2<T>(x * numbers::log2e_v<T>);
}
template<floating_point T>
inline constexpr T pow(T x, T y)
{
if (x == (T)0.0)
return (T)0.0;
return exp2<T>(y * log2<T>(x));
}
template<floating_point T>
inline constexpr T scalbn(T x, int n)
{
asm("fscale" : "+t"(x) : "u"(static_cast<T>(n)));
return x;
}
template<floating_point T>
inline constexpr T ldexp(T x, int y)
{
const bool exp_sign = y < 0;
if (exp_sign)
y = -y;
T exp = (T)1.0;
T mult = (T)2.0;
while (y)
{
if (y & 1)
exp *= mult;
mult *= mult;
y >>= 1;
}
if (exp_sign)
exp = (T)1.0 / exp;
return x * exp;
}
template<floating_point T>
inline constexpr T sqrt(T x)
{
if constexpr(BAN::is_same_v<T, float>)
{
using v4sf = float __attribute__((vector_size(16)));
return __builtin_ia32_sqrtss((v4sf) { x, 0.0f, 0.0f, 0.0f })[0];
}
else if constexpr(BAN::is_same_v<T, double>)
{
using v2df = double __attribute__((vector_size(16)));
return __builtin_ia32_sqrtsd((v2df) { x, 0.0 })[0];
}
else if constexpr(BAN::is_same_v<T, long double>)
{
asm("fsqrt" : "+t"(x));
return x;
}
}
template<floating_point T>
inline constexpr T cbrt(T value)
{
return pow<T>(value, (T)1.0 / (T)3.0);
}
template<floating_point T>
inline constexpr T sin(T x)
{
asm("fsin" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr T cos(T x)
{
asm("fcos" : "+t"(x));
return x;
}
template<floating_point T>
inline constexpr void sincos(T x, T& sin, T& cos)
{
asm("fsincos" : "=t"(cos), "=u"(sin) : "0"(x));
}
template<floating_point T>
inline constexpr T tan(T x)
{
T one, ret;
asm("fptan" : "=t"(one), "=u"(ret) : "0"(x));
return ret;
}
template<floating_point T>
inline constexpr T atan2(T y, T x)
{
asm("fpatan" : "+t"(x) : "u"(y) : "st(1)");
return x;
}
template<floating_point T>
inline constexpr T atan(T x)
{
return atan2<T>(x, (T)1.0);
}
template<floating_point T>
inline constexpr T asin(T x)
{
if (x == (T)0.0)
return (T)0.0;
if (x == (T)1.0)
return +numbers::pi_v<T> / (T)2.0;
if (x == (T)-1.0)
return -numbers::pi_v<T> / (T)2.0;
return (T)2.0 * atan<T>(x / ((T)1.0 + sqrt<T>((T)1.0 - x * x)));
}
template<floating_point T>
inline constexpr T acos(T x)
{
if (x == (T)0.0)
return numbers::pi_v<T> / (T)2.0;
if (x == (T)1.0)
return (T)0.0;
if (x == (T)-1.0)
return numbers::pi_v<T>;
return (T)2.0 * atan<T>(sqrt<T>((T)1.0 - x * x) / ((T)1.0 + x));
}
template<floating_point T>
inline constexpr T sinh(T x)
{
return (exp<T>(x) - exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T cosh(T x)
{
return (exp<T>(x) + exp<T>(-x)) / (T)2.0;
}
template<floating_point T>
inline constexpr T tanh(T x)
{
const T exp_px = exp<T>(+x);
const T exp_nx = exp<T>(-x);
return (exp_px - exp_nx) / (exp_px + exp_nx);
}
template<floating_point T>
inline constexpr T asinh(T x)
{
return log<T>(x + sqrt<T>(x * x + (T)1.0));
}
template<floating_point T>
inline constexpr T acosh(T x)
{
return log<T>(x + sqrt<T>(x * x - (T)1.0));
}
template<floating_point T>
inline constexpr T atanh(T x)
{
return (T)0.5 * log<T>(((T)1.0 + x) / ((T)1.0 - x));
}
template<floating_point T>
inline constexpr T hypot(T x, T y)
{
return sqrt<T>(x * x + y * y);
}
#ifdef BAN_MATH_POP_OPTIONS
#undef BAN_MATH_POP_OPTIONS
#pragma GCC pop_options
#endif
}

View File

@@ -9,12 +9,10 @@
namespace BAN
{
#if defined(__is_kernel)
static constexpr void*(*allocator)(size_t) = kmalloc;
static constexpr void*(*reallocator)(void*, size_t) = nullptr;
static constexpr void(*deallocator)(void*) = kfree;
static constexpr void*(&allocator)(size_t) = kmalloc;
static constexpr void(&deallocator)(void*) = kfree;
#else
static constexpr void*(*allocator)(size_t) = malloc;
static constexpr void*(*reallocator)(void*, size_t) = realloc;
static constexpr void(*deallocator)(void*) = free;
static constexpr void*(&allocator)(size_t) = malloc;
static constexpr void(&deallocator)(void*) = free;
#endif
}

View File

@@ -1,28 +0,0 @@
#pragma once
#include <BAN/Traits.h>
namespace BAN::numbers
{
template<floating_point T> inline constexpr T e_v = 2.71828182845904523536;
template<floating_point T> inline constexpr T log2e_v = 1.44269504088896340736;
template<floating_point T> inline constexpr T lge_v = 0.43429448190325182765;
template<floating_point T> inline constexpr T lg2_v = 0.30102999566398119521;
template<floating_point T> inline constexpr T ln2_v = 0.69314718055994530942;
template<floating_point T> inline constexpr T ln10_v = 2.30258509299404568402;
template<floating_point T> inline constexpr T pi_v = 3.14159265358979323846;
template<floating_point T> inline constexpr T sqrt2_v = 1.41421356237309504880;
template<floating_point T> inline constexpr T sqrt3_v = 1.73205080756887729353;
inline constexpr double e = e_v<double>;
inline constexpr double log2e = log2e_v<double>;
inline constexpr double lge = lge_v<double>;
inline constexpr double lg2 = lge_v<double>;
inline constexpr double ln2 = ln2_v<double>;
inline constexpr double ln10 = ln10_v<double>;
inline constexpr double pi = pi_v<double>;
inline constexpr double sqrt2 = sqrt2_v<double>;
inline constexpr double sqrt3 = sqrt3_v<double>;
}

View File

@@ -25,7 +25,7 @@ namespace BAN
constexpr Optional& operator=(const Optional&);
template<typename... Args>
constexpr Optional& emplace(Args&&...) requires is_constructible_v<T, Args...>;
constexpr Optional& emplace(Args&&...);
constexpr T* operator->();
constexpr const T* operator->() const;
@@ -111,7 +111,7 @@ namespace BAN
template<typename T>
template<typename... Args>
constexpr Optional<T>& Optional<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
constexpr Optional<T>& Optional<T>::emplace(Args&&... args)
{
clear();
m_has_value = true;
@@ -167,14 +167,14 @@ namespace BAN
constexpr T& Optional<T>::value()
{
ASSERT(has_value());
return *reinterpret_cast<T*>(&m_storage);
return (T&)m_storage;
}
template<typename T>
constexpr const T& Optional<T>::value() const
{
ASSERT(has_value());
return *reinterpret_cast<const T*>(&m_storage);
return (const T&)m_storage;
}
template<typename T>
@@ -182,7 +182,7 @@ namespace BAN
{
if (!has_value())
return empty;
return value();
return (T&)m_storage;
}
template<typename T>
@@ -190,7 +190,7 @@ namespace BAN
{
if (!has_value())
return empty;
return value();
return (const T&)m_storage;
}
template<typename T>

View File

@@ -1,10 +1,6 @@
#pragma once
#if __has_include(<new>)
#include <new>
#else
#include <stddef.h>
#include <stddef.h>
inline void* operator new(size_t, void* addr) { return addr; }
inline void* operator new[](size_t, void* addr) { return addr; }
#endif
inline void* operator new(size_t, void* addr) { return addr; }
inline void* operator new[](size_t, void* addr) { return addr; }

View File

@@ -1,64 +0,0 @@
#pragma once
#include "BAN/Errors.h"
#include <BAN/Vector.h>
#include <BAN/Heap.h>
namespace BAN
{
template<typename T, typename Comp = less<T>>
class PriorityQueue
{
public:
PriorityQueue() = default;
PriorityQueue(Comp comp)
: m_comp(comp)
{ }
ErrorOr<void> push(const T& value)
{
TRY(m_data.push_back(value));
push_heap(m_data.begin(), m_data.end());
return {};
}
ErrorOr<void> push(T&& value)
{
TRY(m_data.push_back(move(value)));
push_heap(m_data.begin(), m_data.end());
return {};
}
template<typename... Args>
ErrorOr<void> emplace(Args&&... args) requires is_constructible_v<T, Args...>
{
TRY(m_data.emplace_back(forward<Args>(args)...));
push_heap(m_data.begin(), m_data.end());
return {};
}
void pop()
{
pop_heap(m_data.begin(), m_data.end());
m_data.pop_back();
}
BAN::ErrorOr<void> reserve(Vector<T>::size_type size)
{
return m_data.reserve(size);
}
T& top() { return m_data.front(); }
const T& top() const { return m_data.front(); }
bool empty() const { return m_data.empty(); }
Vector<T>::size_type size() const { return m_data.size(); }
Vector<T>::size_type capacity() const { return m_data.capacity(); }
private:
Comp m_comp;
Vector<T> m_data;
};
}

View File

@@ -31,7 +31,7 @@ namespace BAN
ErrorOr<void> push(T&&);
ErrorOr<void> push(const T&);
template<typename... Args>
ErrorOr<void> emplace(Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> emplace(Args&&...);
ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit();
@@ -131,7 +131,7 @@ namespace BAN
template<typename T>
template<typename... Args>
ErrorOr<void> Queue<T>::emplace(Args&&... args) requires is_constructible_v<T, Args...>
ErrorOr<void> Queue<T>::emplace(Args&&... args)
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...);

View File

@@ -2,9 +2,9 @@
#include <BAN/Atomic.h>
#include <BAN/Errors.h>
#include <BAN/Hash.h>
#include <BAN/Move.h>
#include <BAN/NoCopyMove.h>
#include <stdint.h>
namespace BAN
{
@@ -76,9 +76,8 @@ namespace BAN
return ptr;
}
// NOTE: don't use is_constructible_v<T, Args...> as RefPtr<T> is allowed with friends
template<typename... Args>
static ErrorOr<RefPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
static ErrorOr<RefPtr> create(Args&&... args)
{
T* pointer = new T(forward<Args>(args)...);
if (pointer == nullptr)
@@ -129,9 +128,14 @@ namespace BAN
return *this;
}
T* ptr() const { return m_pointer; }
T& operator*() const { ASSERT(!empty()); return *ptr(); }
T* operator->() const { ASSERT(!empty()); return ptr(); }
T* ptr() { ASSERT(!empty()); return m_pointer; }
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
T& operator*() { return *ptr(); }
const T& operator*() const { return *ptr(); }
T* operator->() { return ptr(); }
const T* operator->() const { return ptr(); }
bool operator==(RefPtr other) const { return m_pointer == other.m_pointer; }
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
@@ -153,13 +157,4 @@ namespace BAN
friend class RefPtr;
};
template<typename T>
struct hash<RefPtr<T>>
{
constexpr hash_t operator()(const RefPtr<T>& ptr) const
{
return hash<T*>()(ptr.ptr());
}
};
}

View File

@@ -1,6 +1,5 @@
#pragma once
#include <BAN/Heap.h>
#include <BAN/Math.h>
#include <BAN/Swap.h>
#include <BAN/Traits.h>
@@ -9,7 +8,7 @@
namespace BAN::sort
{
template<typename It, typename Comp = less<it_value_type_t<It>>>
template<typename It, typename Comp = less<typename It::value_type>>
void exchange_sort(It begin, It end, Comp comp = {})
{
for (It lhs = begin; lhs != end; ++lhs)
@@ -43,7 +42,7 @@ namespace BAN::sort
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
template<typename It, typename Comp = less<typename It::value_type>>
void quick_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
@@ -53,14 +52,14 @@ namespace BAN::sort
quick_sort(++mid, end, comp);
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
template<typename It, typename Comp = less<typename It::value_type>>
void insertion_sort(It begin, It end, Comp comp = {})
{
if (distance(begin, end) <= 1)
return;
for (It it1 = next(begin, 1); it1 != end; ++it1)
{
auto x = move(*it1);
typename It::value_type x = move(*it1);
It it2 = it1;
for (; it2 != begin && comp(x, *prev(it2, 1)); --it2)
*it2 = move(*prev(it2, 1));
@@ -68,7 +67,83 @@ namespace BAN::sort
}
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
namespace detail
{
template<typename It, typename Comp>
void push_heap(It begin, size_t hole_index, size_t top_index, typename It::value_type value, Comp comp)
{
size_t parent = (hole_index - 1) / 2;
while (hole_index > top_index && comp(*next(begin, parent), value))
{
*next(begin, hole_index) = move(*next(begin, parent));
hole_index = parent;
parent = (hole_index - 1) / 2;
}
*next(begin, hole_index) = move(value);
}
template<typename It, typename Comp>
void adjust_heap(It begin, size_t hole_index, size_t len, typename It::value_type value, Comp comp)
{
const size_t top_index = hole_index;
size_t child = hole_index;
while (child < (len - 1) / 2)
{
child = 2 * (child + 1);
if (comp(*next(begin, child), *next(begin, child - 1)))
child--;
*next(begin, hole_index) = move(*next(begin, child));
hole_index = child;
}
if (len % 2 == 0 && child == (len - 2) / 2)
{
child = 2 * (child + 1);
*next(begin, hole_index) = move(*next(begin, child - 1));
hole_index = child - 1;
}
push_heap(begin, hole_index, top_index, move(value), comp);
}
}
template<typename It, typename Comp = less<typename It::value_type>>
void make_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t parent = (len - 2) / 2;
while (true)
{
detail::adjust_heap(begin, parent, len, move(*next(begin, parent)), comp);
if (parent == 0)
break;
parent--;
}
}
template<typename It, typename Comp = less<typename It::value_type>>
void sort_heap(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
if (len <= 1)
return;
size_t last = len;
while (last > 1)
{
last--;
typename It::value_type x = move(*next(begin, last));
*next(begin, last) = move(*begin);
detail::adjust_heap(begin, 0, last, move(x), comp);
}
}
template<typename It, typename Comp = less<typename It::value_type>>
void heap_sort(It begin, It end, Comp comp = {})
{
make_heap(begin, end, comp);
@@ -92,7 +167,7 @@ namespace BAN::sort
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
template<typename It, typename Comp = less<typename It::value_type>>
void intro_sort(It begin, It end, Comp comp = {})
{
const size_t len = distance(begin, end);
@@ -115,10 +190,10 @@ namespace BAN::sort
}
template<typename It, size_t radix = 256>
requires is_unsigned_v<it_value_type_t<It>> && (radix > 0 && (radix & (radix - 1)) == 0)
requires is_unsigned_v<typename It::value_type> && (radix > 0 && (radix & (radix - 1)) == 0)
BAN::ErrorOr<void> radix_sort(It begin, It end)
{
using value_type = it_value_type_t<It>;
using value_type = typename It::value_type;
const size_t len = distance(begin, end);
if (len <= 1)
@@ -156,7 +231,7 @@ namespace BAN::sort
return {};
}
template<typename It, typename Comp = less<it_value_type_t<It>>>
template<typename It, typename Comp = less<typename It::value_type>>
void sort(It begin, It end, Comp comp = {})
{
return intro_sort(begin, end, comp);

View File

@@ -14,89 +14,121 @@ namespace BAN
public:
using value_type = T;
using size_type = size_t;
using iterator = IteratorSimple<value_type, Span>;
using const_iterator = ConstIteratorSimple<value_type, Span>;
private:
template<typename S>
static inline constexpr bool can_init_from_v = is_same_v<value_type, const S> || is_same_v<value_type, S>;
using iterator = IteratorSimple<T, Span>;
using const_iterator = ConstIteratorSimple<T, Span>;
public:
Span() = default;
Span(value_type* data, size_type size)
: m_data(data)
, m_size(size)
{ }
Span(T*, size_type);
Span(Span<T>&);
template<typename S>
Span(const Span<S>& other) requires can_init_from_v<S>
: m_data(other.m_data)
, m_size(other.m_size)
{ }
template<typename S>
Span(Span<S>&& other) requires can_init_from_v<S>
: m_data(other.m_data)
, m_size(other.m_size)
{
other.clear();
}
template<typename S>
Span& operator=(const Span<S>& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
template<typename S>
Span& operator=(Span<S>&& other) requires can_init_from_v<S>
{
m_data = other.m_data;
m_size = other.m_size;
return *this;
}
requires(is_same_v<T, const S>)
Span(const Span<S>&);
iterator begin() { return iterator(m_data); }
iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
value_type& operator[](size_type index) const
{
ASSERT(index < m_size);
return m_data[index];
}
T& operator[](size_type);
const T& operator[](size_type) const;
value_type* data() const
{
return m_data;
}
T* data();
const T* data() const;
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
bool empty() const;
size_type size() const;
void clear()
{
m_data = nullptr;
m_size = 0;
}
void clear();
Span slice(size_type start, size_type length = ~size_type(0)) const
{
ASSERT(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
Span slice(size_type, size_type = ~size_type(0));
Span<const value_type> as_const() const { return *this; }
Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
private:
value_type* m_data = nullptr;
T* m_data = nullptr;
size_type m_size = 0;
friend class Span<const value_type>;
};
template<typename T>
Span<T>::Span(T* data, size_type size)
: m_data(data)
, m_size(size)
{
}
template<typename T>
Span<T>::Span(Span& other)
: m_data(other.data())
, m_size(other.size())
{
}
template<typename T>
template<typename S>
requires(is_same_v<T, const S>)
Span<T>::Span(const Span<S>& other)
: m_data(other.data())
, m_size(other.size())
{
}
template<typename T>
T& Span<T>::operator[](size_type index)
{
ASSERT(m_data);
ASSERT(index < m_size);
return m_data[index];
}
template<typename T>
const T& Span<T>::operator[](size_type index) const
{
ASSERT(m_data);
ASSERT(index < m_size);
return m_data[index];
}
template<typename T>
T* Span<T>::data()
{
return m_data;
}
template<typename T>
const T* Span<T>::data() const
{
return m_data;
}
template<typename T>
bool Span<T>::empty() const
{
return m_size == 0;
}
template<typename T>
typename Span<T>::size_type Span<T>::size() const
{
return m_size;
}
template<typename T>
void Span<T>::clear()
{
m_data = nullptr;
m_size = 0;
}
template<typename T>
Span<T> Span<T>::slice(size_type start, size_type length)
{
ASSERT(m_data);
ASSERT(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
}

View File

@@ -14,7 +14,6 @@ namespace BAN
{
public:
using size_type = size_t;
using value_type = char;
using iterator = IteratorSimple<char, String>;
using const_iterator = ConstIteratorSimple<char, String>;
static constexpr size_type sso_capacity = 15;
@@ -128,7 +127,7 @@ namespace BAN
void remove(size_type index)
{
ASSERT(index < m_size);
memmove(data() + index, data() + index + 1, m_size - index);
memcpy(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
@@ -353,9 +352,10 @@ namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const String& string, const ValueFormat& format)
void print_argument(F putc, const String& string, const ValueFormat&)
{
print_argument(putc, string.sv(), format);
for (String::size_type i = 0; i < string.size(); i++)
putc(string[i]);
}
}

View File

@@ -14,7 +14,6 @@ namespace BAN
{
public:
using size_type = size_t;
using value_type = char;
using const_iterator = ConstIteratorSimple<char, StringView>;
public:
@@ -22,8 +21,7 @@ namespace BAN
constexpr StringView(const char* string, size_type len = -1)
{
if (len == size_type(-1))
for (len = 0; string[len];)
len++;
len = strlen(string);
m_data = string;
m_size = len;
}
@@ -188,10 +186,15 @@ namespace BAN
{
if (target.size() > m_size)
return false;
for (size_type i = 0; i < target.size(); i++)
if (m_data[i] != target[i])
return false;
return true;
for (size_type i = 0; i < m_size - target.size(); i++)
{
bool valid = true;
for (size_type j = 0; j < target.size() && valid; j++)
valid = (m_data[i + j] == target[j]);
if (valid)
return true;
}
return false;
}
constexpr bool contains(char ch) const
@@ -247,12 +250,10 @@ namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const StringView& sv, const ValueFormat& format)
void print_argument(F putc, const StringView& sv, const ValueFormat&)
{
for (StringView::size_type i = 0; i < sv.size(); i++)
putc(sv[i]);
for (int i = sv.size(); i < format.fill; i++)
putc(' ');
}
}

View File

@@ -61,9 +61,6 @@ namespace BAN
template<typename T> struct is_move_constructible { static constexpr bool value = is_constructible_v<T, T&&>; };
template<typename T> inline constexpr bool is_move_constructible_v = is_move_constructible<T>::value;
template<typename T> struct is_trivially_copyable { static constexpr bool value = __is_trivially_copyable(T); };
template<typename T> inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
template<typename T> struct is_integral { static constexpr bool value = requires (T t, T* p, void (*f)(T)) { reinterpret_cast<T>(t); f(0); p + t; }; };
template<typename T> inline constexpr bool is_integral_v = is_integral<T>::value;
template<typename T> concept integral = is_integral_v<T>;
@@ -93,9 +90,6 @@ namespace BAN
template<typename Base, typename Derived> struct is_base_of { static constexpr bool value = __is_base_of(Base, Derived); };
template<typename Base, typename Derived> inline constexpr bool is_base_of_v = is_base_of<Base, Derived>::value;
template<typename T> struct is_pod { static constexpr bool value = __is_pod(T); };
template<typename T> inline constexpr bool is_pod_v = is_pod<T>::value;
namespace detail
{
template<typename T, bool = is_arithmetic_v<T>> struct is_signed { static constexpr bool value = T(-1) < T(0); };
@@ -142,10 +136,6 @@ namespace BAN
template<typename T> using make_signed_t = typename make_signed<T>::type;
#undef __BAN_TRAITS_MAKE_SIGNED_CV
template<typename T> struct it_value_type { using value_type = T::value_type; };
template<typename T> struct it_value_type<T*> { using value_type = T; };
template<typename T> using it_value_type_t = typename it_value_type<T>::value_type;
template<typename T> struct less { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; } };
template<typename T> struct equal { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs == rhs; } };
template<typename T> struct greater { constexpr bool operator()(const T& lhs, const T& rhs) const { return lhs > rhs; } };

View File

@@ -18,7 +18,7 @@ namespace BAN::UTF8
return 3;
if ((first_byte & 0xF8) == 0xF0)
return 4;
return UTF8::invalid;
return 0;
}
template<typename T> requires (sizeof(T) == 1)
@@ -76,8 +76,6 @@ namespace BAN::UTF8
}
}
*ptr = '\0';
return true;
}

View File

@@ -1,7 +1,6 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Hash.h>
#include <BAN/NoCopyMove.h>
namespace BAN
@@ -34,9 +33,8 @@ namespace BAN
return uniq;
}
// NOTE: don't use is_constructible_v<T, Args...> as UniqPtr<T> is allowed with friends
template<typename... Args>
static BAN::ErrorOr<UniqPtr> create(Args&&... args) requires requires(Args&&... args) { T(forward<Args>(args)...); }
static BAN::ErrorOr<UniqPtr> create(Args&&... args)
{
UniqPtr uniq;
uniq.m_pointer = new T(BAN::forward<Args>(args)...);
@@ -54,12 +52,32 @@ namespace BAN
return *this;
}
T* ptr() const { return m_pointer; }
T& operator*() const { ASSERT(!empty()); return *ptr(); }
T* operator->() const { ASSERT(!empty()); return ptr(); }
T& operator*()
{
ASSERT(m_pointer);
return *m_pointer;
}
bool empty() const { return m_pointer == nullptr; }
explicit operator bool() const { return m_pointer; }
const T& operator*() const
{
ASSERT(m_pointer);
return *m_pointer;
}
T* operator->()
{
ASSERT(m_pointer);
return m_pointer;
}
const T* operator->() const
{
ASSERT(m_pointer);
return m_pointer;
}
T* ptr() { return m_pointer; }
const T* ptr() const { return m_pointer; }
void clear()
{
@@ -68,6 +86,8 @@ namespace BAN
m_pointer = nullptr;
}
operator bool() const { return m_pointer != nullptr; }
private:
T* m_pointer = nullptr;
@@ -75,13 +95,4 @@ namespace BAN
friend class UniqPtr;
};
template<typename T>
struct hash<UniqPtr<T>>
{
constexpr hash_t operator()(const UniqPtr<T>& ptr) const
{
return hash<T*>()(ptr.ptr());
}
};
}

View File

@@ -126,16 +126,14 @@ namespace BAN
Variant(Variant&& other)
: m_index(other.m_index)
{
if (other.has_value())
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
other.clear();
}
Variant(const Variant& other)
: m_index(other.m_index)
{
if (other.has_value())
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
}
template<typename T>
@@ -159,13 +157,12 @@ namespace BAN
Variant& operator=(Variant&& other)
{
if (m_index == other.m_index && m_index != invalid_index())
if (m_index == other.m_index)
detail::move_assign<Ts...>(m_index, other.m_storage, m_storage);
else
{
clear();
if (other.has_value())
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
detail::move_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index;
}
other.clear();
@@ -174,13 +171,12 @@ namespace BAN
Variant& operator=(const Variant& other)
{
if (m_index == other.m_index && m_index != invalid_index())
if (m_index == other.m_index)
detail::copy_assign<Ts...>(m_index, other.m_storage, m_storage);
else
{
clear();
if (other.has_value())
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
detail::copy_construct<Ts...>(other.m_index, other.m_storage, m_storage);
m_index = other.m_index;
}
return *this;
@@ -221,7 +217,7 @@ namespace BAN
}
template<typename T, typename... Args>
void emplace(Args&&... args) requires (can_have<T>() && is_constructible_v<T, Args...>)
void emplace(Args&&... args) requires (can_have<T>())
{
clear();
m_index = detail::index<T, Ts...>();

View File

@@ -35,9 +35,9 @@ namespace BAN
ErrorOr<void> push_back(T&&);
ErrorOr<void> push_back(const T&);
template<typename... Args>
ErrorOr<void> emplace_back(Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> emplace_back(Args&&...);
template<typename... Args>
ErrorOr<void> emplace(size_type, Args&&...) requires is_constructible_v<T, Args...>;
ErrorOr<void> emplace(size_type, Args&&...);
ErrorOr<void> insert(size_type, T&&);
ErrorOr<void> insert(size_type, const T&);
@@ -56,7 +56,7 @@ namespace BAN
bool contains(const T&) const;
Span<T> span() { return Span(m_data, m_size); }
Span<const T> span() const { return Span(m_data, m_size); }
const Span<T> span() const { return Span(m_data, m_size); }
const T& operator[](size_type) const;
T& operator[](size_type);
@@ -169,7 +169,7 @@ namespace BAN
template<typename T>
template<typename... Args>
ErrorOr<void> Vector<T>::emplace_back(Args&&... args) requires is_constructible_v<T, Args...>
ErrorOr<void> Vector<T>::emplace_back(Args&&... args)
{
TRY(ensure_capacity(m_size + 1));
new (m_data + m_size) T(forward<Args>(args)...);
@@ -179,7 +179,7 @@ namespace BAN
template<typename T>
template<typename... Args>
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args) requires is_constructible_v<T, Args...>
ErrorOr<void> Vector<T>::emplace(size_type index, Args&&... args)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1));
@@ -381,46 +381,19 @@ namespace BAN
template<typename T>
ErrorOr<void> Vector<T>::ensure_capacity(size_type size)
{
static_assert(alignof(T) <= alignof(max_align_t), "over aligned types not supported");
if (m_capacity >= size)
return {};
const size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
if constexpr (BAN::is_trivially_copyable_v<T>)
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
T* new_data = (T*)BAN::allocator(new_cap * sizeof(T));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
for (size_type i = 0; i < m_size; i++)
{
if constexpr (BAN::reallocator)
{
auto* new_data = static_cast<T*>(BAN::reallocator(m_data, new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
m_data = new_data;
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, m_data, m_size * sizeof(T));
BAN::deallocator(m_data);
m_data = new_data;
}
new (new_data + i) T(move(m_data[i]));
m_data[i].~T();
}
else
{
auto* new_data = static_cast<T*>(BAN::allocator(new_cap * sizeof(T)));
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
for (size_type i = 0; i < m_size; i++)
{
new (new_data + i) T(move(m_data[i]));
m_data[i].~T();
}
BAN::deallocator(m_data);
m_data = new_data;
}
BAN::deallocator(m_data);
m_data = new_data;
m_capacity = new_cap;
return {};
}

View File

@@ -20,7 +20,7 @@ namespace BAN
class WeakLink : public RefCounted<WeakLink<T>>
{
public:
RefPtr<T> try_lock() const
RefPtr<T> try_lock()
{
#if __is_kernel
Kernel::SpinLockGuard _(m_weak_lock);
@@ -44,7 +44,7 @@ namespace BAN
private:
T* m_ptr;
#if __is_kernel
mutable Kernel::SpinLock m_weak_lock;
Kernel::SpinLock m_weak_lock;
#endif
friend class RefPtr<WeakLink<T>>;
};
@@ -99,7 +99,7 @@ namespace BAN
return *this;
}
RefPtr<T> lock() const
RefPtr<T> lock()
{
if (m_link)
return m_link->try_lock();

View File

@@ -4,8 +4,12 @@ if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
endif ()
set(BANAN_ENABLE_SSE 1)
project(banan-os CXX C ASM)
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include)
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
@@ -25,16 +29,17 @@ set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(CMAKE_SHARED_LIBRARY_PREFIX "")
set(BUILD_SHARED_LIBS True)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
# include headers of ${library} to ${target}
function(banan_include_headers target library)
target_include_directories(${target} PUBLIC $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
target_include_directories(${target} PRIVATE $<TARGET_PROPERTY:${library},SOURCE_DIR>/include)
endfunction()
# include headers and link ${library} to ${target}
function(banan_link_library target library)
target_link_libraries(${target} PUBLIC ${library})
target_link_libraries(${target} PRIVATE ${library})
banan_include_headers(${target} ${library})
endfunction()
@@ -52,3 +57,8 @@ add_subdirectory(kernel)
add_subdirectory(bootloader)
add_subdirectory(BAN)
add_subdirectory(userspace)
add_custom_target(sysroot
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
)

View File

@@ -9,8 +9,6 @@ This is my hobby operating system written in C++. Currently supports x86\_64 and
You can find a live demo [here](https://bananymous.com/banan-os)
If you want to try out DOOM, you should first enter the GUI environment using the `start-gui` command. Then you can run `doom` in the GUI terminal.
### Features
#### General
@@ -22,8 +20,8 @@ If you want to try out DOOM, you should first enter the GUI environment using th
- [x] AML interpreter (partial)
- [x] Basic graphical environment
- [x] Terminal emulator
- [x] Status bar
- [x] Program launcher
- [ ] Task bar
- [ ] Program launcher
- [ ] Some nice apps
- [x] ELF dynamic linking
- [x] copy-on-write memory
@@ -34,18 +32,13 @@ If you want to try out DOOM, you should first enter the GUI environment using th
- [x] NVMe disks
- [x] ATA (IDE, SATA) disks
- [x] E1000 and E1000E NICs
- [x] RTL8111/8168/8211/8411 NICs
- [x] PS2 keyboard (all scancode sets)
- [x] PS2 mouse
- [x] USB
- [x] xHCI
- [ ] EHCI
- [ ] OHCI
- [ ] UHCI
- [x] Keyboard
- [x] Mouse
- [x] Mass storage
- [x] Hubs
- [ ] Hubs
- [ ] Mass storage
- [ ] ...
- [ ] virtio devices (network, storage)
@@ -118,8 +111,6 @@ To change the bootloader you can set environment variable BANAN\_BOOTLOADER; sup
To run with UEFI set environment variable BANAN\_UEFI\_BOOT=1. You will also have to set OVMF\_PATH to the correct OVMF (default */usr/share/ovmf/x64/OVMF.fd*).
To build an image with no physical root filesystem, but an initrd set environment variable BANAN\_INITRD=1. This can be used when testing on hardware with unsupported USB controller.
If you have corrupted your disk image or want to create new one, you can either manually delete *build/banan-os.img* and build system will automatically create you a new one or you can run the following command.
```sh
./bos image-full

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

View File

@@ -84,4 +84,4 @@ command_line:
# 100 character command line
command_line_buffer:
.ascii "root=/dev/sda2"
.skip 100 - (. - command_line_buffer)
.skip 100 - 28

View File

@@ -306,8 +306,20 @@ memset32:
movw $GDT_DATA32, %dx
movw %dx, %es
movl %ecx, %edx
andl $3, %ecx
rep stosb %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
movb %al, %ah
movw %ax, %dx
shll $16, %eax
movw %dx, %ax
rep stosl %es:(%edi)
ljmpl $GDT_CODE16, $.memset32_pmode16
.code16
@@ -358,8 +370,14 @@ memcpy32:
movw %dx, %ds
movw %dx, %es
movl %ecx, %edx
andl $3, %ecx
rep movsb %ds:(%esi), %es:(%edi)
movl %edx, %ecx
shrl $2, %ecx
rep movsl %ds:(%esi), %es:(%edi)
ljmpl $GDT_CODE16, $.memcpy32_pmode16
.code16

View File

@@ -1,16 +1,17 @@
set(KERNEL_SOURCES
font/prefs.psf.o
kernel/ACPI/ACPI.cpp
kernel/ACPI/AML.cpp
kernel/ACPI/AML/Field.cpp
kernel/ACPI/AML/Integer.cpp
kernel/ACPI/AML/NamedObject.cpp
kernel/ACPI/AML/Namespace.cpp
kernel/ACPI/AML/Node.cpp
kernel/ACPI/AML/OpRegion.cpp
kernel/ACPI/BatterySystem.cpp
kernel/ACPI/EmbeddedController.cpp
kernel/ACPI/AML/Package.cpp
kernel/ACPI/AML/Register.cpp
kernel/ACPI/AML/Scope.cpp
kernel/ACPI/AML/String.cpp
kernel/APIC.cpp
kernel/Audio/AC97/Controller.cpp
kernel/Audio/Controller.cpp
kernel/Audio/HDAudio/AudioFunctionGroup.cpp
kernel/Audio/HDAudio/Controller.cpp
kernel/BootInfo.cpp
kernel/CPUID.cpp
kernel/Credentials.cpp
@@ -22,10 +23,8 @@ set(KERNEL_SOURCES
kernel/Device/RandomDevice.cpp
kernel/Device/ZeroDevice.cpp
kernel/ELF.cpp
kernel/Epoll.cpp
kernel/Errors.cpp
kernel/FS/DevFS/FileSystem.cpp
kernel/FS/EventFD.cpp
kernel/FS/Ext2/FileSystem.cpp
kernel/FS/Ext2/Inode.cpp
kernel/FS/FAT/FileSystem.cpp
@@ -37,7 +36,6 @@ set(KERNEL_SOURCES
kernel/FS/ProcFS/Inode.cpp
kernel/FS/TmpFS/FileSystem.cpp
kernel/FS/TmpFS/Inode.cpp
kernel/FS/USTARModule.cpp
kernel/FS/VirtualFileSystem.cpp
kernel/GDT.cpp
kernel/IDT.cpp
@@ -50,8 +48,6 @@ set(KERNEL_SOURCES
kernel/Interruptable.cpp
kernel/InterruptController.cpp
kernel/kernel.cpp
kernel/Lock/SpinLock.cpp
kernel/Memory/ByteRingBuffer.cpp
kernel/Memory/DMARegion.cpp
kernel/Memory/FileBackedRegion.cpp
kernel/Memory/Heap.cpp
@@ -65,12 +61,10 @@ set(KERNEL_SOURCES
kernel/Networking/E1000/E1000.cpp
kernel/Networking/E1000/E1000E.cpp
kernel/Networking/IPv4Layer.cpp
kernel/Networking/Loopback.cpp
kernel/Networking/NetworkInterface.cpp
kernel/Networking/NetworkLayer.cpp
kernel/Networking/NetworkManager.cpp
kernel/Networking/NetworkSocket.cpp
kernel/Networking/RTL8169/RTL8169.cpp
kernel/Networking/TCPSocket.cpp
kernel/Networking/UDPSocket.cpp
kernel/Networking/UNIX/Socket.cpp
@@ -82,6 +76,7 @@ set(KERNEL_SOURCES
kernel/Processor.cpp
kernel/Random.cpp
kernel/Scheduler.cpp
kernel/ThreadBlocker.cpp
kernel/SSP.cpp
kernel/Storage/ATA/AHCI/Controller.cpp
kernel/Storage/ATA/AHCI/Device.cpp
@@ -93,31 +88,22 @@ set(KERNEL_SOURCES
kernel/Storage/NVMe/Namespace.cpp
kernel/Storage/NVMe/Queue.cpp
kernel/Storage/Partition.cpp
kernel/Storage/SCSI.cpp
kernel/Storage/StorageDevice.cpp
kernel/Syscall.cpp
kernel/Terminal/FramebufferTerminal.cpp
kernel/Terminal/PseudoTerminal.cpp
kernel/Terminal/Serial.cpp
kernel/Terminal/TerminalDriver.cpp
kernel/Terminal/TextModeTerminal.cpp
kernel/Terminal/TTY.cpp
kernel/Terminal/VirtualTTY.cpp
kernel/Thread.cpp
kernel/ThreadBlocker.cpp
kernel/Timer/HPET.cpp
kernel/Timer/PIT.cpp
kernel/Timer/RTC.cpp
kernel/Timer/Timer.cpp
kernel/USB/Controller.cpp
kernel/USB/Device.cpp
kernel/USB/HID/HIDDriver.cpp
kernel/USB/HID/Joystick.cpp
kernel/USB/HID/Keyboard.cpp
kernel/USB/HID/Mouse.cpp
kernel/USB/Hub/HubDriver.cpp
kernel/USB/MassStorage/MassStorageDriver.cpp
kernel/USB/MassStorage/SCSIDevice.cpp
kernel/USB/USBManager.cpp
kernel/USB/XHCI/Controller.cpp
kernel/USB/XHCI/Device.cpp
@@ -139,8 +125,6 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
arch/x86_64/Signal.S
arch/x86_64/Syscall.S
arch/x86_64/Thread.S
arch/x86_64/User.S
arch/x86_64/Yield.S
)
elseif("${BANAN_ARCH}" STREQUAL "i686")
set(KERNEL_SOURCES
@@ -151,8 +135,6 @@ elseif("${BANAN_ARCH}" STREQUAL "i686")
arch/i686/Signal.S
arch/i686/Syscall.S
arch/i686/Thread.S
arch/i686/User.S
arch/i686/Yield.S
)
else()
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
@@ -168,13 +150,6 @@ set(BAN_SOURCES
set(KLIBC_SOURCES
klibc/ctype.cpp
klibc/string.cpp
klibc/arch/${BANAN_ARCH}/string.S
)
set(LIBDEFLATE_SOURCE
../userspace/libraries/LibDEFLATE/Compressor.cpp
../userspace/libraries/LibDEFLATE/Decompressor.cpp
../userspace/libraries/LibDEFLATE/HuffmanTree.cpp
)
set(LIBFONT_SOURCES
@@ -187,25 +162,19 @@ set(LIBINPUT_SOURCE
../userspace/libraries/LibInput/KeyEvent.cpp
)
set(LIBQR_SOURCE
../userspace/libraries/LibQR/QRCode.cpp
)
set(KERNEL_SOURCES
${KERNEL_SOURCES}
${BAN_SOURCES}
${KLIBC_SOURCES}
${LIBDEFLATE_SOURCE}
${LIBFONT_SOURCES}
${LIBINPUT_SOURCE}
${LIBQR_SOURCE}
)
add_executable(kernel ${KERNEL_SOURCES})
target_compile_definitions(kernel PRIVATE __is_kernel)
target_compile_definitions(kernel PRIVATE __arch=${BANAN_ARCH})
target_compile_definitions(kernel PRIVATE LIBDEFLATE_AVOID_STACK=1)
target_compile_definitions(kernel PRIVATE __enable_sse=${BANAN_ENABLE_SSE})
target_compile_options(kernel PRIVATE
-O2 -g
@@ -259,11 +228,9 @@ add_custom_command(
banan_include_headers(kernel ban)
banan_include_headers(kernel libc)
banan_include_headers(kernel libdeflate)
banan_include_headers(kernel libelf)
banan_include_headers(kernel libfont)
banan_include_headers(kernel libelf)
banan_include_headers(kernel libinput)
banan_include_headers(kernel libqr)
banan_install_headers(kernel)
set_target_properties(kernel PROPERTIES OUTPUT_NAME banan-os.kernel)
@@ -277,7 +244,7 @@ endif()
add_custom_command(
OUTPUT font/prefs.psf.o
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/font && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && objcopy -O ${ELF_FORMAT} -B i386 -I binary font/prefs.psf ${CMAKE_CURRENT_BINARY_DIR}/font/prefs.psf.o
)
set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_COMPILER} <CMAKE_CXX_LINK_FLAGS> <FLAGS> <LINK_FLAGS> -o <TARGET> ${CMAKE_CURRENT_BINARY_DIR}/crti.o ${CMAKE_CURRENT_BINARY_DIR}/crtbegin.o <OBJECTS> ${CMAKE_CURRENT_BINARY_DIR}/crtend.o ${CMAKE_CURRENT_BINARY_DIR}/crtn.o -lgcc ")

View File

@@ -16,18 +16,11 @@ extern uint8_t g_kernel_writable_end[];
extern uint8_t g_userspace_start[];
extern uint8_t g_userspace_end[];
extern uint64_t g_boot_fast_page_pt[];
namespace Kernel
{
SpinLock PageTable::s_fast_page_lock;
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
static bool s_is_initialized = false;
static PageTable* s_kernel = nullptr;
static bool s_has_nxe = false;
static bool s_has_pge = false;
@@ -35,28 +28,6 @@ namespace Kernel
static paddr_t s_global_pdpte = 0;
static uint64_t* s_fast_page_pt { nullptr };
static uint64_t* allocate_zeroed_page_aligned_page()
{
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
ASSERT(page);
memset(page, 0, PAGE_SIZE);
return (uint64_t*)page;
}
template<typename T>
static paddr_t V2P(const T vaddr)
{
return (vaddr_t)vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
}
template<typename T>
static uint64_t* P2V(const T paddr)
{
return reinterpret_cast<uint64_t*>(reinterpret_cast<paddr_t>(paddr) - g_boot_info.kernel_paddr + KERNEL_OFFSET);
}
static inline PageTable::flags_t parse_flags(uint64_t entry)
{
using Flags = PageTable::Flags;
@@ -75,22 +46,26 @@ namespace Kernel
return result;
}
void PageTable::initialize_fast_page()
{
s_fast_page_pt = g_boot_fast_page_pt;
}
static void detect_cpu_features()
void PageTable::initialize()
{
if (CPUID::has_nxe())
s_has_nxe = true;
if (CPUID::has_pge())
s_has_pge = true;
if (CPUID::has_pat())
s_has_pat = true;
ASSERT(s_kernel == nullptr);
s_kernel = new PageTable();
ASSERT(s_kernel);
s_kernel->initialize_kernel();
s_kernel->initial_load();
}
void PageTable::enable_cpu_features()
void PageTable::initial_load()
{
if (s_has_nxe)
{
@@ -131,56 +106,8 @@ namespace Kernel
"movl %%eax, %%cr0;"
::: "rax"
);
}
void PageTable::initialize_and_load()
{
detect_cpu_features();
enable_cpu_features();
ASSERT(s_kernel == nullptr);
s_kernel = new PageTable();
ASSERT(s_kernel);
auto* pdpt = allocate_zeroed_page_aligned_page();
ASSERT(pdpt);
s_kernel->m_highest_paging_struct = V2P(pdpt);
s_kernel->map_kernel_memory();
PageTable::with_fast_page(s_kernel->m_highest_paging_struct, [] {
s_global_pdpte = PageTable::fast_page_as_sized<paddr_t>(3);
});
// update fast page pt
{
constexpr vaddr_t vaddr = fast_page();
constexpr uint16_t pdpte = (vaddr >> 30) & 0x1FF;
constexpr uint16_t pde = (vaddr >> 21) & 0x1FF;
const auto get_or_allocate_entry =
[](paddr_t table_paddr, uint16_t entry, uint64_t flags)
{
uint64_t* table = P2V(table_paddr);
if (!(table[entry] & Flags::Present))
{
auto* vaddr = allocate_zeroed_page_aligned_page();
ASSERT(vaddr);
table[entry] = V2P(vaddr);
}
table[entry] |= flags;
return table[entry] & s_page_addr_mask;
};
const paddr_t pdpt = s_kernel->m_highest_paging_struct;
const paddr_t pd = get_or_allocate_entry(pdpt, pdpte, Flags::Present);
s_fast_page_pt = P2V(get_or_allocate_entry(pd, pde, Flags::ReadWrite | Flags::Present));
}
s_kernel->load();
load();
}
PageTable& PageTable::kernel()
@@ -194,12 +121,48 @@ namespace Kernel
return true;
}
void PageTable::map_kernel_memory()
static uint64_t* allocate_zeroed_page_aligned_page()
{
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
ASSERT(page);
memset(page, 0, PAGE_SIZE);
return (uint64_t*)page;
}
template<typename T>
static paddr_t V2P(const T vaddr)
{
return (vaddr_t)vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
}
template<typename T>
static vaddr_t P2V(const T paddr)
{
return (paddr_t)paddr - g_boot_info.kernel_paddr + KERNEL_OFFSET;
}
void PageTable::initialize_kernel()
{
ASSERT(s_global_pdpte == 0);
s_global_pdpte = V2P(allocate_zeroed_page_aligned_page());
map_kernel_memory();
prepare_fast_page();
// Map main bios area below 1 MiB
map_range_at(
0x000E0000,
P2V(0x000E0000),
0x00100000 - 0x000E0000,
PageTable::Flags::Present
);
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
ASSERT((vaddr_t)g_kernel_start % PAGE_SIZE == 0);
map_range_at(
V2P(g_kernel_start),
reinterpret_cast<vaddr_t>(g_kernel_start),
(vaddr_t)g_kernel_start,
g_kernel_end - g_kernel_start,
Flags::Present
);
@@ -207,7 +170,7 @@ namespace Kernel
// Map executable kernel memory as executable
map_range_at(
V2P(g_kernel_execute_start),
reinterpret_cast<vaddr_t>(g_kernel_execute_start),
(vaddr_t)g_kernel_execute_start,
g_kernel_execute_end - g_kernel_execute_start,
Flags::Execute | Flags::Present
);
@@ -215,7 +178,7 @@ namespace Kernel
// Map writable kernel memory as writable
map_range_at(
V2P(g_kernel_writable_start),
reinterpret_cast<vaddr_t>(g_kernel_writable_start),
(vaddr_t)g_kernel_writable_start,
g_kernel_writable_end - g_kernel_writable_start,
Flags::ReadWrite | Flags::Present
);
@@ -223,34 +186,69 @@ namespace Kernel
// Map userspace memory
map_range_at(
V2P(g_userspace_start),
reinterpret_cast<vaddr_t>(g_userspace_start),
(vaddr_t)g_userspace_start,
g_userspace_end - g_userspace_start,
Flags::Execute | Flags::UserSupervisor | Flags::Present
);
}
void PageTable::prepare_fast_page()
{
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
ASSERT(pdpt[pdpte] & Flags::Present);
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte]) & PAGE_ADDR_MASK);
ASSERT(!(pd[pde] & Flags::Present));
pd[pde] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde]) & PAGE_ADDR_MASK);
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = V2P(allocate_zeroed_page_aligned_page());
}
void PageTable::map_fast_page(paddr_t paddr)
{
ASSERT(paddr && paddr % PAGE_SIZE == 0);
ASSERT(s_kernel);
ASSERT(paddr);
ASSERT(s_fast_page_pt);
ASSERT(s_fast_page_lock.current_processor_has_lock());
ASSERT(!(*s_fast_page_pt & Flags::Present));
s_fast_page_pt[0] = paddr | Flags::ReadWrite | Flags::Present;
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
asm volatile("invlpg (%0)" :: "r"(fast_page()));
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
invalidate(fast_page(), false);
}
void PageTable::unmap_fast_page()
{
ASSERT(s_fast_page_pt);
ASSERT(s_kernel);
ASSERT(s_fast_page_lock.current_processor_has_lock());
ASSERT((*s_fast_page_pt & Flags::Present));
s_fast_page_pt[0] = 0;
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
asm volatile("invlpg (%0)" :: "r"(fast_page()));
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(s_kernel->m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
ASSERT(pt[pte] & Flags::Present);
pt[pte] = 0;
invalidate(fast_page(), false);
}
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
@@ -259,41 +257,41 @@ namespace Kernel
PageTable* page_table = new PageTable;
if (page_table == nullptr)
return BAN::Error::from_errno(ENOMEM);
page_table->map_kernel_memory();
return page_table;
}
uint64_t* pdpt = allocate_zeroed_page_aligned_page();
if (pdpt == nullptr)
{
delete page_table;
return BAN::Error::from_errno(ENOMEM);
}
void PageTable::map_kernel_memory()
{
ASSERT(s_kernel);
ASSERT(s_global_pdpte);
page_table->m_highest_paging_struct = V2P(pdpt);
ASSERT(m_highest_paging_struct == 0);
m_highest_paging_struct = V2P(kmalloc(32, 32, true));
ASSERT(m_highest_paging_struct);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
pdpt[0] = 0;
pdpt[1] = 0;
pdpt[2] = 0;
pdpt[3] = s_global_pdpte | Flags::Present;
static_assert(KERNEL_OFFSET == 0xC0000000);
return page_table;
}
PageTable::~PageTable()
{
if (m_highest_paging_struct == 0)
return;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
uint64_t* pdpt = P2V(m_highest_paging_struct);
for (uint32_t pdpte = 0; pdpte < 3; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
continue;
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
for (uint32_t pde = 0; pde < 512; pde++)
{
if (!(pd[pde] & Flags::Present))
continue;
kfree(P2V(pd[pde] & s_page_addr_mask));
kfree(reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK)));
}
kfree(pd);
}
@@ -304,43 +302,15 @@ namespace Kernel
{
SpinLockGuard _(m_lock);
ASSERT(m_highest_paging_struct < 0x100000000);
asm volatile("movl %0, %%cr3" :: "r"(static_cast<uint32_t>(m_highest_paging_struct)));
const uint32_t pdpt_lo = m_highest_paging_struct;
asm volatile("movl %0, %%cr3" :: "r"(pdpt_lo));
Processor::set_current_page_table(this);
}
void PageTable::invalidate_range(vaddr_t vaddr, size_t pages, bool send_smp_message)
void PageTable::invalidate(vaddr_t vaddr, bool send_smp_message)
{
ASSERT(vaddr % PAGE_SIZE == 0);
const bool is_userspace = (vaddr < KERNEL_OFFSET);
if (is_userspace && this != &PageTable::current())
;
else if (pages <= 32 || !s_is_initialized)
{
for (size_t i = 0; i < pages; i++, vaddr += PAGE_SIZE)
asm volatile("invlpg (%0)" :: "r"(vaddr));
}
else if (is_userspace || !s_has_pge)
{
asm volatile("movl %0, %%cr3" :: "r"(static_cast<uint32_t>(m_highest_paging_struct)));
}
else
{
asm volatile(
"movl %%cr4, %%eax;"
"andl $~0x80, %%eax;"
"movl %%eax, %%cr4;"
"movl %0, %%cr3;"
"orl $0x80, %%eax;"
"movl %%eax, %%cr4;"
:
: "r"(static_cast<uint32_t>(m_highest_paging_struct))
: "eax"
);
}
asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
if (send_smp_message)
{
@@ -348,14 +318,13 @@ namespace Kernel
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = pages,
.page_table = vaddr < KERNEL_OFFSET ? this : nullptr,
.page_count = 1
}
});
}
}
void PageTable::unmap_page(vaddr_t vaddr, bool invalidate)
void PageTable::unmap_page(vaddr_t vaddr, bool send_smp_message)
{
ASSERT(vaddr);
ASSERT(vaddr % PAGE_SIZE == 0);
@@ -374,16 +343,12 @@ namespace Kernel
if (is_page_free(vaddr))
Kernel::panic("trying to unmap unmapped page 0x{H}", vaddr);
uint64_t* pdpt = P2V(m_highest_paging_struct);
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
const paddr_t old_paddr = pt[pte] & s_page_addr_mask;
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
pt[pte] = 0;
if (invalidate && old_paddr != 0)
invalidate_page(vaddr, true);
invalidate(vaddr, send_smp_message);
}
void PageTable::unmap_range(vaddr_t vaddr, size_t size)
@@ -395,10 +360,17 @@ namespace Kernel
SpinLockGuard _(m_lock);
for (vaddr_t page = 0; page < page_count; page++)
unmap_page(vaddr + page * PAGE_SIZE, false);
invalidate_range(vaddr, page_count, true);
Processor::broadcast_smp_message({
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
}
});
}
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool invalidate)
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type, bool send_smp_message)
{
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
@@ -433,11 +405,11 @@ namespace Kernel
SpinLockGuard _(m_lock);
uint64_t* pdpt = P2V(m_highest_paging_struct);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
if (!(pdpt[pdpte] & Flags::Present))
pdpt[pdpte] = V2P(allocate_zeroed_page_aligned_page()) | Flags::Present;
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
if ((pd[pde] & uwr_flags) != uwr_flags)
{
if (!(pd[pde] & Flags::Present))
@@ -448,14 +420,10 @@ namespace Kernel
if (!(flags & Flags::Present))
uwr_flags &= ~Flags::Present;
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
const paddr_t old_paddr = pt[pte] & s_page_addr_mask;
uint64_t* pt = reinterpret_cast<uint64_t*>(P2V(pd[pde] & PAGE_ADDR_MASK));
pt[pte] = paddr | uwr_flags | extra_flags;
if (invalidate && old_paddr != 0)
invalidate_page(vaddr, true);
invalidate(vaddr, send_smp_message);
}
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
@@ -469,49 +437,14 @@ namespace Kernel
SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type, false);
invalidate_range(vaddr, page_count, true);
}
void PageTable::remove_writable_from_range(vaddr_t vaddr, size_t size)
{
ASSERT(vaddr);
ASSERT(vaddr % PAGE_SIZE == 0);
uint32_t pdpte = (vaddr >> 30) & 0x1FF;
uint32_t pde = (vaddr >> 21) & 0x1FF;
uint32_t pte = (vaddr >> 12) & 0x1FF;
const uint32_t e_pdpte = ((vaddr + size - 1) >> 30) & 0x1FF;
const uint32_t e_pde = ((vaddr + size - 1) >> 21) & 0x1FF;
const uint32_t e_pte = ((vaddr + size - 1) >> 12) & 0x1FF;
SpinLockGuard _(m_lock);
const uint64_t* pdpt = P2V(m_highest_paging_struct);
for (; pdpte <= e_pdpte; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
continue;
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
for (; pde < 512; pde++)
{
if (pdpte == e_pdpte && pde > e_pde)
break;
if (!(pd[pde] & Flags::ReadWrite))
continue;
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
for (; pte < 512; pte++)
{
if (pdpte == e_pdpte && pde == e_pde && pte > e_pte)
break;
pt[pte] &= ~static_cast<uint64_t>(Flags::ReadWrite);
}
pte = 0;
Processor::broadcast_smp_message({
.type = Processor::SMPMessage::Type::FlushTLB,
.flush_tlb = {
.vaddr = vaddr,
.page_count = page_count
}
pde = 0;
}
invalidate_range(vaddr, size / PAGE_SIZE, true);
});
}
uint64_t PageTable::get_page_data(vaddr_t vaddr) const
@@ -524,15 +457,15 @@ namespace Kernel
SpinLockGuard _(m_lock);
const uint64_t* pdpt = P2V(m_highest_paging_struct);
uint64_t* pdpt = (uint64_t*)P2V(m_highest_paging_struct);
if (!(pdpt[pdpte] & Flags::Present))
return 0;
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
if (!(pd[pde] & Flags::Present))
return 0;
const uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
if (!(pt[pte] & Flags::Used))
return 0;
@@ -546,7 +479,8 @@ namespace Kernel
paddr_t PageTable::physical_address_of(vaddr_t vaddr) const
{
return get_page_data(vaddr) & s_page_addr_mask;
uint64_t page_data = get_page_data(vaddr);
return (page_data & PAGE_ADDR_MASK) & ~(1ull << 63);
}
bool PageTable::is_page_free(vaddr_t vaddr) const
@@ -567,13 +501,13 @@ namespace Kernel
return true;
}
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free, bool send_smp_message)
bool PageTable::reserve_page(vaddr_t vaddr, bool only_free)
{
SpinLockGuard _(m_lock);
ASSERT(vaddr % PAGE_SIZE == 0);
if (only_free && !is_page_free(vaddr))
return false;
map_page_at(0, vaddr, Flags::Reserved, MemoryType::Normal, send_smp_message);
map_page_at(0, vaddr, Flags::Reserved);
return true;
}
@@ -587,9 +521,7 @@ namespace Kernel
if (only_free && !is_range_free(vaddr, bytes))
return false;
for (size_t offset = 0; offset < bytes; offset += PAGE_SIZE)
reserve_page(vaddr + offset, true, false);
invalidate_range(vaddr, bytes / PAGE_SIZE, true);
reserve_page(vaddr + offset);
return true;
}
@@ -602,47 +534,48 @@ namespace Kernel
if (size_t rem = last_address % PAGE_SIZE)
last_address -= rem;
uint32_t pdpte = (first_address >> 30) & 0x1FF;
uint32_t pde = (first_address >> 21) & 0x1FF;
uint32_t pte = (first_address >> 12) & 0x1FF;
const uint32_t s_pdpte = (first_address >> 30) & 0x1FF;
const uint32_t s_pde = (first_address >> 21) & 0x1FF;
const uint32_t s_pte = (first_address >> 12) & 0x1FF;
const uint32_t e_pdpte = ((last_address - 1) >> 30) & 0x1FF;
const uint32_t e_pde = ((last_address - 1) >> 21) & 0x1FF;
const uint32_t e_pte = ((last_address - 1) >> 12) & 0x1FF;
const uint32_t e_pdpte = (last_address >> 30) & 0x1FF;
const uint32_t e_pde = (last_address >> 21) & 0x1FF;
const uint32_t e_pte = (last_address >> 12) & 0x1FF;
SpinLockGuard _(m_lock);
// Try to find free page that can be mapped without
// allocations (page table with unused entries)
const uint64_t* pdpt = P2V(m_highest_paging_struct);
for (; pdpte <= e_pdpte; pdpte++)
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
for (uint32_t pdpte = s_pdpte; pdpte < 4; pdpte++)
{
if (pdpte > e_pdpte)
break;
if (!(pdpt[pdpte] & Flags::Present))
continue;
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
for (; pde < 512; pde++)
uint64_t* pd = reinterpret_cast<uint64_t*>(P2V(pdpt[pdpte] & PAGE_ADDR_MASK));
for (uint32_t pde = s_pde; pde < 512; pde++)
{
if (pdpte == e_pdpte && pde > e_pde)
break;
if (!(pd[pde] & Flags::Present))
continue;
const uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
for (; pte < 512; pte++)
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
for (uint32_t pte = s_pte; pte < 512; pte++)
{
if (pdpte == e_pdpte && pde == e_pde && pte > e_pte)
if (pdpte == e_pdpte && pde == e_pde && pte >= e_pte)
break;
if (pt[pte] & Flags::Used)
continue;
vaddr_t vaddr = 0;
vaddr |= (vaddr_t)pdpte << 30;
vaddr |= (vaddr_t)pde << 21;
vaddr |= (vaddr_t)pte << 12;
ASSERT(reserve_page(vaddr));
return vaddr;
if (!(pt[pte] & Flags::Used))
{
vaddr_t vaddr = 0;
vaddr |= (vaddr_t)pdpte << 30;
vaddr |= (vaddr_t)pde << 21;
vaddr |= (vaddr_t)pte << 12;
ASSERT(reserve_page(vaddr));
return vaddr;
}
}
pte = 0;
}
pde = 0;
}
// Find any free page
@@ -655,7 +588,7 @@ namespace Kernel
}
}
return 0;
ASSERT_NOT_REACHED();
}
vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address)
@@ -688,7 +621,7 @@ namespace Kernel
}
}
return 0;
ASSERT_NOT_REACHED();
}
static void dump_range(vaddr_t start, vaddr_t end, PageTable::flags_t flags)
@@ -711,7 +644,7 @@ namespace Kernel
flags_t flags = 0;
vaddr_t start = 0;
const uint64_t* pdpt = P2V(m_highest_paging_struct);
uint64_t* pdpt = reinterpret_cast<uint64_t*>(P2V(m_highest_paging_struct));
for (uint32_t pdpte = 0; pdpte < 4; pdpte++)
{
if (!(pdpt[pdpte] & Flags::Present))
@@ -720,7 +653,7 @@ namespace Kernel
start = 0;
continue;
}
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
for (uint64_t pde = 0; pde < 512; pde++)
{
if (!(pd[pde] & Flags::Present))
@@ -729,7 +662,7 @@ namespace Kernel
start = 0;
continue;
}
const uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
for (uint64_t pte = 0; pte < 512; pte++)
{
if (parse_flags(pt[pte]) != flags)

View File

@@ -1,86 +1,35 @@
.section .userspace, "ax"
// stack contains
// (4 bytes) return address (on return stack)
// (4 bytes) return stack
// (4 bytes) return rflags
// (8 bytes) restore sigmask
// (36 bytes) siginfo_t
// (4 bytes) signal number
// (4 bytes) signal handler
// return address
// signal number
// signal handler
.global signal_trampoline
signal_trampoline:
pushl %esi // gregs
pushl %edi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
pushl %ebp
movl %esp, %ebp
movl 84(%esp), %eax
pushl %eax; addl $4, (%esp)
pushl (%eax)
pusha
// FIXME: populate these
xorl %eax, %eax
pushl %eax // stack
pushl %eax
pushl %eax
pushl %eax // sigset
pushl %eax
pushl %eax // link
movl %esp, %edx // ucontext
leal 68(%esp), %esi // siginfo
movl 64(%esp), %edi // signal number
movl 60(%esp), %eax // handlers
movl 40(%esp), %edi
movl 36(%esp), %eax
// align stack to 16 bytes
movl %esp, %ebp
andl $-16, %esp
movl %esp, %ebx
andl $0x0F, %ebx
subl %ebx, %esp
subl $512, %esp
fxsave (%esp)
subl $4, %esp
pushl %edx
pushl %esi
subl $12, %esp
pushl %edi
call *%eax
addl $16, %esp
fxrstor (%esp)
addl $512, %esp
// restore stack
movl %ebp, %esp
addl $24, %esp
addl %ebx, %esp
popa
// restore sigmask
movl $83, %eax // SYS_SIGPROCMASK
movl $3, %ebx // SIG_SETMASK
leal 72(%esp), %ecx // set
xorl %edx, %edx // oset
int $0xF0
// restore registers
leave
addl $8, %esp
popl %ebp
popl %eax
popl %ebx
popl %ecx
popl %edx
popl %edi
popl %esi
// skip handler, number, siginfo_t, sigmask
addl $52, %esp
// restore flags
popf
movl (%esp), %esp
ret

View File

@@ -1,6 +1,12 @@
// arguments in EAX, EBX, ECX, EDX, ESI, EDI
.global asm_syscall_handler
asm_syscall_handler:
# save segment registers
pushw %ds
pushw %es
pushw %fs
pushw %gs
# save general purpose registers
pushl %ebx
pushl %ecx
@@ -8,14 +14,16 @@ asm_syscall_handler:
pushl %edi
pushl %esi
pushl %ebp
cld
# align stack
movl %esp, %ebp
andl $-16, %esp
subl $15, %esp
andl $0xFFFFFFF0, %esp
# push arguments
subl $8, %esp
subl $4, %esp
pushl %ebp
addl $32, (%esp)
pushl %edi
pushl %esi
pushl %edx
@@ -35,15 +43,6 @@ asm_syscall_handler:
movl %ebp, %esp
# restore userspace segments
movw $(0x20 | 3), %bx
movw %bx, %ds
movw %bx, %es
movw $(0x30 | 3), %bx
movw %bx, %fs
movw $(0x38 | 3), %bx
movw %bx, %gs
# restore general purpose registers
popl %ebp
popl %esi
@@ -52,6 +51,12 @@ asm_syscall_handler:
popl %ecx
popl %ebx
# restore segment registers
popw %gs
popw %fs
popw %es
popw %ds
iret
.global sys_fork_trampoline
@@ -63,7 +68,7 @@ sys_fork_trampoline:
call read_ip
testl %eax, %eax
jz .done
jz .reload_stack
movl %esp, %ebx
@@ -79,3 +84,9 @@ sys_fork_trampoline:
popl %ebx
popl %ebp
ret
.reload_stack:
call get_thread_start_sp
movl %eax, %esp
xorl %eax, %eax
jmp .done

View File

@@ -7,6 +7,9 @@ read_ip:
# void start_kernel_thread()
.global start_kernel_thread
start_kernel_thread:
call get_thread_start_sp
movl %eax, %esp
# STACK LAYOUT
# on_exit arg
# on_exit func
@@ -28,15 +31,39 @@ start_kernel_thread:
subl $12, %esp
pushl %edi
call *%esi
addl $16, %esp
.global start_userspace_thread
start_userspace_thread:
call get_thread_start_sp
movl %eax, %esp
# STACK LAYOUT
# entry
# argc
# argv
# envp
# userspace stack
call get_userspace_thread_stack_top
movw $(0x20 | 3), %bx
movw %bx, %ds
movw %bx, %es
movw $(0x30 | 3), %bx
movw %bx, %fs
movw $(0x38 | 3), %bx
movw %bx, %gs
xorw %bx, %bx
popl %edi
popl %esi
popl %edx
popl %ecx
popl %ebx
pushl $(0x20 | 3)
pushl %eax
pushl $0x202
pushl $(0x18 | 3)
pushl %ebx
iret

View File

@@ -1,54 +0,0 @@
# bool safe_user_memcpy(void*, const void*, size_t)
.global safe_user_memcpy
.global safe_user_memcpy_end
.global safe_user_memcpy_fault
safe_user_memcpy:
xorl %eax, %eax
xchgl 4(%esp), %edi
xchgl 8(%esp), %esi
movl 12(%esp), %ecx
movl %edi, %edx
rep movsb
movl 4(%esp), %edi
movl 8(%esp), %esi
incl %eax
safe_user_memcpy_fault:
ret
safe_user_memcpy_end:
# bool safe_user_strncpy(void*, const void*, size_t)
.global safe_user_strncpy
.global safe_user_strncpy_end
.global safe_user_strncpy_fault
safe_user_strncpy:
xchgl 4(%esp), %edi
xchgl 8(%esp), %esi
movl 12(%esp), %ecx
testl %ecx, %ecx
jz safe_user_strncpy_fault
.safe_user_strncpy_loop:
movb (%esi), %al
movb %al, (%edi)
testb %al, %al
jz .safe_user_strncpy_done
incl %edi
incl %esi
decl %ecx
jnz .safe_user_strncpy_loop
safe_user_strncpy_fault:
xorl %eax, %eax
jmp .safe_user_strncpy_return
.safe_user_strncpy_done:
movl $1, %eax
.safe_user_strncpy_return:
movl 4(%esp), %edi
movl 8(%esp), %esi
ret
safe_user_strncpy_end:

View File

@@ -1,25 +0,0 @@
.global asm_yield_trampoline
asm_yield_trampoline:
leal 4(%esp), %ecx
movl 4(%esp), %esp
pushl -4(%ecx)
pushl %ecx
pushl %eax
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
pushl %esp
call scheduler_on_yield
addl $4, %esp
popl %ebp
popl %edi
popl %esi
popl %ebx
popl %eax
movl 4(%esp), %ecx
movl 0(%esp), %esp
jmp *%ecx

View File

@@ -11,28 +11,9 @@
.code32
// video mode info, page align modules
.set multiboot_flags, (1 << 2) | (1 << 0)
# multiboot2 header
.section .multiboot, "aw"
multiboot_start:
.long 0x1BADB002
.long multiboot_flags
.long -(0x1BADB002 + multiboot_flags)
.long 0
.long 0
.long 0
.long 0
.long 0
.long 0
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
multiboot_end:
.section .multiboot2, "aw"
.align 8
multiboot2_start:
.long 0xE85250D6
.long 0
@@ -55,12 +36,6 @@ multiboot2_start:
.long 12
.long V2P(_start)
# page align modules
.align 8
.short 6
.short 0
.long 8
.align 8
.short 0
.short 0
@@ -68,6 +43,7 @@ multiboot2_start:
multiboot2_end:
.section .bananboot, "aw"
.align 8
bananboot_start:
.long 0xBABAB007
.long -(0xBABAB007 + FB_WIDTH + FB_HEIGHT + FB_BPP)
@@ -77,10 +53,10 @@ bananboot_start:
bananboot_end:
.section .bss, "aw", @nobits
.global g_boot_stack_top
g_boot_stack_bottom:
.align 4096
boot_stack_bottom:
.skip 4096 * 4
g_boot_stack_top:
boot_stack_top:
.global g_kernel_cmdline
g_kernel_cmdline:
@@ -98,7 +74,8 @@ bananboot_end:
boot_pdpt:
.long V2P(boot_pd) + (PG_PRESENT)
.long 0
.skip 2 * 8
.quad 0
.quad 0
.long V2P(boot_pd) + (PG_PRESENT)
.long 0
.align 4096
@@ -111,16 +88,13 @@ boot_pd:
.endr
boot_pts:
.set i, 0
.rept 511
.rept 512
.rept 512
.long i + (PG_READ_WRITE | PG_PRESENT)
.long 0
.set i, i + 0x1000
.endr
.endr
.global g_boot_fast_page_pt
g_boot_fast_page_pt:
.skip 512 * 8
boot_gdt:
.quad 0x0000000000000000 # null descriptor
@@ -187,13 +161,6 @@ enable_sse:
movl %eax, %cr4
ret
enable_tsc:
# allow userspace to use RDTSC
movl %cr4, %ecx
andl $0xFFFFFFFB, %ecx
movl %ecx, %cr4
ret
initialize_paging:
# enable PAE
movl %cr4, %ecx
@@ -221,7 +188,7 @@ _start:
movl %ebx, V2P(bootloader_info)
# load boot stack
movl $V2P(g_boot_stack_top), %esp
movl $V2P(boot_stack_top), %esp
# load boot GDT
lgdt V2P(boot_gdtr)
@@ -236,11 +203,10 @@ gdt_flush:
# do processor initialization
call check_requirements
call enable_sse
call enable_tsc
call initialize_paging
# load higher half stack pointer
movl $g_boot_stack_top, %esp
movl $boot_stack_top, %esp
# jump to higher half
leal higher_half, %ecx
@@ -276,7 +242,7 @@ system_halt:
jmp 1b
#define AP_REL(vaddr) ((vaddr) - ap_trampoline + 0xF000)
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
.section .ap_init, "ax"
@@ -286,27 +252,21 @@ ap_trampoline:
jmp 1f
.align 8
ap_stack_paddr:
.skip 4
ap_stack_vaddr:
.skip 4
ap_prepare_paging:
.skip 4
ap_page_table:
.skip 4
ap_ready:
ap_stack_ptr:
.skip 4
ap_stack_loaded:
.skip 1
1: cli; cld
ljmpl $0x00, $AP_REL(ap_cs_clear)
ljmpl $0x00, $AP_V2P(ap_cs_clear)
ap_cs_clear:
# load ap gdt and enter protected mode
lgdt AP_REL(ap_gdtr)
lgdt AP_V2P(ap_gdtr)
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $0x08, $AP_REL(ap_protected_mode)
ljmpl $0x08, $AP_V2P(ap_protected_mode)
.code32
ap_protected_mode:
@@ -315,36 +275,32 @@ ap_protected_mode:
movw %ax, %ss
movw %ax, %es
movl AP_REL(ap_stack_paddr), %esp
movl AP_V2P(ap_stack_ptr), %esp
movb $1, AP_V2P(ap_stack_loaded)
leal V2P(enable_sse), %ecx; call *%ecx
leal V2P(enable_tsc), %ecx; call *%ecx
leal V2P(initialize_paging), %ecx; call *%ecx
# load boot gdt and enter long mode
lgdt V2P(boot_gdtr)
ljmpl $0x08, $AP_REL(ap_flush_gdt)
ljmpl $0x08, $AP_V2P(ap_flush_gdt)
ap_flush_gdt:
movl $ap_higher_half, %ecx
# move stack pointer to higher half
movl %esp, %esp
addl $KERNEL_OFFSET, %esp
# jump to higher half
leal ap_higher_half, %ecx
jmp *%ecx
ap_higher_half:
movl AP_REL(ap_prepare_paging), %eax
call *%eax
# load AP's initial values
movl AP_REL(ap_stack_vaddr), %esp
movl AP_REL(ap_page_table), %eax
movl $1, AP_REL(ap_ready)
movl %eax, %cr3
# clear rbp for stacktrace
xorl %ebp, %ebp
1: pause
cmpb $0, g_ap_startup_done
je 1b
jz 1b
lock incb g_ap_running_count

View File

@@ -1,107 +1,133 @@
.macro intr_header, n
.macro push_userspace
pushw %gs
pushw %fs
pushw %es
pushw %ds
pushal
testb $3, \n+8*4(%esp)
jz 1f
.endm
.macro load_kernel_segments
movw $0x10, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw $0x28, %ax
movw %ax, %gs
1: cld
.endm
.macro intr_footer, n
testb $3, \n+8*4(%esp)
jz 1f
call cpp_check_signal
movw $(0x20 | 3), %bx
movw %bx, %ds
movw %bx, %es
movw $(0x30 | 3), %bx
movw %bx, %fs
movw $(0x38 | 3), %bx
movw %bx, %gs
1: popal
.macro pop_userspace
popal
popw %ds
popw %es
popw %fs
popw %gs
.endm
isr_stub:
intr_header 12
push_userspace
load_kernel_segments
movl %cr0, %eax; pushl %eax
movl %cr2, %eax; pushl %eax
movl %cr3, %eax; pushl %eax
movl %cr4, %eax; pushl %eax
movl 48(%esp), %edi // isr number
movl 52(%esp), %esi // error code
leal 56(%esp), %edx // interrupt stack ptr
movl %esp, %ecx // register ptr
# stack frame for stack trace
leal 56(%esp), %eax
pushl (%eax)
pushl %ebp
movl %esp, %eax // register ptr
leal 64(%esp), %ebx // interrupt stack ptr
movl 60(%esp), %ecx // error code
movl 56(%esp), %edx // isr number
movl %esp, %ebp
andl $-16, %esp
subl $15, %esp
andl $0xFFFFFFF0, %esp
pushl %eax
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
pushl %edi
call cpp_isr_handler
movl %ebp, %esp
addl $24, %esp
addl $16, %esp
intr_footer 12
pop_userspace
addl $8, %esp
iret
irq_stub:
intr_header 12
push_userspace
load_kernel_segments
movl 32(%esp), %edi # interrupt number
movl 40(%esp), %eax # interrupt number
movl %esp, %ebp
andl $-16, %esp
subl $15, %esp
andl $0xFFFFFFF0, %esp
subl $12, %esp
pushl %edi
pushl %eax
call cpp_irq_handler
movl %ebp, %esp
intr_footer 12
pop_userspace
addl $8, %esp
iret
.global asm_yield_handler
asm_yield_handler:
# This can only be called from kernel, so no segment saving is needed
pushal
movl %esp, %eax # interrupt registers ptr
leal 32(%esp), %ebx # interrupt stack ptr
movl %esp, %ebp
subl $15, %esp
andl $0xFFFFFFF0, %esp
subl $8, %esp
pushl %eax
pushl %ebx
call cpp_yield_handler
movl %ebp, %esp
popal
iret
.global asm_ipi_handler
asm_ipi_handler:
intr_header 4
push_userspace
load_kernel_segments
movl %esp, %ebp
andl $-16, %esp
subl $15, %esp
andl $0xFFFFFFF0, %esp
call cpp_ipi_handler
movl %ebp, %esp
intr_footer 4
pop_userspace
iret
.global asm_timer_handler
asm_timer_handler:
intr_header 4
push_userspace
load_kernel_segments
movl %esp, %ebp
andl $-16, %esp
subl $15, %esp
andl $0xFFFFFFF0, %esp
call cpp_timer_handler
movl %ebp, %esp
intr_footer 4
pop_userspace
iret
.macro isr n
@@ -160,26 +186,35 @@ isr 29
isr 30
isr 31
.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, \
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, \
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, \
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, \
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, \
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, \
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, \
100,101,102,103,104,105,106,107,108,109, \
110,111,112,113,114,115,116,117,118,119, \
120,121,122,123,124,125,126,127,128,129, \
130,131,132,133,134,135,136,137,138,139, \
140,141,142,143,144,145,146,147,148,149, \
150,151,152,153,154,155,156,157,158,159, \
160,161,162,163,164,165,166,167,168,169, \
170,171,172,173,174,175,176,177,178,179, \
180,181,182,183,184,185,186,187,188,189, \
190,191,192,193,194,195,196,197,198,199, \
200,201,202,203,204,205,206,207
irq \i
.endr
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31

View File

@@ -11,21 +11,20 @@ SECTIONS
{
g_kernel_execute_start = .;
*(.multiboot)
*(.multiboot2)
*(.bananboot)
*(.text.*)
}
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
{
g_ap_init_addr = .;
*(.ap_init)
g_kernel_execute_end = .;
}
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
{
g_userspace_start = .;
*(.userspace)
g_userspace_end = .;
g_kernel_execute_end = .;
}
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
{
g_ap_init_addr = .;
*(.ap_init)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,97 +1,57 @@
.section .userspace, "ax"
// stack contains
// (8 bytes) return address (on return stack)
// (8 bytes) return stack
// (8 bytes) return rflags
// (8 bytes) restore sigmask
// (56 bytes) siginfo_t
// (8 bytes) signal number
// (8 bytes) signal handler
// return address
// signal number
// signal handler
.global signal_trampoline
signal_trampoline:
pushq %r15 // gregs
pushq %r14
pushq %r13
pushq %r12
pushq %r11
pushq %r10
pushq %r9
pushq %r8
pushq %rsi
pushq %rdi
pushq %rdx
pushq %rcx
pushq %rax
pushq %rbx
pushq %rax
pushq %rcx
pushq %rdx
pushq %rbp
pushq %rdi
pushq %rsi
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq 208(%rsp), %rax
pushq %rax; addq $(128 + 8), (%rsp)
pushq (%rax)
// FIXME: populate these
xorq %rax, %rax
pushq %rax // stack
pushq %rax
pushq %rax
pushq %rax // sigset
pushq %rax // link
movq %rsp, %rdx // ucontext
leaq 192(%rsp), %rsi // siginfo
movq 184(%rsp), %rdi // signal number
movq 176(%rsp), %rax // handler
movq 128(%rsp), %rdi
movq 120(%rsp), %rax
// align stack to 16 bytes
movq %rsp, %rbp
andq $-16, %rsp
subq $512, %rsp
fxsave64 (%rsp)
movq %rsp, %rbx
andq $0x0F, %rbx
subq %rbx, %rsp
call *%rax
fxrstor64 (%rsp)
addq $512, %rsp
// restore stack
movq %rbp, %rsp
addq $40, %rsp
// restore sigmask
movq $83, %rdi // SYS_SIGPROCMASK
movq $3, %rsi // SIG_SETMASK
leaq 192(%rsp), %rdx // set
xorq %r10, %r10 // oset
syscall
// restore registers
addq $16, %rsp
popq %rbp
popq %rax
popq %rbx
popq %rcx
popq %rdx
popq %rdi
popq %rsi
popq %r8
popq %r9
popq %r10
popq %r11
popq %r12
popq %r13
popq %r14
addq %rbx, %rsp
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rsi
popq %rdi
popq %rbp
popq %rdx
popq %rcx
popq %rbx
popq %rax
// skip handler, number, siginfo_t, sigmask
addq $80, %rsp
// restore flags
popfq
movq (%rsp), %rsp
addq $16, %rsp
// return over red-zone
ret $128

View File

@@ -1,26 +1,48 @@
// arguments in RAX, RBX, RCX, RDX, RSI, RDI
// System V ABI: RDI, RSI, RDX, RCX, R8, R9
.global asm_syscall_handler
asm_syscall_handler:
swapgs
movq %rsp, %rax
movq %gs:8, %rsp
pushq $(0x20 | 3)
pushq %rax
pushq %r11
pushq $(0x28 | 3)
pushq %rbx
pushq %rcx
subq $8, %rsp
pushq %rdx
pushq %rdi
pushq %rsi
pushq %rbp
pushq %r8
pushq %r9
pushq %r10
pushq %r11
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %r10, %rcx
movq %rsi, %r8
movq %rdi, %r9
movq %rax, %rdi
movq %rbx, %rsi
xchgq %rcx, %rdx
leaq 112(%rsp), %rbx
pushq %rbx
call cpp_syscall_handler
addq $8, %rsp
movq 8(%rsp), %rcx
movq 24(%rsp), %r11
movq 32(%rsp), %rsp
popq %r15
popq %r14
popq %r13
popq %r12
popq %r11
popq %r10
popq %r9
popq %r8
popq %rbp
popq %rsi
popq %rdi
popq %rdx
popq %rcx
popq %rbx
iretq
swapgs
sysretq
.global sys_fork_trampoline
sys_fork_trampoline:
@@ -33,7 +55,7 @@ sys_fork_trampoline:
call read_ip
testq %rax, %rax
jz .done
je .reload_stack
movq %rax, %rsi
movq %rsp, %rdi
@@ -47,3 +69,9 @@ sys_fork_trampoline:
popq %rbp
popq %rbx
ret
.reload_stack:
call get_thread_start_sp
movq %rax, %rsp
xorq %rax, %rax
jmp .done

View File

@@ -7,6 +7,9 @@ read_ip:
# void start_kernel_thread()
.global start_kernel_thread
start_kernel_thread:
call get_thread_start_sp
movq %rax, %rsp
# STACK LAYOUT
# on_exit arg
# on_exit func
@@ -24,5 +27,27 @@ start_kernel_thread:
.global start_userspace_thread
start_userspace_thread:
swapgs
call get_thread_start_sp
movq %rax, %rsp
# STACK LAYOUT
# entry
# argc
# argv
# envp
# userspace stack
call get_userspace_thread_stack_top
popq %rdi
popq %rsi
popq %rdx
popq %rcx
popq %rbx
pushq $(0x20 | 3)
pushq %rax
pushq $0x202
pushq $(0x18 | 3)
pushq %rbx
iretq

View File

@@ -1,87 +0,0 @@
# bool safe_user_memcpy(void*, const void*, size_t)
.global safe_user_memcpy
.global safe_user_memcpy_end
.global safe_user_memcpy_fault
safe_user_memcpy:
xorq %rax, %rax
movq %rdx, %rcx
rep movsb
incq %rax
safe_user_memcpy_fault:
ret
safe_user_memcpy_end:
# bool safe_user_strncpy(void*, const void*, size_t)
.global safe_user_strncpy
.global safe_user_strncpy_end
.global safe_user_strncpy_fault
safe_user_strncpy:
movq %rdx, %rcx
testq %rcx, %rcx
jz safe_user_strncpy_fault
.safe_user_strncpy_align_loop:
testb $0x7, %sil
jz .safe_user_strncpy_align_done
movb (%rsi), %al
movb %al, (%rdi)
testb %al, %al
jz .safe_user_strncpy_done
incq %rdi
incq %rsi
decq %rcx
jnz .safe_user_strncpy_align_loop
jmp safe_user_strncpy_fault
.safe_user_strncpy_align_done:
movq $0x0101010101010101, %r8
movq $0x8080808080808080, %r9
.safe_user_strncpy_qword_loop:
cmpq $8, %rcx
jb .safe_user_strncpy_qword_done
movq (%rsi), %rax
movq %rax, %r10
movq %rax, %r11
# https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
subq %r8, %r10
notq %r11
andq %r11, %r10
andq %r9, %r10
jnz .safe_user_strncpy_byte_loop
movq %rax, (%rdi)
addq $8, %rdi
addq $8, %rsi
subq $8, %rcx
jnz .safe_user_strncpy_qword_loop
jmp safe_user_strncpy_fault
.safe_user_strncpy_qword_done:
testq %rcx, %rcx
jz safe_user_strncpy_fault
.safe_user_strncpy_byte_loop:
movb (%rsi), %al
movb %al, (%rdi)
testb %al, %al
jz .safe_user_strncpy_done
incq %rdi
incq %rsi
decq %rcx
jnz .safe_user_strncpy_byte_loop
safe_user_strncpy_fault:
xorq %rax, %rax
ret
.safe_user_strncpy_done:
movb $1, %al
ret
safe_user_strncpy_end:

View File

@@ -1,29 +0,0 @@
.global asm_yield_trampoline
asm_yield_trampoline:
leaq 8(%rsp), %rcx
movq %rdi, %rsp
subq $8, %rsp
pushq -8(%rcx)
pushq %rcx
pushq %rax
pushq %rbx
pushq %rbp
pushq %r12
pushq %r13
pushq %r14
pushq %r15
movq %rsp, %rdi
call scheduler_on_yield
popq %r15
popq %r14
popq %r13
popq %r12
popq %rbp
popq %rbx
popq %rax
movq 8(%rsp), %rcx
movq 0(%rsp), %rsp
jmp *%rcx

View File

@@ -11,28 +11,9 @@
.code32
// custom addresses, video mode info, page align modules
.set multiboot_flags, (1 << 16) | (1 << 2) | (1 << 0)
# multiboot2 header
.section .multiboot, "aw"
multiboot_start:
.long 0x1BADB002
.long multiboot_flags
.long -(0x1BADB002 + multiboot_flags)
.long V2P(multiboot_start)
.long V2P(g_kernel_start)
.long V2P(g_kernel_bss_start)
.long V2P(g_kernel_end)
.long V2P(_start)
.long 0
.long FB_WIDTH
.long FB_HEIGHT
.long FB_BPP
multiboot_end:
.section .multiboot2, "aw"
.align 8
multiboot2_start:
.long 0xE85250D6
.long 0
@@ -55,12 +36,6 @@ multiboot2_start:
.long 12
.long V2P(_start)
# page align modules
.align 8
.short 6
.short 0
.long 8
.align 8
.short 0
.short 0
@@ -68,6 +43,7 @@ multiboot2_start:
multiboot2_end:
.section .bananboot, "aw"
.align 8
bananboot_start:
.long 0xBABAB007
.long -(0xBABAB007 + FB_WIDTH + FB_HEIGHT + FB_BPP)
@@ -77,10 +53,9 @@ bananboot_start:
bananboot_end:
.section .bss, "aw", @nobits
.global g_boot_stack_top
g_boot_stack_bottom:
.skip 4096 * 4
g_boot_stack_top:
boot_stack_bottom:
.skip 4096 * 64
boot_stack_top:
.global g_kernel_cmdline
g_kernel_cmdline:
@@ -97,25 +72,27 @@ bananboot_end:
.align 4096
boot_pml4:
.quad V2P(boot_pdpt_lo) + (PG_READ_WRITE | PG_PRESENT)
.skip 510 * 8
.rept 510
.quad 0
.endr
.quad V2P(boot_pdpt_hi) + (PG_READ_WRITE | PG_PRESENT)
boot_pdpt_lo:
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
.skip 511 * 8
.rept 511
.quad 0
.endr
boot_pdpt_hi:
.skip 510 * 8
.rept 510
.quad 0
.endr
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
.skip 8
.quad 0
boot_pd:
.set i, 0
.rept 511
.rept 512
.quad i + (PG_PAGE_SIZE | PG_READ_WRITE | PG_PRESENT)
.set i, i + 0x200000
.endr
.quad V2P(g_boot_fast_page_pt) + (PG_READ_WRITE | PG_PRESENT)
.global g_boot_fast_page_pt
g_boot_fast_page_pt:
.skip 512 * 8
boot_gdt:
.quad 0x0000000000000000 # null descriptor
@@ -178,13 +155,6 @@ enable_sse:
movl %eax, %cr4
ret
enable_tsc:
# allow userspace to use RDTSC
movl %cr4, %ecx
andl $0xFFFFFFFB, %ecx
movl %ecx, %cr4
ret
initialize_paging:
# enable PAE
movl %cr4, %ecx
@@ -217,11 +187,10 @@ _start:
movl %eax, V2P(bootloader_magic)
movl %ebx, V2P(bootloader_info)
movl $V2P(g_boot_stack_top), %esp
movl $V2P(boot_stack_top), %esp
call check_requirements
call enable_sse
call enable_tsc
call initialize_paging
# flush gdt and jump to 64 bit
@@ -271,7 +240,7 @@ system_halt:
jmp 1b
#define AP_REL(vaddr) ((vaddr) - ap_trampoline + 0xF000)
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
.section .ap_init, "ax"
@@ -281,27 +250,21 @@ ap_trampoline:
jmp 1f
.align 8
ap_stack_paddr:
.skip 8
ap_stack_vaddr:
.skip 8
ap_prepare_paging:
.skip 8
ap_page_table:
.skip 8
ap_ready:
.skip 8
ap_stack_ptr:
.skip 4
ap_stack_loaded:
.skip 1
1: cli; cld
ljmpl $0x00, $AP_REL(ap_cs_clear)
ljmpl $0x00, $AP_V2P(ap_cs_clear)
ap_cs_clear:
# load ap gdt and enter protected mode
lgdt AP_REL(ap_gdtr)
lgdt AP_V2P(ap_gdtr)
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $0x08, $AP_REL(ap_protected_mode)
ljmpl $0x08, $AP_V2P(ap_protected_mode)
.code32
ap_protected_mode:
@@ -310,42 +273,36 @@ ap_protected_mode:
movw %ax, %ss
movw %ax, %es
movl AP_REL(ap_stack_paddr), %esp
movl AP_V2P(ap_stack_ptr), %esp
movb $1, AP_V2P(ap_stack_loaded)
leal V2P(enable_sse), %ecx; call *%ecx
leal V2P(enable_tsc), %ecx; call *%ecx
leal V2P(initialize_paging), %ecx; call *%ecx
# load boot gdt and enter long mode
lgdt V2P(boot_gdtr)
ljmpl $0x08, $AP_REL(ap_long_mode)
ljmpl $0x08, $AP_V2P(ap_long_mode)
.code64
ap_long_mode:
movq $ap_higher_half, %rax
jmp *%rax
ap_higher_half:
movq AP_REL(ap_prepare_paging), %rax
call *%rax
# load AP's initial values
movq AP_REL(ap_stack_vaddr), %rsp
movq AP_REL(ap_page_table), %rax
movq $1, AP_REL(ap_ready)
movq %rax, %cr3
# move stack pointer to higher half
movl %esp, %esp
addq $KERNEL_OFFSET, %rsp
# clear rbp for stacktrace
xorq %rbp, %rbp
xorb %al, %al
1: pause
cmpb $0, g_ap_startup_done
je 1b
cmpb %al, g_ap_startup_done
jz 1b
lock incb g_ap_running_count
call ap_main
jmp system_halt
# jump to ap_main in higher half
movabsq $ap_main, %rcx
call *%rcx
jmp V2P(system_halt)
ap_gdt:
.quad 0x0000000000000000 # null descriptor

View File

@@ -1,4 +1,4 @@
.macro intr_header, n
.macro pushaq
pushq %rax
pushq %rcx
pushq %rdx
@@ -14,18 +14,10 @@
pushq %r13
pushq %r14
pushq %r15
testb $3, \n+15*8(%rsp)
jz 1f
swapgs
1: cld
.endm
.macro intr_footer, n
testb $3, \n+15*8(%rsp)
jz 1f
call cpp_check_signal
swapgs
1: popq %r15
.macro popaq
popq %r15
popq %r14
popq %r13
popq %r12
@@ -43,7 +35,8 @@
.endm
isr_stub:
intr_header 24
pushaq
movq %cr0, %rax; pushq %rax
movq %cr2, %rax; pushq %rax
movq %cr3, %rax; pushq %rax
@@ -56,33 +49,39 @@ isr_stub:
call cpp_isr_handler
addq $32, %rsp
intr_footer 24
popaq
addq $16, %rsp
iretq
irq_stub:
intr_header 24
xorq %rbp, %rbp
pushaq
movq 120(%rsp), %rdi # irq number
call cpp_irq_handler
intr_footer 24
popaq
addq $16, %rsp
iretq
.global asm_yield_handler
asm_yield_handler:
pushaq
leaq 120(%rsp), %rdi # interrupt stack ptr
movq %rsp, %rsi # interrupt register ptr
call cpp_yield_handler
popaq
iretq
.global asm_ipi_handler
asm_ipi_handler:
intr_header 8
xorq %rbp, %rbp
pushaq
call cpp_ipi_handler
intr_footer 8
popaq
iretq
.global asm_timer_handler
asm_timer_handler:
intr_header 8
xorq %rbp, %rbp
pushaq
call cpp_timer_handler
intr_footer 8
popaq
iretq
.macro isr n
@@ -141,26 +140,35 @@ isr 29
isr 30
isr 31
.irp i, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, \
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, \
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, \
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, \
40, 41, 42, 43, 44, 45, 46, 47, 48, 49, \
50, 51, 52, 53, 54, 55, 56, 57, 58, 59, \
60, 61, 62, 63, 64, 65, 66, 67, 68, 69, \
70, 71, 72, 73, 74, 75, 76, 77, 78, 79, \
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, \
90, 91, 92, 93, 94, 95, 96, 97, 98, 99, \
100,101,102,103,104,105,106,107,108,109, \
110,111,112,113,114,115,116,117,118,119, \
120,121,122,123,124,125,126,127,128,129, \
130,131,132,133,134,135,136,137,138,139, \
140,141,142,143,144,145,146,147,148,149, \
150,151,152,153,154,155,156,157,158,159, \
160,161,162,163,164,165,166,167,168,169, \
170,171,172,173,174,175,176,177,178,179, \
180,181,182,183,184,185,186,187,188,189, \
190,191,192,193,194,195,196,197,198,199, \
200,201,202,203,204,205,206,207
irq \i
.endr
irq 0
irq 1
irq 2
irq 3
irq 4
irq 5
irq 6
irq 7
irq 8
irq 9
irq 10
irq 11
irq 12
irq 13
irq 14
irq 15
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31

View File

@@ -11,21 +11,20 @@ SECTIONS
{
g_kernel_execute_start = .;
*(.multiboot)
*(.multiboot2)
*(.bananboot)
*(.text.*)
}
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
{
g_ap_init_addr = .;
*(.ap_init)
g_kernel_execute_end = .;
}
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
{
g_userspace_start = .;
*(.userspace)
g_userspace_end = .;
g_kernel_execute_end = .;
}
.ap_init ALIGN(4K) : AT(ADDR(.ap_init) - KERNEL_OFFSET)
{
g_ap_init_addr = .;
*(.ap_init)
}
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
{
@@ -44,7 +43,6 @@ SECTIONS
}
.bss ALIGN(4K) : AT(ADDR(.bss) - KERNEL_OFFSET)
{
g_kernel_bss_start = .;
*(COMMON)
*(.bss)
g_kernel_writable_end = .;

View File

@@ -1,8 +1,8 @@
#pragma once
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/EmbeddedController.h>
#include <kernel/ACPI/Headers.h>
#include <kernel/Memory/Types.h>
#include <kernel/ThreadBlocker.h>
@@ -29,38 +29,25 @@ namespace Kernel::ACPI
// 2: SAPIC
BAN::ErrorOr<void> enter_acpi_mode(uint8_t mode);
BAN::ErrorOr<void> initialize_acpi_devices();
// This function will power off the system
// This function will return only if there was an error
void poweroff();
AML::Namespace* acpi_namespace() { return m_namespace; }
BAN::ErrorOr<void> poweroff();
BAN::ErrorOr<void> reset();
BAN::ErrorOr<void> register_gpe_handler(uint8_t gpe, void (*callback)(void*), void* argument);
// This function will reset the system
// This function will return only if there was an error
void reset();
void handle_irq() override;
BAN::Span<BAN::UniqPtr<EmbeddedController>> embedded_controllers() { return m_embedded_controllers.span(); }
private:
ACPI() = default;
BAN::ErrorOr<void> initialize_impl();
FADT& fadt() { return *m_fadt; }
BAN::ErrorOr<void> prepare_sleep(uint8_t sleep_state);
bool prepare_sleep(uint8_t sleep_state);
void acpi_event_task();
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);
BAN::ErrorOr<void> initialize_embedded_controller(const AML::Scope& embedded_controller);
BAN::ErrorOr<void> initialize_embedded_controllers();
BAN::Optional<GAS> find_gpe_block(size_t index);
bool enable_gpe(uint8_t gpe);
private:
paddr_t m_header_table_paddr = 0;
vaddr_t m_header_table_vaddr = 0;
@@ -78,27 +65,10 @@ namespace Kernel::ACPI
FADT* m_fadt { nullptr };
ThreadBlocker m_event_thread_blocker;
BAN::Vector<BAN::UniqPtr<EmbeddedController>> m_embedded_controllers;
struct GPEHandler
{
bool has_callback { false };
union {
AML::Reference* method;
struct
{
void (*callback)(void*);
void* argument;
};
};
};
bool m_has_any_gpes { false };
AML::Scope m_gpe_scope;
BAN::Array<GPEHandler, 0xFF> m_gpe_methods;
BAN::Array<BAN::RefPtr<AML::Method>, 0xFF> m_gpe_methods;
bool m_hardware_reduced { false };
AML::Namespace* m_namespace { nullptr };
BAN::RefPtr<AML::Namespace> m_namespace;
};
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <kernel/ACPI/Headers.h>
#include <kernel/ACPI/AML/Namespace.h>
namespace Kernel::ACPI::AML
{
BAN::RefPtr<AML::Namespace> initialize_namespace();
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Names.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Alias
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::AliasOp);
context.aml_data = context.aml_data.slice(1);
auto source_string = AML::NameString::parse(context.aml_data);
if (!source_string.has_value())
return ParseResult::Failure;
auto source_object = AML::Namespace::root_namespace()->find_object(context.scope, source_string.value(), AML::Namespace::FindMode::Normal);
auto alias_string = AML::NameString::parse(context.aml_data);
if (!alias_string.has_value())
return ParseResult::Failure;
if (!source_object)
{
AML_PRINT("Alias target could not be found");
return ParseResult::Success;
}
if (!Namespace::root_namespace()->add_named_object(context, alias_string.value(), source_object))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("Alias \"");
alias_string->debug_print();
AML_DEBUG_PRINT("\" => ");
source_object->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,312 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI::AML
{
struct Buffer final : public AML::Node
{
BAN::Vector<uint8_t> buffer;
Buffer()
: AML::Node(Node::Type::Buffer)
{}
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop)
{
auto rhs_node = node ? node->convert(AML::Node::ConvBuffer) : BAN::RefPtr<AML::Node>();
if (!rhs_node)
{
AML_ERROR("Buffer logical compare RHS cannot be converted to buffer");
return {};
}
(void)binaryop;
AML_TODO("Logical compare buffer");
return {};
}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (mask & AML::Node::ConvBuffer)
return this;
if (mask & AML::Node::ConvInteger)
return as_integer();
if (mask & AML::Node::ConvString)
{
AML_TODO("Convert BufferField to String");
return {};
}
return {};
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(node);
auto conv_node = node->convert(AML::Node::ConvBuffer);
if (!conv_node)
{
AML_ERROR("Buffer store could not convert to buffer");
return {};
}
auto& conv_buffer = static_cast<AML::Buffer*>(conv_node.ptr())->buffer;
MUST(buffer.resize(conv_buffer.size()));
for (size_t i = 0; i < buffer.size(); i++)
buffer[i] = conv_buffer[i];
return this;
}
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::BufferOp);
context.aml_data = context.aml_data.slice(1);
auto buffer_pkg = AML::parse_pkg(context.aml_data);
if (!buffer_pkg.has_value())
return ParseResult::Failure;
auto buffer_context = context;
buffer_context.aml_data = buffer_pkg.value();
auto buffer_size_result = AML::parse_object(buffer_context);
if (!buffer_size_result.success())
return ParseResult::Failure;
auto buffer_size_node = buffer_size_result.node() ? buffer_size_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!buffer_size_node)
{
AML_ERROR("Buffer size is not an integer");
return ParseResult::Failure;
}
const uint32_t actual_buffer_size = BAN::Math::max<uint32_t>(
static_cast<AML::Integer*>(buffer_size_node.ptr())->value,
buffer_context.aml_data.size()
);
auto buffer = MUST(BAN::RefPtr<Buffer>::create());
MUST(buffer->buffer.resize(actual_buffer_size, 0));
for (uint32_t i = 0; i < buffer_context.aml_data.size(); i++)
buffer->buffer[i] = buffer_context.aml_data[i];
#if AML_DEBUG_LEVEL >= 2
buffer->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult(buffer);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Buffer ({} bytes)", buffer.size());
}
private:
BAN::RefPtr<AML::Integer> as_integer()
{
uint64_t value = 0;
for (size_t i = 0; i < BAN::Math::min<size_t>(buffer.size(), 8); i++)
value |= static_cast<uint64_t>(buffer[i]) << (8 * i);
return MUST(BAN::RefPtr<Integer>::create(value));
}
};
struct BufferField final : AML::NamedObject
{
BAN::RefPtr<AML::Node> buffer;
size_t field_bit_offset;
size_t field_bit_size;
template<typename T> requires BAN::is_same_v<T, AML::Buffer> || BAN::is_same_v<T, AML::String>
BufferField(AML::NameSeg name, BAN::RefPtr<T> buffer, size_t field_bit_offset, size_t field_bit_size)
: AML::NamedObject(Node::Type::BufferField, name)
, buffer(buffer)
, field_bit_offset(field_bit_offset)
, field_bit_size(field_bit_size)
{}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (mask & AML::Node::ConvBufferField)
return this;
if (mask & AML::Node::ConvInteger)
return as_integer();
if (mask & AML::Node::ConvBuffer)
{
AML_TODO("Convert BufferField to Buffer");
return {};
}
if (mask & AML::Node::ConvString)
{
AML_TODO("Convert BufferField to String");
return {};
}
return {};
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(buffer);
ASSERT(buffer->type == AML::Node::Type::Buffer || buffer->type == AML::Node::Type::String);
auto& buffer = (this->buffer->type == AML::Node::Type::Buffer)
? static_cast<AML::Buffer*>(this->buffer.ptr())->buffer
: static_cast<AML::String*>(this->buffer.ptr())->string;
ASSERT(field_bit_offset + field_bit_size <= buffer.size() * 8);
auto value_node = node ? node->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!value_node)
return {};
const auto value = static_cast<AML::Integer*>(value_node.ptr())->value;
// TODO: optimize for whole byte accesses
for (size_t i = 0; i < field_bit_size; i++)
{
const size_t bit = field_bit_offset + i;
buffer[bit / 8] &= ~(1 << (bit % 8));
buffer[bit / 8] |= ((value >> i) & 1) << (bit % 8);
}
return value_node;
}
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
bool offset_in_bytes;
size_t field_bit_size;
switch (static_cast<AML::Byte>(context.aml_data[0]))
{
case AML::Byte::CreateBitFieldOp:
field_bit_size = 1;
offset_in_bytes = false;
break;
case AML::Byte::CreateByteFieldOp:
field_bit_size = 8;
offset_in_bytes = true;
break;
case AML::Byte::CreateWordFieldOp:
field_bit_size = 16;
offset_in_bytes = true;
break;
case AML::Byte::CreateDWordFieldOp:
field_bit_size = 32;
offset_in_bytes = true;
break;
case AML::Byte::CreateQWordFieldOp:
field_bit_size = 64;
offset_in_bytes = true;
break;
case AML::Byte::ExtOpPrefix:
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CreateFieldOp);
field_bit_size = 0;
offset_in_bytes = false;
break;
default:
ASSERT_NOT_REACHED();
}
context.aml_data = context.aml_data.slice(1 + (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix));
auto buffer_result = AML::parse_object(context);
if (!buffer_result.success())
return ParseResult::Failure;
auto buffer_node = buffer_result.node() ? buffer_result.node()->convert(AML::Node::ConvBuffer) : BAN::RefPtr<AML::Node>();
if (!buffer_node || buffer_node->type != Node::Type::Buffer)
{
AML_ERROR("Buffer source does not evaluate to a Buffer");
return ParseResult::Failure;
}
auto buffer = BAN::RefPtr<AML::Buffer>(static_cast<AML::Buffer*>(buffer_node.ptr()));
auto index_result = AML::parse_object(context);
if (!index_result.success())
return ParseResult::Failure;
auto index_node = index_result.node() ? index_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!index_node)
{
AML_ERROR("Failed to parse index for BufferField");
return ParseResult::Failure;
}
if (field_bit_size == 0)
{
auto bit_count_result = AML::parse_object(context);
if (!bit_count_result.success())
return ParseResult::Failure;
auto bit_count_node = bit_count_result.node() ? bit_count_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!bit_count_node)
{
AML_ERROR("Failed to parse bit count for BufferField");
return ParseResult::Failure;
}
field_bit_size = static_cast<AML::Integer*>(bit_count_node.ptr())->value;
}
auto field_name = AML::NameString::parse(context.aml_data);
if (!field_name.has_value())
return ParseResult::Failure;
if (field_name->path.empty())
{
AML_ERROR("Empty field name for BufferField");
return ParseResult::Failure;
}
size_t field_bit_offset = static_cast<AML::Integer*>(index_node.ptr())->value;
if (offset_in_bytes)
field_bit_offset *= 8;
auto field = MUST(BAN::RefPtr<BufferField>::create(field_name->path.back(), buffer, field_bit_offset, field_bit_size));
if (!Namespace::root_namespace()->add_named_object(context, field_name.value(), field))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
field->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("BufferField {} at bit offset {} ({} bits) to { ", name, field_bit_offset, field_bit_size);
buffer->debug_print(0);
AML_DEBUG_PRINT(" }");
}
private:
BAN::RefPtr<AML::Integer> as_integer()
{
ASSERT(buffer);
ASSERT(buffer->type == AML::Node::Type::Buffer || buffer->type == AML::Node::Type::String);
const auto& buffer = (this->buffer->type == AML::Node::Type::Buffer)
? static_cast<AML::Buffer*>(this->buffer.ptr())->buffer
: static_cast<AML::String*>(this->buffer.ptr())->string;
uint64_t value = 0;
// TODO: optimize for whole byte accesses
for (size_t i = 0; i < BAN::Math::min<size_t>(field_bit_size, 64); i++)
{
const size_t bit = field_bit_offset + i;
value |= static_cast<uint64_t>((buffer[bit / 8] >> (bit % 8)) & 1) << i;
}
return MUST(BAN::RefPtr<Integer>::create(value));
}
};
}

View File

@@ -145,35 +145,19 @@ namespace Kernel::ACPI::AML
DataRegionOp = 0x88,
};
inline constexpr bool is_digit_char(uint8_t ch)
static constexpr bool is_digit_char(uint8_t ch)
{
return '0' <= ch && ch <= '9';
}
inline constexpr bool is_lead_name_char(uint8_t ch)
static constexpr bool is_lead_name_char(uint8_t ch)
{
return ('A' <= ch && ch <= 'Z') || ch == '_';
}
inline constexpr bool is_name_char(uint8_t ch)
static constexpr bool is_name_char(uint8_t ch)
{
return is_lead_name_char(ch) || is_digit_char(ch);
}
inline constexpr bool is_name_string_start(uint8_t ch)
{
if (is_lead_name_char(ch))
return true;
switch (static_cast<AML::Byte>(ch))
{
case AML::Byte::RootChar:
case AML::Byte::ParentPrefixChar:
case AML::Byte::MultiNamePrefix:
case AML::Byte::DualNamePrefix:
return true;
default:
return false;
}
}
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI::AML
{
struct Conversion
{
static ParseResult parse(AML::ParseContext& context)
{
const auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
switch (opcode)
{
case AML::Byte::ToIntegerOp:
case AML::Byte::ToBufferOp:
case AML::Byte::ToStringOp:
break;
default:
ASSERT_NOT_REACHED();
}
auto data_result = AML::parse_object(context);
if (!data_result.success())
return ParseResult::Failure;
auto data_node = data_result.node();
if (!data_node)
{
AML_ERROR("Conversion {2H} data could not be evaluated", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
if (context.aml_data.size() < 1)
{
AML_ERROR("Conversion {2H} missing target", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
BAN::RefPtr<AML::Node> target_node;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
target_node = target_result.node();
if (!target_node)
{
AML_ERROR("Conversion {2H} target invalid", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
}
BAN::RefPtr<AML::Node> converted;
switch (opcode)
{
case AML::Byte::ToBufferOp:
converted = data_node->convert(AML::Node::ConvBuffer);
break;
case AML::Byte::ToIntegerOp:
converted = data_node->convert(AML::Node::ConvInteger);
break;
case AML::Byte::ToStringOp:
converted = data_node->convert(AML::Node::ConvString);
break;
default:
ASSERT_NOT_REACHED();
}
if (!converted)
{
AML_ERROR("Conversion {2H} could not convert from node type {}", static_cast<uint8_t>(opcode), static_cast<uint8_t>(data_node->type));
return ParseResult::Failure;
}
if (target_node && !target_node->store(converted))
{
AML_ERROR("Conversion {2H} failed to store converted value", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
return ParseResult(converted);
}
};
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <kernel/ACPI/AML/Alias.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI::AML
{
struct CopyObject
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::CopyObjectOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node()
? source_result.node()->to_underlying()
: BAN::RefPtr<AML::Node>();
if (!source)
{
AML_ERROR("CopyObject source is null");
return ParseResult::Failure;
}
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("CopyObject destination is null");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("CopyObject {");
source->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("} to {");
destination->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("}");
#endif
switch (destination->type)
{
case AML::Node::Type::Name:
static_cast<AML::Name*>(destination.ptr())->object = source->copy();
return source;
case AML::Node::Type::Register:
static_cast<AML::Register*>(destination.ptr())->value = source->copy();
return source;
default:
ASSERT_NOT_REACHED();
}
}
};
}

View File

@@ -0,0 +1,59 @@
#pragma once
#include <kernel/ACPI/AML/Method.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct Device final : public AML::Scope
{
Device(NameSeg name)
: Scope(Node::Type::Device, name)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::DeviceOp);
context.aml_data = context.aml_data.slice(2);
auto device_pkg = AML::parse_pkg(context.aml_data);
if (!device_pkg.has_value())
return ParseResult::Failure;
auto name_string = AML::NameString::parse(device_pkg.value());
if (!name_string.has_value())
return ParseResult::Failure;
auto device = MUST(BAN::RefPtr<Device>::create(name_string->path.back()));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), device))
return ParseResult::Success;
return device->enter_context_and_parse_term_list(context, name_string.value(), device_pkg.value());
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Device ");
name.debug_print();
AML_DEBUG_PRINTLN(" {");
Namespace::root_namespace()->for_each_child(scope,
[&](const auto&, const auto& child)
{
child->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
}
);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,148 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ThreadBlocker.h>
#include <kernel/Timer/Timer.h>
namespace Kernel::ACPI::AML
{
struct Event final : public AML::NamedObject
{
BAN::Atomic<uint32_t> signal_count { 0 };
ThreadBlocker thread_blocker;
Event(NameSeg name)
: NamedObject(Node::Type::Event, name)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
const auto ext_op = static_cast<AML::ExtOp>(context.aml_data[1]);
switch (ext_op)
{
case AML::ExtOp::EventOp:
return parse_event(context);
case AML::ExtOp::ResetOp:
case AML::ExtOp::SignalOp:
case AML::ExtOp::WaitOp:
break;
default:
ASSERT_NOT_REACHED();
}
context.aml_data = context.aml_data.slice(2);
auto event_result = parse_object(context);
if (!event_result.success())
return ParseResult::Failure;
auto general_node = event_result.node();
if (!general_node || general_node->type != Node::Type::Event)
{
AML_ERROR("Release, Wait or Signal does not name an event");
return ParseResult::Failure;
}
auto* event_node = static_cast<AML::Event*>(general_node.ptr());
if (ext_op == AML::ExtOp::WaitOp)
{
auto timeout_result = parse_object(context);
if (!timeout_result.success())
return ParseResult::Failure;
auto timeout_node = timeout_result.node()
? timeout_result.node()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!timeout_node)
{
AML_ERROR("Wait timeout does not evaluate to integer");
return ParseResult::Failure;
}
const auto timeout_value = static_cast<AML::Integer*>(timeout_node.ptr())->value;
const uint64_t start_ms = SystemTimer::get().ms_since_boot();
while (true)
{
auto expected = event_node->signal_count.load();
while (true)
{
if (expected == 0)
break;
if (event_node->signal_count.compare_exchange(expected, expected - 1))
return ParseResult(Integer::Constants::Zero);
}
if (timeout_value >= 0xFFFF)
event_node->thread_blocker.block_indefinite();
else
{
const uint64_t current_ms = SystemTimer::get().ms_since_boot();
if (current_ms >= start_ms + timeout_value)
return ParseResult(Integer::Constants::Ones);
event_node->thread_blocker.block_with_timeout_ms(start_ms + timeout_value - current_ms);
}
}
ASSERT_NOT_REACHED();
}
switch (ext_op)
{
case AML::ExtOp::ResetOp:
event_node->signal_count = 0;
break;
case AML::ExtOp::SignalOp:
event_node->signal_count++;
event_node->thread_blocker.unblock();
break;
default:
ASSERT_NOT_REACHED();
}
return ParseResult::Success;
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Event ");
name.debug_print();
AML_DEBUG_PRINT(" (Signals: {})", signal_count.load());
}
private:
static ParseResult parse_event(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::EventOp);
context.aml_data = context.aml_data.slice(2);
auto name_string = NameString::parse(context.aml_data);
if (!name_string.has_value())
return ParseResult::Failure;
auto event = MUST(BAN::RefPtr<Event>::create(name_string->path.back()));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), event))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
event->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,243 @@
#pragma once
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/String.h>
namespace Kernel::ACPI::AML
{
struct Expression
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
switch (static_cast<Byte>(context.aml_data[0]))
{
// unary
case AML::Byte::IncrementOp:
case AML::Byte::DecrementOp:
{
auto opcode = (static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::IncrementOp) ? AML::Byte::AddOp : AML::Byte::SubtractOp;
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto conv_node = source_result.node() ? source_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!conv_node)
{
AML_ERROR("UnaryOp source not integer, type {}", static_cast<uint8_t>(source_result.node()->type));
return ParseResult::Failure;
}
auto source_node = static_cast<AML::Integer*>(conv_node.ptr());
if (source_node->constant)
{
AML_ERROR("UnaryOp source is constant");
return ParseResult::Failure;
}
source_node->value += (opcode == AML::Byte::AddOp) ? 1 : -1;
return ParseResult(source_node);
}
case AML::Byte::NotOp:
AML_TODO("NotOp", context.aml_data[0]);
return ParseResult::Failure;
case AML::Byte::LNotOp:
{
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto conv_node = source_result.node() ? source_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!conv_node)
{
AML_ERROR("UnaryOp source not integer, type {}", static_cast<uint8_t>(source_result.node()->type));
return ParseResult::Failure;
}
auto source_node = static_cast<AML::Integer*>(conv_node.ptr());
if (!source_node)
{
AML_ERROR("Logical NotOp source is not integer");
return ParseResult::Failure;
}
auto result = source_node->value ? Integer::Constants::Zero : Integer::Constants::Ones;
return ParseResult(result);
}
case AML::Byte::AddOp:
case AML::Byte::AndOp:
case AML::Byte::ModOp:
case AML::Byte::MultiplyOp:
case AML::Byte::NandOp:
case AML::Byte::NorOp:
case AML::Byte::OrOp:
case AML::Byte::ShiftLeftOp:
case AML::Byte::ShiftRightOp:
case AML::Byte::SubtractOp:
case AML::Byte::XorOp:
return parse_binary_op(context);
case AML::Byte::LAndOp:
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
case AML::Byte::LOrOp:
return parse_logical_binary_op(context);
case AML::Byte::DivideOp:
AML_TODO("DivideOp");
return ParseResult::Failure;
default:
ASSERT_NOT_REACHED();
}
}
private:
static ParseResult parse_binary_op(ParseContext& context)
{
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto lhs_result = AML::parse_object(context);
if (!lhs_result.success())
return ParseResult::Failure;
auto lhs_conv = lhs_result.node() ? lhs_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!lhs_conv)
{
AML_ERROR("BinaryOP {2H} LHS not an integer, type {}",
static_cast<uint8_t>(opcode),
static_cast<uint8_t>(lhs_result.node()->type)
);
if (lhs_result.node())
lhs_result.node()->debug_print(1);
AML_DEBUG_PRINTLN("");
return ParseResult::Failure;
}
const auto lhs_value = static_cast<AML::Integer*>(lhs_conv.ptr())->value;
auto rhs_result = AML::parse_object(context);
if (!rhs_result.success())
return ParseResult::Failure;
auto rhs_conv = rhs_result.node() ? rhs_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!rhs_conv)
{
AML_ERROR("BinaryOP {2H} RHS not an integer", static_cast<uint8_t>(opcode));
if (rhs_result.node())
rhs_result.node()->debug_print(1);
AML_DEBUG_PRINTLN("");
return ParseResult::Failure;
}
const auto rhs_value = static_cast<AML::Integer*>(rhs_conv.ptr())->value;
if (context.aml_data.size() < 1)
{
AML_ERROR("BinaryOP {2H} missing target", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
BAN::RefPtr<AML::Node> target_node;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
target_node = target_result.node();
if (!target_node)
{
AML_ERROR("BinaryOP {2H} target invalid", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
}
uint64_t (*func)(uint64_t, uint64_t) = nullptr;
switch (opcode)
{
case AML::Byte::AddOp: func = [](uint64_t a, uint64_t b) { return a + b; }; break;
case AML::Byte::AndOp: func = [](uint64_t a, uint64_t b) { return a & b; }; break;
case AML::Byte::ModOp: func = [](uint64_t a, uint64_t b) { return a % b; }; break;
case AML::Byte::MultiplyOp: func = [](uint64_t a, uint64_t b) { return a * b; }; break;
case AML::Byte::NandOp: func = [](uint64_t a, uint64_t b) { return ~(a & b); }; break;
case AML::Byte::NorOp: func = [](uint64_t a, uint64_t b) { return ~(a | b); }; break;
case AML::Byte::OrOp: func = [](uint64_t a, uint64_t b) { return a | b; }; break;
case AML::Byte::ShiftLeftOp: func = [](uint64_t a, uint64_t b) { return a << b; }; break;
case AML::Byte::ShiftRightOp: func = [](uint64_t a, uint64_t b) { return a >> b; }; break;
case AML::Byte::SubtractOp: func = [](uint64_t a, uint64_t b) { return a - b; }; break;
case AML::Byte::XorOp: func = [](uint64_t a, uint64_t b) { return a ^ b; }; break;
default:
ASSERT_NOT_REACHED();
}
auto result_node = MUST(BAN::RefPtr<AML::Integer>::create(func(lhs_value, rhs_value)));
if (target_node && !target_node->store(result_node))
{
AML_ERROR("BinaryOp {2H} failed to store result", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
return ParseResult(result_node);
}
static ParseResult parse_logical_binary_op(ParseContext& context)
{
auto opcode = static_cast<AML::Byte>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
uint8_t mask;
switch (opcode)
{
case AML::Byte::LAndOp:
case AML::Byte::LOrOp:
mask = AML::Node::ConvInteger;
break;
case AML::Byte::LEqualOp:
case AML::Byte::LGreaterOp:
case AML::Byte::LLessOp:
mask = AML::Node::ConvInteger | AML::Node::ConvString | AML::Node::ConvBuffer;
break;
default:
ASSERT_NOT_REACHED();
}
auto lhs_result = AML::parse_object(context);
if (!lhs_result.success())
return ParseResult::Failure;
auto lhs_node = lhs_result.node() ? lhs_result.node()->convert(mask) : BAN::RefPtr<AML::Node>();
if (!lhs_node)
{
AML_TODO("Logical BinaryOP {2H} LHS evaluated to nothing", static_cast<uint8_t>(opcode));
return ParseResult::Failure;
}
auto rhs_result = AML::parse_object(context);
if (!rhs_result.success())
return ParseResult::Failure;
BAN::Optional<bool> result = false;
switch (lhs_node->type)
{
case AML::Node::Type::Integer:
result = static_cast<AML::Integer*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
break;
case AML::Node::Type::Buffer:
result = static_cast<AML::Buffer*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
break;
case AML::Node::Type::String:
result = static_cast<AML::String*>(lhs_node.ptr())->logical_compare(rhs_result.node(), opcode);
break;
default:
ASSERT_NOT_REACHED();
}
if (!result.has_value())
return ParseResult::Failure;
return ParseResult(result.value() ? AML::Integer::Constants::Ones : AML::Integer::Constants::Zero);
}
};
}

View File

@@ -0,0 +1,199 @@
#pragma once
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Region.h>
namespace Kernel::ACPI::AML
{
struct FieldRules
{
enum class AccessType
{
Any = 0,
Byte = 1,
Word = 2,
DWord = 3,
QWord = 4,
Buffer = 5,
};
AccessType access_type;
enum class LockRule
{
NoLock = 0,
Lock = 1,
};
LockRule lock_rule;
enum class UpdateRule
{
Preserve = 0,
WriteAsOnes = 1,
WriteAsZeros = 2,
};
UpdateRule update_rule;
enum class AccessAttrib
{
Normal = 0,
Bytes = 1,
RawBytes = 2,
RawProcessBytes = 3,
};
AccessAttrib access_attrib = AccessAttrib::Normal;
uint8_t access_length = 0;
};
struct FieldElement final : public AML::NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
FieldRules access_rules;
BAN::RefPtr<OpRegion> op_region;
FieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::FieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (mask & AML::Node::ConvInteger)
return as_integer();
if (mask & AML::Node::ConvBuffer)
{
AML_TODO("Convert BankFieldElement to Buffer");
return {};
}
if (mask & AML::Node::ConvString)
{
AML_TODO("Convert BankFieldElement to String");
return {};
}
return {};
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
void debug_print(int indent) const override;
private:
BAN::RefPtr<AML::Integer> as_integer();
BAN::Optional<uint64_t> evaluate_internal();
bool store_internal(uint64_t value);
friend struct IndexFieldElement;
friend struct BankFieldElement;
};
struct Field
{
static ParseResult parse(ParseContext& context);
};
struct IndexFieldElement final : public AML::NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
FieldRules access_rules;
BAN::RefPtr<FieldElement> index_element;
BAN::RefPtr<FieldElement> data_element;
IndexFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::IndexFieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (mask & AML::Node::ConvInteger)
if (auto node = as_integer())
return node;
if (mask & AML::Node::ConvBuffer)
{
AML_TODO("convert BankFieldElement to Buffer");
return {};
}
if (mask & AML::Node::ConvString)
{
AML_TODO("convert BankFieldElement to String");
return {};
}
return {};
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
void debug_print(int indent) const override;
private:
BAN::RefPtr<AML::Integer> as_integer();
};
struct IndexField
{
static ParseResult parse(ParseContext& context);
};
struct BankFieldElement final : public AML::NamedObject
{
uint64_t bit_offset;
uint64_t bit_count;
FieldRules access_rules;
BAN::RefPtr<OpRegion> op_region;
BAN::RefPtr<FieldElement> bank_selector;
uint64_t bank_value;
BankFieldElement(NameSeg name, uint64_t bit_offset, uint64_t bit_count, FieldRules access_rules)
: NamedObject(Node::Type::BankFieldElement, name)
, bit_offset(bit_offset)
, bit_count(bit_count)
, access_rules(access_rules)
{}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (mask & AML::Node::ConvInteger)
if (auto node = as_integer())
return node;
if (mask & AML::Node::ConvBuffer)
{
AML_TODO("convert BankFieldElement to Buffer");
return {};
}
if (mask & AML::Node::ConvString)
{
AML_TODO("convert BankFieldElement to String");
return {};
}
return {};
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<Node> source) override;
void debug_print(int indent) const override;
private:
BAN::RefPtr<AML::Integer> as_integer();
};
struct BankField
{
static ParseResult parse(ParseContext& context);
};
}

View File

@@ -0,0 +1,65 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
namespace Kernel::ACPI::AML
{
struct IfElse
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IfOp);
context.aml_data = context.aml_data.slice(1);
auto if_pkg = AML::parse_pkg(context.aml_data);
if (!if_pkg.has_value())
return ParseResult::Failure;
auto outer_aml_data = context.aml_data;
context.aml_data = if_pkg.value();
auto predicate_result = AML::parse_object(context);
if (!predicate_result.success())
return ParseResult::Failure;
auto predicate_node = predicate_result.node() ? predicate_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!predicate_node)
{
AML_ERROR("If predicate is not an integer");
return ParseResult::Failure;
}
const auto predicate_value = static_cast<AML::Integer*>(predicate_node.ptr())->value;
// Else
BAN::ConstByteSpan else_pkg;
if (outer_aml_data.size() >= 1 && static_cast<AML::Byte>(outer_aml_data[0]) == Byte::ElseOp)
{
outer_aml_data = outer_aml_data.slice(1);
auto else_pkg_result = AML::parse_pkg(outer_aml_data);
if (!else_pkg_result.has_value())
return ParseResult::Failure;
else_pkg = else_pkg_result.value();
}
if (predicate_value == 0)
context.aml_data = else_pkg;
while (context.aml_data.size() > 0)
{
auto object_result = AML::parse_object(context);
if (object_result.returned() || object_result.breaked() || object_result.continued())
return object_result;
if (!object_result.success())
return ParseResult::Failure;
}
context.aml_data = outer_aml_data;
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,126 @@
#pragma once
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Reference.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI::AML
{
struct Index
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::IndexOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node() ? source_result.node()->to_underlying() : BAN::RefPtr<AML::Node>();
if (source && source->type != AML::Node::Type::Package)
source = source->convert(AML::Node::ConvBuffer | AML::Node::ConvInteger | AML::Node::ConvString);
if (!source)
{
AML_ERROR("IndexOp source could not be converted");
return ParseResult::Failure;
}
auto index_result = AML::parse_object(context);
if (!index_result.success())
return ParseResult::Failure;
auto index_node = index_result.node()
? index_result.node()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!index_node)
{
AML_ERROR("IndexOp index is not an integer");
return ParseResult::Failure;
}
const auto index = static_cast<AML::Integer*>(index_node.ptr())->value;
BAN::RefPtr<AML::Reference> result;
switch (source->type)
{
case AML::Node::Type::Buffer:
{
auto buffer = BAN::RefPtr<AML::Buffer>(static_cast<AML::Buffer*>(source.ptr()));
if (index >= buffer->buffer.size())
{
AML_ERROR("IndexOp index is out of buffer bounds");
return ParseResult::Failure;
}
auto buffer_field = MUST(BAN::RefPtr<BufferField>::create(NameSeg(""_sv), buffer, index * 8, 8));
result = MUST(BAN::RefPtr<AML::Reference>::create(buffer_field));
break;
}
case AML::Node::Type::Package:
{
auto package = static_cast<AML::Package*>(source.ptr());
if (index >= package->elements.size())
{
AML_ERROR("IndexOp index is out of package bounds");
return ParseResult::Failure;
}
auto package_element = package->elements[index];
if (!package_element)
{
AML_ERROR("IndexOp target is null package element");
return ParseResult::Failure;
}
result = MUST(BAN::RefPtr<AML::Reference>::create(package_element));
break;
}
case AML::Node::Type::String:
{
auto string = BAN::RefPtr<AML::String>(static_cast<AML::String*>(source.ptr()));
if (index >= string->string.size())
{
AML_ERROR("IndexOp index is out of string bounds");
return ParseResult::Failure;
}
auto buffer_field = MUST(BAN::RefPtr<BufferField>::create(NameSeg(""_sv), string, index * 8, 8));
result = MUST(BAN::RefPtr<AML::Reference>::create(buffer_field));
break;
}
default:
AML_ERROR("IndexOp source is not a Buffer, Package, or String");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("Index {}, ", index);
source->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
if (context.aml_data.size() < 1)
return ParseResult::Failure;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("IndexOp failed to resolve destination");
return ParseResult::Failure;
}
if (!destination->store(result))
return ParseResult::Failure;
}
return ParseResult(result);
}
};
}

View File

@@ -0,0 +1,39 @@
#pragma once
#include <BAN/Optional.h>
#include <BAN/String.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI::AML
{
struct Integer final : public AML::Node
{
struct Constants
{
// Initialized in Namespace::create_root_namespace
static BAN::RefPtr<Integer> Zero;
static BAN::RefPtr<Integer> One;
static BAN::RefPtr<Integer> Ones;
};
uint64_t value;
const bool constant;
Integer(uint64_t value, bool constant = false);
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop);
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
BAN::RefPtr<Node> copy() override;
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> store_node) override;
static ParseResult parse(BAN::ConstByteSpan& aml_data);
void debug_print(int indent) const override;
};
}

View File

@@ -0,0 +1,182 @@
#pragma once
#include <BAN/Function.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Register.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct Method final : public AML::Scope
{
Kernel::Mutex mutex;
uint8_t arg_count;
bool serialized;
uint8_t sync_level;
BAN::Function<BAN::RefPtr<AML::Node>(ParseContext&)> override_function;
BAN::ConstByteSpan term_list;
Method(AML::NameSeg name, uint8_t arg_count, bool serialized, uint8_t sync_level)
: AML::Scope(Node::Type::Method, name)
, arg_count(arg_count)
, serialized(serialized)
, sync_level(sync_level)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::MethodOp);
context.aml_data = context.aml_data.slice(1);
auto method_pkg = AML::parse_pkg(context.aml_data);
if (!method_pkg.has_value())
return ParseResult::Failure;
auto name_string = AML::NameString::parse(method_pkg.value());
if (!name_string.has_value())
return ParseResult::Failure;
if (method_pkg->size() < 1)
return ParseResult::Failure;
auto method_flags = method_pkg.value()[0];
method_pkg = method_pkg.value().slice(1);
auto method = MUST(BAN::RefPtr<Method>::create(
name_string.value().path.back(),
method_flags & 0x07,
(method_flags >> 3) & 0x01,
method_flags >> 4
));
if (!Namespace::root_namespace()->add_named_object(context, name_string.value(), method))
return ParseResult::Success;
method->term_list = method_pkg.value();
#if AML_DEBUG_LEVEL >= 2
method->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
BAN::Optional<BAN::RefPtr<AML::Node>> invoke(
BAN::RefPtr<AML::Node> arg0 = {},
BAN::RefPtr<AML::Node> arg1 = {},
BAN::RefPtr<AML::Node> arg2 = {},
BAN::RefPtr<AML::Node> arg3 = {},
BAN::RefPtr<AML::Node> arg4 = {},
BAN::RefPtr<AML::Node> arg5 = {},
BAN::RefPtr<AML::Node> arg6 = {}
)
{
BAN::Vector<uint8_t> sync_stack;
return invoke_with_sync_stack(sync_stack, arg0, arg1, arg2, arg3, arg4, arg5, arg6);
}
BAN::Optional<BAN::RefPtr<AML::Node>> invoke_with_sync_stack(
BAN::Vector<uint8_t>& current_sync_stack,
BAN::RefPtr<AML::Node> arg0 = {},
BAN::RefPtr<AML::Node> arg1 = {},
BAN::RefPtr<AML::Node> arg2 = {},
BAN::RefPtr<AML::Node> arg3 = {},
BAN::RefPtr<AML::Node> arg4 = {},
BAN::RefPtr<AML::Node> arg5 = {},
BAN::RefPtr<AML::Node> arg6 = {}
)
{
if (serialized && !current_sync_stack.empty() && sync_level < current_sync_stack.back())
{
AML_ERROR("Trying to evaluate method {} with lower sync level than current sync level", scope);
return {};
}
ParseContext context;
context.aml_data = term_list;
context.scope = scope;
context.method_args[0] = MUST(BAN::RefPtr<AML::Register>::create(arg0));
context.method_args[1] = MUST(BAN::RefPtr<AML::Register>::create(arg1));
context.method_args[2] = MUST(BAN::RefPtr<AML::Register>::create(arg2));
context.method_args[3] = MUST(BAN::RefPtr<AML::Register>::create(arg3));
context.method_args[4] = MUST(BAN::RefPtr<AML::Register>::create(arg4));
context.method_args[5] = MUST(BAN::RefPtr<AML::Register>::create(arg5));
context.method_args[6] = MUST(BAN::RefPtr<AML::Register>::create(arg6));
context.sync_stack = BAN::move(current_sync_stack);
for (auto& local : context.method_locals)
local = MUST(BAN::RefPtr<AML::Register>::create());
if (serialized)
{
mutex.lock();
MUST(context.sync_stack.push_back(sync_level));
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Evaluating {}", scope);
#endif
BAN::Optional<BAN::RefPtr<AML::Node>> return_value = BAN::RefPtr<AML::Node>();
if (override_function)
return_value = override_function(context);
else
{
while (context.aml_data.size() > 0)
{
auto parse_result = AML::parse_object(context);
if (parse_result.returned())
{
return_value = parse_result.node();
break;
}
if (!parse_result.success())
{
AML_ERROR("Method {} evaluate failed", scope);
return_value = {};
break;
}
}
}
if (return_value.has_value() && return_value.value() && return_value.value()->type == AML::Node::Type::Register)
return_value.value() = static_cast<AML::Register*>(return_value.value().ptr())->value;
while (!context.created_objects.empty())
{
Namespace::root_namespace()->remove_named_object(context.created_objects.back());
context.created_objects.pop_back();
}
if (serialized)
{
context.sync_stack.pop_back();
mutex.unlock();
}
current_sync_stack = BAN::move(context.sync_stack);
return return_value;
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Method ");
name.debug_print();
AML_DEBUG_PRINTLN("({} args, {}Serialized, 0x{H}) {", arg_count, serialized ? "" : "Not", sync_level);
AML_DEBUG_PRINT_INDENT(indent + 1);
AML_DEBUG_PRINTLN("TermList: {} bytes", term_list.size());
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,170 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/Lock/Mutex.h>
#include <kernel/Timer/Timer.h>
namespace Kernel::ACPI::AML
{
struct Mutex final : public AML::NamedObject
{
Kernel::Mutex mutex;
uint8_t sync_level;
bool global;
Mutex(NameSeg name, uint8_t sync_level, bool global = false)
: NamedObject(Node::Type::Mutex, name)
, sync_level(sync_level)
, global(global)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
switch (static_cast<AML::ExtOp>(context.aml_data[1]))
{
case AML::ExtOp::MutexOp:
return parse_mutex(context);
case AML::ExtOp::AcquireOp:
return parse_acquire(context);
case AML::ExtOp::ReleaseOp:
return parse_release(context);
default:
ASSERT_NOT_REACHED();
}
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("Mutex ");
name.debug_print();
AML_DEBUG_PRINT(" (SyncLevel: {})", sync_level);
}
private:
static ParseResult parse_mutex(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::MutexOp);
context.aml_data = context.aml_data.slice(2);
auto name = NameString::parse(context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
if (context.aml_data.size() < 1)
return ParseResult::Failure;
auto sync_level = context.aml_data[0];
context.aml_data = context.aml_data.slice(1);
if (sync_level & 0xF0)
{
AML_ERROR("Invalid sync level {}", sync_level);
return ParseResult::Failure;
}
auto mutex = MUST(BAN::RefPtr<Mutex>::create(name->path.back(), sync_level));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), mutex))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
mutex->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
static ParseResult parse_acquire(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::AcquireOp);
context.aml_data = context.aml_data.slice(2);
auto mutex_result = AML::parse_object(context);
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
{
AML_ERROR("Acquire does not name a valid mutex");
return ParseResult::Failure;
}
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
if (mutex->sync_level < context.sync_level())
{
AML_ERROR("Trying to acquire mutex with lower sync level than current sync level");
return ParseResult::Failure;
}
if (context.aml_data.size() < 2)
{
AML_ERROR("Missing timeout value");
return ParseResult::Failure;
}
uint16_t timeout = context.aml_data[0] | (context.aml_data[1] << 8);
context.aml_data = context.aml_data.slice(2);
if (timeout >= 0xFFFF)
mutex->mutex.lock();
else
{
// FIXME: This is a very inefficient way to wait for a mutex
uint64_t wake_time = SystemTimer::get().ms_since_boot() + timeout;
while (!mutex->mutex.try_lock())
{
if (SystemTimer::get().ms_since_boot() >= wake_time)
return ParseResult(Integer::Constants::Ones);
Processor::yield();
}
}
MUST(context.sync_stack.push_back(mutex->sync_level));
return ParseResult(Integer::Constants::Zero);
}
static ParseResult parse_release(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ReleaseOp);
context.aml_data = context.aml_data.slice(2);
auto mutex_result = AML::parse_object(context);
if (!mutex_result.success() || !mutex_result.node() || mutex_result.node()->type != AML::Node::Type::Mutex)
{
AML_ERROR("Release does not name a valid mutex");
return ParseResult::Failure;
}
if (context.sync_stack.empty())
{
AML_ERROR("Trying to release mutex without having acquired it");
return ParseResult::Failure;
}
auto* mutex = static_cast<AML::Mutex*>(mutex_result.node().ptr());
if (mutex->sync_level != context.sync_level())
{
AML_ERROR("Trying to release mutex with different sync level than current sync level");
return ParseResult::Failure;
}
mutex->mutex.unlock();
context.sync_stack.pop_back();
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,44 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/ACPI/AML/Names.h>
namespace Kernel::ACPI::AML
{
struct NamedObject : public AML::Node
{
BAN::RefPtr<NamedObject> parent;
NameSeg name;
NamedObject(Node::Type type, NameSeg name) : Node(type), name(name) {}
};
struct Name final : public AML::NamedObject
{
BAN::RefPtr<AML::Node> object;
Name(NameSeg name, BAN::RefPtr<AML::Node> object)
: NamedObject(Node::Type::Name, name), object(BAN::move(object))
{}
BAN::RefPtr<AML::Node> to_underlying() override { return object; }
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
ASSERT(object);
return object->convert(mask);
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(object);
ASSERT(object->type != AML::Node::Type::Reference);
return object->store(node);
}
static ParseResult parse(ParseContext& context);
virtual void debug_print(int indent) const override;
};
}

View File

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

View File

@@ -1,74 +1,66 @@
#pragma once
#include <BAN/Bitcast.h>
#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>
#include <kernel/ACPI/AML/Scope.h>
#include <kernel/ACPI/Headers.h>
#include <kernel/Lock/Mutex.h>
namespace Kernel::ACPI::AML
{
struct Namespace
struct Namespace final : public AML::Scope
{
BAN_NON_COPYABLE(Namespace);
BAN_NON_MOVABLE(Namespace);
public:
Namespace() = default;
~Namespace();
static BAN::RefPtr<AML::Namespace> create_root_namespace();
static BAN::RefPtr<AML::Namespace> root_namespace();
static BAN::RefPtr<AML::Node> debug_node;
static BAN::ErrorOr<void> prepare_root_namespace();
static Namespace& root_namespace();
BAN::ErrorOr<void> post_load_initialize();
BAN::ErrorOr<void> parse(BAN::ConstByteSpan);
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(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);
// node is nullptr if it is not found
struct FindResult
template<typename F>
static void for_each_child(const AML::NameString& scope, const F& callback)
{
Scope path;
Reference* node { nullptr };
auto canonical_path = root_namespace()->resolve_path(scope, {}, FindMode::ForceAbsolute);
ASSERT(canonical_path.has_value());
for (auto& [path, child] : root_namespace()->m_objects)
{
if (path.size() < canonical_path->size() + 1)
continue;
if (canonical_path->size() != 1 && path[canonical_path->size()] != '.')
continue;
if (path.sv().substring(0, canonical_path->size()) != canonical_path->sv())
continue;
if (path.sv().substring(canonical_path->size() + 1).contains('.'))
continue;
callback(path, child);
}
}
Namespace(NameSeg name) : AML::Scope(Node::Type::Namespace, name) {}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
bool parse(const SDTHeader& header);
void debug_print(int indent) const override;
enum class FindMode
{
Normal,
ForceAbsolute,
};
BAN::ErrorOr<FindResult> find_named_object(const Scope& scope, const NameString& name_string, bool force_absolute = false);
BAN::Optional<BAN::String> resolve_path(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode, bool check_existence = true) const;
BAN::ErrorOr<Scope> find_reference_scope(const Reference* reference);
// Find an object in the namespace. Returns nullptr if the object is not found.
BAN::RefPtr<NamedObject> find_object(const AML::NameString& relative_base, const AML::NameString& relative_path, FindMode mode);
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*)>&);
// Add an object to the namespace. Returns false if the parent object could not be added.
bool add_named_object(ParseContext&, const AML::NameString& object_path, BAN::RefPtr<NamedObject> object);
BAN::ErrorOr<BAN::Vector<Scope>> find_device_with_eisa_id(BAN::StringView eisa_id);
BAN::ErrorOr<BAN::Vector<Scope>> find_device_with_eisa_id(BAN::Span<BAN::StringView> eisa_ids);
// Remove an object from the namespace. Returns false if the object could not be removed.
bool remove_named_object(const AML::NameString& absolute_path);
private:
BAN::ErrorOr<Scope> resolve_path(const Scope& scope, const NameString& name_string);
BAN::ErrorOr<void> initialize_scope(const Scope& scope);
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_initialized_namespace { false };
BAN::HashMap<Scope, Reference*> m_named_objects;
BAN::HashMap<Scope, uint32_t> m_called_reg_bitmaps;
BAN::HashSet<Scope> m_aliases;
BAN::HashMap<BAN::String, BAN::RefPtr<NamedObject>> m_objects;
mutable Mutex m_object_mutex;
};
}

View File

@@ -1,449 +1,115 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/ByteSpan.h>
#include <BAN/LinkedList.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Optional.h>
#include <BAN/StringView.h>
#include <BAN/RefPtr.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/Scope.h>
#include <kernel/ACPI/Headers.h>
#include <kernel/Lock/Mutex.h>
#define AML_DUMP_FUNCTION_CALLS 0
#define AML_ENABLE_DEBUG 1
#include <kernel/ACPI/AML/Utils.h>
namespace Kernel::ACPI::AML
{
struct NameString
struct Buffer;
struct Integer;
struct String;
struct Node : public BAN::RefCounted<Node>
{
BAN_NON_COPYABLE(NameString);
public:
NameString() = default;
NameString(NameString&& other)
: base(other.base)
, parts(BAN::move(other.parts))
{}
NameString& operator=(NameString&& other)
static uint64_t total_node_count;
enum Conversion : uint8_t
{
base = other.base;
parts = BAN::move(other.parts);
return *this;
}
static BAN::ErrorOr<NameString> from_string(BAN::StringView name);
BAN::ErrorOr<NameString> copy() const
{
NameString result;
result.base = this->base;
TRY(result.parts.resize(parts.size()));
for (size_t i = 0; i < parts.size(); i++)
result.parts[i] = parts[i];
return result;
}
static constexpr uint32_t base_root = -1;
uint32_t base { 0 };
BAN::Vector<uint32_t> parts;
};
template<typename T1, typename T2>
struct Pair
{
T1 elem1;
T2 elem2;
};
struct Node;
struct ParseContext;
struct Reference;
struct Mutex
{
Kernel::Mutex mutex;
uint8_t sync_level;
bool global_lock;
uint32_t ref_count;
};
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[];
};
struct OpRegion
{
GAS::AddressSpaceID address_space;
uint64_t offset;
uint64_t length;
alignas(Scope) uint8_t scope_storage[sizeof(Scope)];
Scope& scope() { return *reinterpret_cast<Scope*>(scope_storage); }
const Scope& scope() const { return *reinterpret_cast<const Scope*>(scope_storage); }
};
struct FieldUnit
{
enum class Type {
Field,
IndexField,
BankField,
};
uint64_t offset;
uint64_t length;
uint8_t flags;
Type type;
union {
struct {
OpRegion opregion;
} field;
struct {
Reference* index;
Reference* data;
} index_field;
struct {
OpRegion opregion;
Reference* bank_selector;
uint64_t bank_value;
} bank_field;
} as;
};
struct Package
{
struct Element
{
struct Location {
NameString name;
Scope scope;
};
bool resolved { true };
union {
Node* node { nullptr };
Location* location;
} value;
ConvBuffer = 1 << 0,
ConvBufferField = 1 << 1,
ConvFieldUnit = 1 << 2,
ConvInteger = 1 << 3,
ConvString = 1 << 4,
};
uint64_t num_elements;
uint32_t ref_count;
Element elements[];
};
struct Node
{
BAN_NON_COPYABLE(Node);
public:
Node() = default;
~Node() { clear(); }
Node(Node&& other) { *this = BAN::move(other); }
Node& operator=(Node&&);
static BAN::ErrorOr<Node> create_string(BAN::StringView string)
enum class Type : uint8_t
{
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,
Debug,
Integer,
String,
None,
BankFieldElement,
Buffer,
Package,
BufferField,
OpRegion,
FieldUnit,
Event,
Debug,
Device,
Processor,
PowerResource,
ThermalZone,
Event,
FieldElement,
IndexFieldElement,
Integer,
Method,
Mutex,
// FIXME: Index should not be its own type
// parsing index should return references
Index,
Name,
Namespace,
OpRegion,
Package,
PackageElement,
PowerResource,
Processor,
Reference,
PredefinedScope,
Count
} type { Type::Uninitialized };
Register,
String,
ThermalZone,
};
const Type type;
inline bool is_scope() const
Node(Type type) : type(type) { total_node_count++; }
virtual ~Node() { total_node_count--; }
virtual bool is_scope() const { return false; }
[[nodiscard]] virtual BAN::RefPtr<AML::Node> to_underlying() { return this; }
[[nodiscard]] virtual BAN::RefPtr<AML::Node> convert(uint8_t mask) = 0;
[[nodiscard]] virtual BAN::RefPtr<Node> copy() { return this; }
[[nodiscard]] virtual BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node>) { AML_TODO("store, type {}", static_cast<uint8_t>(type)); return {}; }
virtual void debug_print(int indent) const = 0;
};
struct ParseContext;
struct ParseResult
{
static ParseResult Failure;
static ParseResult Success;
enum class Result
{
switch (type)
{
case Type::Device:
case Type::Processor:
case Type::PowerResource:
case Type::ThermalZone:
case Type::PredefinedScope:
return true;
default:
return false;
}
ASSERT_NOT_REACHED();
Success,
Failure,
Returned,
Breaked,
Continued,
};
ParseResult(Result success)
: m_result(success)
{}
ParseResult(Result success, BAN::RefPtr<Node> node)
: m_result(success)
, m_node(BAN::move(node))
{}
ParseResult(BAN::RefPtr<Node> node)
: m_result(Result::Success)
, m_node(BAN::move(node))
{
ASSERT(m_node);
}
union
bool success() const { return m_result == Result::Success; }
bool returned() const { return m_result == Result::Returned; }
bool breaked() const { return m_result == Result::Breaked; }
bool continued() const { return m_result == Result::Continued; }
BAN::RefPtr<Node> node()
{
struct {
uint64_t value;
} integer;
Package* package;
Buffer* str_buf;
struct {
Buffer* buffer;
uint64_t bit_offset;
uint64_t bit_count;
} buffer_field;
OpRegion opregion;
FieldUnit field_unit;
struct {
uint32_t signal_count;
} event;
struct {
uint8_t storage[sizeof(Kernel::Mutex)];
const uint8_t* start;
size_t length;
uint8_t arg_count;
BAN::ErrorOr<Node> (*override_func)(const BAN::Array<Reference*, 7>&);
bool serialized;
Mutex* mutex;
} method;
Mutex* mutex;
struct {
Node::Type type;
union {
Buffer* str_buf;
Package* package;
} as;
uint64_t index;
} index;
Reference* reference;
} as;
return m_node;
}
BAN::ErrorOr<Node> copy() const;
void clear();
private:
Result m_result = Result::Failure;
BAN::RefPtr<Node> m_node;
};
struct Reference
{
Node node {};
uint32_t ref_count { 1 };
};
struct ParseContext
{
Scope scope;
BAN::ConstByteSpan aml_data;
uint32_t call_depth { 0 };
BAN::Array<Reference*, 8> locals;
BAN::Array<Reference*, 7> args;
BAN::LinkedList<Scope> created_nodes;
~ParseContext();
BAN::ErrorOr<void> allocate_locals();
};
enum class ExecutionFlow
{
Normal,
Break,
Continue,
Return,
};
using ExecutionFlowResult = Pair<ExecutionFlow, BAN::Optional<Node>>;
enum Conversion : uint8_t
{
ConvInteger = 1,
ConvBuffer = 2,
ConvString = 4,
};
BAN::ErrorOr<Node> parse_node(ParseContext& context, bool return_ref = false);
BAN::ErrorOr<ExecutionFlowResult> parse_node_or_execution_flow(ParseContext& context);
BAN::ErrorOr<NameString> parse_name_string(BAN::ConstByteSpan& aml_data);
BAN::ErrorOr<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data);
BAN::ErrorOr<Node> convert_node(Node&& source, uint8_t conversion, uint64_t max_length);
BAN::ErrorOr<Node> convert_node(Node&& source, const Node& target);
BAN::ErrorOr<Node> evaluate_node(const Scope& node_path, const Node& node);
// 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);
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const Kernel::ACPI::AML::NameString& name_string, const ValueFormat&)
{
if (name_string.base == Kernel::ACPI::AML::NameString::base_root)
putc('\\');
else for (uint32_t i = 0; i < name_string.base; i++)
putc('^');
for (size_t i = 0; i < name_string.parts.size(); i++) {
if (i != 0)
putc('.');
const char* name_seg = reinterpret_cast<const char*>(&name_string.parts[i]);
putc(name_seg[0]); putc(name_seg[1]); putc(name_seg[2]); putc(name_seg[3]);
}
}
template<typename F>
void print_argument(F putc, const Kernel::ACPI::AML::Buffer& buffer, const ValueFormat&)
{
static constexpr size_t max_elements { 16 };
print(putc, "<buffer '");
if (buffer.size)
print(putc, "{2H}", buffer.bytes[0]);
for (size_t i = 1; i < buffer.size && i < max_elements; i++)
print(putc, " {2H}", buffer.bytes[i]);
if (buffer.size > max_elements)
print(putc, "...");
print(putc, "'>");
}
template<typename F>
void print_argument(F putc, const Kernel::ACPI::AML::Package& package, const ValueFormat&)
{
print(putc, "<package '{} elements'>", package.num_elements);
}
template<typename F>
void print_argument(F putc, const Kernel::ACPI::AML::Node& node, const ValueFormat&)
{
switch (node.type)
{
case Kernel::ACPI::AML::Node::Type::Uninitialized:
print(putc, "<uninitialized>");
break;
case Kernel::ACPI::AML::Node::Type::Debug:
print(putc, "<debug>");
break;
case Kernel::ACPI::AML::Node::Type::Integer:
print(putc, "<integer 0x{H}>", node.as.integer.value);
break;
case Kernel::ACPI::AML::Node::Type::String:
print(putc, "<string '{}'>", node.as.str_buf->as_sv());
break;
case Kernel::ACPI::AML::Node::Type::Package:
print(putc, "{}", *node.as.package);
break;
case Kernel::ACPI::AML::Node::Type::Buffer:
print(putc, "{}", *node.as.str_buf);
break;
case Kernel::ACPI::AML::Node::Type::BufferField:
print(putc, "<buffer field '{} bytes, offset 0x{H}, bit count {}'>",
node.as.buffer_field.buffer->size,
node.as.buffer_field.bit_offset,
node.as.buffer_field.bit_count
);
break;
case Kernel::ACPI::AML::Node::Type::OpRegion:
print(putc, "<opregion 'type {2H}, offset 0x{H}, length 0x{H}'>",
static_cast<uint8_t>(node.as.opregion.address_space),
node.as.opregion.offset,
node.as.opregion.length
);
break;
case Kernel::ACPI::AML::Node::Type::FieldUnit:
print(putc, "<field unit ({}), 'offset 0x{H}, length 0x{H}'>",
static_cast<uint8_t>(node.as.field_unit.type),
node.as.field_unit.offset,
node.as.field_unit.length
);
break;
case Kernel::ACPI::AML::Node::Type::Event:
print(putc, "<event '{} signals'>", node.as.event.signal_count);
break;
case Kernel::ACPI::AML::Node::Type::Device:
print(putc, "<device>");
break;
case Kernel::ACPI::AML::Node::Type::Processor:
print(putc, "<processor>");
break;
case Kernel::ACPI::AML::Node::Type::PowerResource:
print(putc, "<power resouce>");
break;
case Kernel::ACPI::AML::Node::Type::ThermalZone:
print(putc, "<thermal zone>");
break;
case Kernel::ACPI::AML::Node::Type::Method:
print(putc, "<method '{} bytes'>", node.as.method.length);
break;
case Kernel::ACPI::AML::Node::Type::Mutex:
print(putc, "<mutex>");
break;
case Kernel::ACPI::AML::Node::Type::Index:
switch (node.as.index.type)
{
case Kernel::ACPI::AML::Node::Type::String:
case Kernel::ACPI::AML::Node::Type::Buffer:
print(putc, "<index {}, {}>", *node.as.index.as.str_buf, node.as.index.index);
break;
case Kernel::ACPI::AML::Node::Type::Package:
print(putc, "<index {}, {}>", *node.as.index.as.package, node.as.index.index);
break;
default:
print(putc, "<index {}??, {}>", (uint32_t)node.as.index.type, node.as.index.index);
break;
}
break;
case Kernel::ACPI::AML::Node::Type::Reference:
print(putc, "<reference {}, {} refs>", node.as.reference->node, node.as.reference->ref_count);
break;
case Kernel::ACPI::AML::Node::Type::PredefinedScope:
print(putc, "<scope>");
break;
case Kernel::ACPI::AML::Node::Type::Count:
ASSERT_NOT_REACHED();
}
}
ParseResult parse_object(ParseContext& context);
}

View File

@@ -0,0 +1,67 @@
#pragma once
#include <kernel/ACPI/AML/Device.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Processor.h>
#include <kernel/ACPI/AML/ThermalZone.h>
namespace Kernel::ACPI::AML
{
struct Notify
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::NotifyOp);
context.aml_data = context.aml_data.slice(1);
auto object_result = AML::parse_object(context);
if (!object_result.success())
return ParseResult::Failure;
auto object = object_result.node();
if (!object)
{
AML_ERROR("Notify object is null");
return ParseResult::Failure;
}
auto value_result = AML::parse_object(context);
if (!value_result.success())
return ParseResult::Failure;
auto value_node = value_result.node() ? value_result.node()->convert(AML::Node::ConvInteger) : BAN::RefPtr<AML::Node>();
if (!value_node)
{
AML_ERROR("Notify value is not an integer");
return ParseResult::Failure;
}
const auto value = static_cast<AML::Integer*>(value_node.ptr())->value;
BAN::StringView object_type_sv;
BAN::StringView object_name_sv;
switch (object->type)
{
case AML::Node::Type::Device:
object_type_sv = "Device"_sv;
object_name_sv = static_cast<AML::Device*>(object.ptr())->name.sv();
break;
case AML::Node::Type::Processor:
object_type_sv = "Processor"_sv;
object_name_sv = static_cast<AML::Processor*>(object.ptr())->name.sv();
break;
case AML::Node::Type::ThermalZone:
object_type_sv = "ThermalZone"_sv;
object_name_sv = static_cast<AML::ThermalZone*>(object.ptr())->name.sv();
break;
default:
object_type_sv = "Unknown"_sv;
object_name_sv = "????"_sv;
break;
}
AML_TODO("Notify: {} {}: {2H}", object_type_sv, object_name_sv, value);
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,107 @@
#pragma once
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Reference.h>
namespace Kernel::ACPI::AML
{
struct ObjectType
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::ObjectTypeOp);
context.aml_data = context.aml_data.slice(1);
auto object_result = AML::parse_object(context);
if (!object_result.success())
return ParseResult::Failure;
auto object = object_result.node()
? object_result.node()->to_underlying()
: BAN::RefPtr<AML::Node>();
if (object && object->type == AML::Node::Type::Reference)
object = static_cast<AML::Reference*>(object.ptr())->node->to_underlying();
uint64_t value = 0;
if (object)
{
switch (object->type)
{
case AML::Node::Type::None:
case AML::Node::Type::Name:
case AML::Node::Type::PackageElement:
case AML::Node::Type::Reference:
case AML::Node::Type::Register:
ASSERT_NOT_REACHED();
case AML::Node::Type::Namespace:
value = 0;
break;
case AML::Node::Type::Integer:
value = 1;
break;
case AML::Node::Type::String:
value = 2;
break;
case AML::Node::Type::Buffer:
value = 3;
break;
case AML::Node::Type::Package:
value = 4;
break;
case AML::Node::Type::FieldElement:
case AML::Node::Type::BankFieldElement:
case AML::Node::Type::IndexFieldElement:
value = 5;
break;
case AML::Node::Type::Device:
value = 6;
break;
case AML::Node::Type::Event:
value = 7;
break;
case AML::Node::Type::Method:
value = 8;
break;
case AML::Node::Type::Mutex:
value = 9;
break;
case AML::Node::Type::OpRegion:
value = 10;
break;
case AML::Node::Type::PowerResource:
value = 11;
break;
case AML::Node::Type::Processor:
value = 12;
break;
case AML::Node::Type::ThermalZone:
value = 13;
break;
case AML::Node::Type::BufferField:
value = 14;
break;
case AML::Node::Type::Debug:
value = 16;
break;
}
}
#if AML_DEBUG_LEVEL >= 2
if (!object)
AML_DEBUG_PRINTLN("ObjectType { null }");
else
{
AML_DEBUG_PRINTLN("ObjectType {");
object->debug_print(1);
AML_DEBUG_PRINTLN("");
}
#endif
return ParseResult(MUST(BAN::RefPtr<AML::Integer>::create(value)));
}
};
}

View File

@@ -1,16 +0,0 @@
#pragma once
#include <kernel/ACPI/AML/Node.h>
namespace Kernel::ACPI::AML
{
BAN::ErrorOr<void> parse_opregion_op(ParseContext& context);
BAN::ErrorOr<void> parse_field_op(ParseContext& context);
BAN::ErrorOr<void> parse_index_field_op(ParseContext& context);
BAN::ErrorOr<void> parse_bank_field_op(ParseContext& context);
BAN::ErrorOr<Node> convert_from_field_unit(const Node& node, uint8_t conversion, uint64_t max_length);
BAN::ErrorOr<void> store_to_field_unit(const Node& source, const Node& target);
}

View File

@@ -0,0 +1,168 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Reference.h>
namespace Kernel::ACPI::AML
{
struct PackageElement;
struct Package final : public AML::Node
{
BAN::Vector<BAN::RefPtr<PackageElement>> elements;
AML::NameString scope;
Package(AML::NameString scope)
: Node(Node::Type::Package)
, elements(BAN::move(elements))
, scope(scope)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(AML::ParseContext& context);
virtual void debug_print(int indent) const override;
};
struct PackageElement final : public AML::Node
{
BAN::RefPtr<AML::Package> parent;
BAN::RefPtr<AML::Node> element;
AML::NameString unresolved_name;
bool resolved = false;
bool initialized = false;
PackageElement(BAN::RefPtr<AML::Package> parent, BAN::RefPtr<AML::Node> element)
: Node(Node::Type::PackageElement)
, parent(parent)
, element(element)
{
ASSERT(element);
resolved = true;
initialized = true;
}
PackageElement(BAN::RefPtr<AML::Package> parent, AML::NameString unresolved_name)
: Node(Node::Type::PackageElement)
, parent(parent)
, unresolved_name(unresolved_name)
{
resolved = false;
initialized = true;
}
PackageElement(BAN::RefPtr<AML::Package> parent)
: Node(Node::Type::PackageElement)
, parent(parent)
{
resolved = false;
initialized = false;
}
bool resolve()
{
ASSERT(!resolved);
auto object = Namespace::root_namespace()->find_object(parent->scope, unresolved_name, Namespace::FindMode::Normal);
if (!object)
{
AML_ERROR("Failed to resolve reference {} in package {}", unresolved_name, parent->scope);
return false;
}
element = object;
resolved = true;
return true;
}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
if (!initialized)
{
AML_ERROR("Trying to convert uninitialized PackageElement");
return {};
}
if (!resolved && !resolve())
return {};
return element->convert(mask);
}
BAN::RefPtr<AML::Node> to_underlying() override
{
if (!initialized)
{
AML_ERROR("Trying to read uninitialized PackageElement");
return {};
}
if (!resolved && !resolve())
return {};
return element;
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
{
if (!initialized)
{
initialized = true;
resolved = true;
}
if (!resolved && !resolve())
return {};
ASSERT(!element || element->type != AML::Node::Type::Reference);
if (node->type == AML::Node::Type::Reference)
element = static_cast<AML::Reference*>(node.ptr())->node;
else
element = node->copy();
return node;
}
static ParseResult parse(AML::ParseContext& context, BAN::RefPtr<AML::Package> package)
{
BAN::RefPtr<AML::PackageElement> element;
if (context.aml_data[0] != 0x00 && AML::NameString::can_parse(context.aml_data))
{
auto name = AML::NameString::parse(context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
element = MUST(BAN::RefPtr<PackageElement>::create(package, name.value()));
}
else
{
auto element_result = AML::parse_object(context);
if (!element_result.success())
return ParseResult::Failure;
element = MUST(BAN::RefPtr<PackageElement>::create(package, element_result.node()));
}
return ParseResult(element);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("PackageElement {");
if (!initialized)
{
AML_DEBUG_PRINT_INDENT(indent + 1);
AML_DEBUG_PRINT("Uninitialized");
}
else if (!resolved)
{
AML_DEBUG_PRINT_INDENT(indent + 1);
AML_DEBUG_PRINT("Unresolved {}", unresolved_name);
}
else
{
element->debug_print(indent + 1);
}
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/ByteSpan.h>
#include <BAN/LinkedList.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/Register.h>
namespace Kernel::ACPI::AML
{
struct ParseContext
{
BAN::ConstByteSpan aml_data;
AML::NameString scope;
// Used for cleaning up on method exit
// NOTE: This uses linked list instead of vector because
// we don't really need large contiguous memory
BAN::LinkedList<AML::NameString> created_objects;
uint8_t sync_level() const { return !sync_stack.empty() ? sync_stack.back() : 0; }
BAN::Vector<uint8_t> sync_stack;
BAN::Array<BAN::RefPtr<Register>, 7> method_args;
BAN::Array<BAN::RefPtr<Register>, 8> method_locals;
};
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/Optional.h>
namespace Kernel::ACPI::AML
{
static BAN::Optional<uint32_t> parse_pkg_length(BAN::ConstByteSpan aml_data)
{
if (aml_data.size() < 1)
return {};
uint8_t lead_byte = aml_data[0];
if ((lead_byte & 0xC0) && (lead_byte & 0x30))
return {};
uint32_t pkg_length = lead_byte & 0x3F;
uint8_t byte_count = (lead_byte >> 6) + 1;
if (aml_data.size() < byte_count)
return {};
for (uint8_t i = 1; i < byte_count; i++)
pkg_length |= aml_data[i] << (i * 8 - 4);
return pkg_length;
}
static void trim_pkg_length(BAN::ConstByteSpan& aml_data)
{
ASSERT(aml_data.size() >= 1);
uint8_t byte_count = (aml_data[0] >> 6) + 1;
aml_data = aml_data.slice(byte_count);
}
static BAN::Optional<BAN::ConstByteSpan> parse_pkg(BAN::ConstByteSpan& aml_data)
{
auto pkg_length = parse_pkg_length(aml_data);
if (!pkg_length.has_value())
return {};
auto result = aml_data.slice(0, pkg_length.value());
trim_pkg_length(result);
aml_data = aml_data.slice(pkg_length.value());
return result;
}
}

View File

@@ -0,0 +1,70 @@
#pragma once
#include <BAN/Endianness.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct PowerResource final : public AML::Scope
{
uint8_t system_level;
uint16_t resource_order;
PowerResource(NameSeg name, uint8_t system_level, uint16_t resource_order)
: Scope(Node::Type::PowerResource, name)
, system_level(system_level)
, resource_order(resource_order)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::PowerResOp);
context.aml_data = context.aml_data.slice(2);
auto opt_power_res_pkg = AML::parse_pkg(context.aml_data);
if (!opt_power_res_pkg.has_value())
return ParseResult::Failure;
auto power_res_pkg = opt_power_res_pkg.value();
auto name = NameString::parse(power_res_pkg);
if (!name.has_value())
return ParseResult::Failure;
if (power_res_pkg.size() < 1)
return ParseResult::Failure;
uint8_t system_level = power_res_pkg[0];
power_res_pkg = power_res_pkg.slice(1);
if (power_res_pkg.size() < 2)
return ParseResult::Failure;
uint16_t resource_order = BAN::little_endian_to_host<uint16_t>(*reinterpret_cast<const uint16_t*>(power_res_pkg.data()));
power_res_pkg = power_res_pkg.slice(2);
auto power_res = MUST(BAN::RefPtr<PowerResource>::create(name->path.back(), system_level, resource_order));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), power_res))
return ParseResult::Failure;
#if AML_DEBUG_LEVEL >= 2
power_res->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return power_res->enter_context_and_parse_term_list(context, name.value(), power_res_pkg);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("PowerResource {} (SystemLevel {}, ResourceOrder {})", name, system_level, resource_order);
}
};
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <BAN/Endianness.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct Processor final : public AML::Scope
{
uint8_t id;
uint32_t pblk_addr;
uint8_t pblk_len;
Processor(NameSeg name, uint8_t id, uint32_t pblk_addr, uint8_t pblk_len)
: Scope(Node::Type::Processor, name)
, id(id)
, pblk_addr(pblk_addr)
, pblk_len(pblk_len)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ProcessorOp);
context.aml_data = context.aml_data.slice(2);
auto opt_processor_pkg = AML::parse_pkg(context.aml_data);
if (!opt_processor_pkg.has_value())
return ParseResult::Failure;
auto processor_pkg = opt_processor_pkg.value();
auto name = NameString::parse(processor_pkg);
if (!name.has_value())
return ParseResult::Failure;
if (processor_pkg.size() < 1)
return ParseResult::Failure;
uint8_t id = processor_pkg[0];
processor_pkg = processor_pkg.slice(1);
if (processor_pkg.size() < 4)
return ParseResult::Failure;
uint32_t pblk_addr = BAN::little_endian_to_host<uint32_t>(*reinterpret_cast<const uint32_t*>(processor_pkg.data()));
processor_pkg = processor_pkg.slice(4);
if (processor_pkg.size() < 1)
return ParseResult::Failure;
uint8_t pblk_len = processor_pkg[0];
processor_pkg = processor_pkg.slice(1);
auto processor = MUST(BAN::RefPtr<Processor>::create(name->path.back(), id, pblk_addr, pblk_len));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), processor))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
processor->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return processor->enter_context_and_parse_term_list(context, name.value(), processor_pkg);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Processor {} (ID: {}, PBlkAddr: 0x{H}, PBlkLen: {}) {", name, id, pblk_addr, pblk_len);
Namespace::root_namespace()->for_each_child(scope,
[&](const auto&, const auto& child)
{
child->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
}
);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,186 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/Names.h>
#include <kernel/ACPI/AML/String.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Reference final : public AML::Node
{
BAN::RefPtr<AML::Node> node;
Reference(BAN::RefPtr<AML::Node> node)
: Node(AML::Node::Type::Reference)
, node(node)
{
ASSERT(node);
}
BAN::RefPtr<AML::Node> convert(uint8_t mask) override
{
ASSERT(node);
return node->convert(mask);
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> value) override
{
ASSERT(node);
return node->store(value);
}
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
bool conditional = false;
switch (static_cast<AML::Byte>(context.aml_data[0]))
{
case AML::Byte::DerefOfOp:
return parse_dereference(context);
case AML::Byte::RefOfOp:
context.aml_data = context.aml_data.slice(1);
conditional = false;
break;
case AML::Byte::ExtOpPrefix:
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::CondRefOfOp);
context.aml_data = context.aml_data.slice(2);
conditional = true;
break;
default:
ASSERT_NOT_REACHED();
}
BAN::RefPtr<AML::Node> object;
if (NameString::can_parse(context.aml_data))
{
auto name = NameString::parse(context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
object = Namespace::root_namespace()->find_object(context.scope, name.value(), Namespace::FindMode::Normal);
}
else
{
auto parse_result = AML::parse_object(context);
if (!parse_result.success())
return ParseResult::Failure;
object = parse_result.node();
}
if (object && object->type == AML::Node::Type::Reference)
object = static_cast<AML::Reference*>(object.ptr())->node;
if (object && object->type == AML::Node::Type::Register)
object = static_cast<AML::Register*>(object.ptr())->value;
if (!conditional)
{
if (!object)
{
AML_ERROR("RefOf failed to resolve reference");
return ParseResult::Failure;
}
auto reference = MUST(BAN::RefPtr<Reference>::create(object));
#if AML_DEBUG_LEVEL >= 2
reference->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult(reference);
}
if (context.aml_data.size() < 1)
{
AML_ERROR("CondRefOf missing target");
return ParseResult::Failure;
}
BAN::RefPtr<AML::Node> target_node;
if (context.aml_data[0] == 0x00)
context.aml_data = context.aml_data.slice(1);
else
{
auto target_result = AML::parse_object(context);
if (!target_result.success())
return ParseResult::Failure;
target_node = target_result.node();
if (!target_node)
{
AML_ERROR("CondRefOf failed to resolve target");
return ParseResult::Failure;
}
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("CondRefOf ");
if (object)
object->debug_print(0);
else
AML_DEBUG_PRINT("null");
AML_DEBUG_PRINTLN("");
#endif
if (!object)
return AML::ParseResult(Integer::Constants::Zero);
if (target_node && !target_node->store(object))
{
AML_ERROR("CondRefOf failed to store into target");
return ParseResult::Failure;
}
return AML::ParseResult(Integer::Constants::Ones);
}
static ParseResult parse_dereference(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::DerefOfOp);
context.aml_data = context.aml_data.slice(1);
if (context.aml_data.size() >= 1 && static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::StringPrefix)
{
auto string_result = AML::String::parse(context);
if (!string_result.success())
return ParseResult::Failure;
ASSERT(string_result.node());
auto string = static_cast<AML::String*>(string_result.node().ptr());
AML_TODO("DerefOf String ({})", string->string);
return ParseResult::Failure;
}
else
{
auto parse_result = AML::parse_object(context);
if (!parse_result.success())
return ParseResult::Failure;
auto node = parse_result.node();
if (node && node->type == AML::Node::Type::Register)
node = static_cast<AML::Register*>(node.ptr())->value;
if (!node || node->type != AML::Node::Type::Reference)
{
AML_TODO("DerefOf source is not a Reference, but a {}", node ? static_cast<uint8_t>(node->type) : 999);
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINT("DerefOf ");
node->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult(static_cast<Reference*>(node.ptr())->node);
}
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINTLN("Reference {");
node->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,116 @@
#pragma once
#include <kernel/ACPI/AML/Integer.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Namespace.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct OpRegion final : public AML::NamedObject
{
using RegionSpace = GAS::AddressSpaceID;
RegionSpace region_space;
uint64_t region_offset;
uint64_t region_length;
Kernel::Mutex mutex;
OpRegion(NameSeg name, RegionSpace region_space, uint64_t region_offset, uint64_t region_length)
: NamedObject(Node::Type::OpRegion, name)
, region_space(region_space)
, region_offset(region_offset)
, region_length(region_length)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(AML::ParseContext& context)
{
ASSERT(context.aml_data.size() > 2);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::ExtOpPrefix);
ASSERT(static_cast<ExtOp>(context.aml_data[1]) == ExtOp::OpRegionOp);
context.aml_data = context.aml_data.slice(2);
auto name = NameString::parse(context.aml_data);
if (!name.has_value())
return ParseResult::Failure;
if (context.aml_data.size() < 1)
return ParseResult::Failure;
auto region_space = static_cast<RegionSpace>(context.aml_data[0]);
context.aml_data = context.aml_data.slice(1);
auto offset_result = AML::parse_object(context);
if (!offset_result.success())
return ParseResult::Failure;
auto offset_node = offset_result.node()
? offset_result.node()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!offset_node)
{
AML_ERROR("OpRegion offset must be an integer");
return ParseResult::Failure;
}
auto length_result = AML::parse_object(context);
if (!length_result.success())
return ParseResult::Failure;
auto length_node = length_result.node()
? length_result.node()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!length_node)
{
AML_ERROR("OpRegion length must be an integer");
return ParseResult::Failure;
}
const auto offset = static_cast<AML::Integer*>(offset_node.ptr())->value;
const auto length = static_cast<AML::Integer*>(length_node.ptr())->value;
auto op_region = MUST(BAN::RefPtr<OpRegion>::create(
name->path.back(),
region_space,
offset,
length
));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), op_region))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
op_region->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return ParseResult::Success;
}
virtual void debug_print(int indent) const override
{
BAN::StringView region_space_name;
switch (region_space)
{
case RegionSpace::SystemMemory: region_space_name = "SystemMemory"_sv; break;
case RegionSpace::SystemIO: region_space_name = "SystemIO"_sv; break;
case RegionSpace::PCIConfig: region_space_name = "PCIConfig"_sv; break;
case RegionSpace::EmbeddedController: region_space_name = "EmbeddedController"_sv; break;
case RegionSpace::SMBus: region_space_name = "SMBus"_sv; break;
case RegionSpace::SystemCMOS: region_space_name = "SystemCMOS"_sv; break;
case RegionSpace::PCIBarTarget: region_space_name = "PCIBarTarget"_sv; break;
case RegionSpace::IPMI: region_space_name = "IPMI"_sv; break;
case RegionSpace::GeneralPurposeIO: region_space_name = "GeneralPurposeIO"_sv; break;
case RegionSpace::GenericSerialBus: region_space_name = "GenericSerialBus"_sv; break;
case RegionSpace::PlatformCommunicationChannel: region_space_name = "PlatformCommunicationChannel"_sv; break;
default: region_space_name = "Unknown"_sv; break;
}
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("OperationRegion(");
name.debug_print();
AML_DEBUG_PRINT(", {}, 0x{H}, 0x{H})", region_space_name, region_offset, region_length);
}
};
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <kernel/ACPI/AML/Node.h>
namespace Kernel::ACPI::AML
{
struct Register final : public AML::Node
{
BAN::RefPtr<AML::Node> value;
Register();
Register(BAN::RefPtr<AML::Node> node);
BAN::RefPtr<AML::Node> to_underlying() override { return value->to_underlying(); }
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> source) override;
void debug_print(int indent) const override;
};
}

View File

@@ -1,74 +1,27 @@
#pragma once
#include <BAN/Hash.h>
#include <BAN/NoCopyMove.h>
#include <BAN/Vector.h>
#include <kernel/ACPI/AML/NamedObject.h>
#include <kernel/ACPI/AML/Names.h>
namespace Kernel::ACPI::AML
{
struct Scope
struct Scope : public AML::NamedObject
{
BAN_NON_COPYABLE(Scope);
public:
Scope() = default;
Scope(Scope&& other) { *this = BAN::move(other); }
Scope& operator=(Scope&& other) { parts = BAN::move(other.parts); return *this; }
AML::NameString scope;
BAN::Vector<uint32_t> parts;
Scope(Node::Type type, NameSeg name)
: NamedObject(type, name)
{}
BAN::ErrorOr<Scope> copy() const
{
Scope result;
TRY(result.parts.reserve(parts.size()));
for (uint32_t part : parts)
TRY(result.parts.push_back(part));
return result;
}
virtual bool is_scope() const override { return true; }
bool operator==(const Scope& other) const
{
if (parts.size() != other.parts.size())
return false;
for (size_t i = 0; i < parts.size(); i++)
if (parts[i] != other.parts[i])
return false;
return true;
}
static ParseResult parse(ParseContext& context);
protected:
ParseResult enter_context_and_parse_term_list(ParseContext& outer_context, const AML::NameString& name, BAN::ConstByteSpan aml_data);
};
}
namespace BAN
{
template<>
struct hash<Kernel::ACPI::AML::Scope>
{
hash_t operator()(const Kernel::ACPI::AML::Scope& scope) const
{
hash_t hash { 0 };
for (uint32_t part : scope.parts)
hash ^= u32_hash(part);
return hash;
}
};
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const Kernel::ACPI::AML::Scope& scope, const ValueFormat&)
{
putc('\\');
for (size_t i = 0; i < scope.parts.size(); i++) {
if (i != 0)
putc('.');
const char* name_seg = reinterpret_cast<const char*>(&scope.parts[i]);
putc(name_seg[0]); putc(name_seg[1]); putc(name_seg[2]); putc(name_seg[3]);
}
}
bool initialize_scope(BAN::RefPtr<Scope> scope);
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include <kernel/ACPI/AML/Buffer.h>
#include <kernel/ACPI/AML/Names.h>
#include <kernel/ACPI/AML/Package.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Reference.h>
namespace Kernel::ACPI::AML
{
struct SizeOf
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::SizeOfOp);
context.aml_data = context.aml_data.slice(1);
auto object_result = AML::parse_object(context);
if (!object_result.success())
return ParseResult::Failure;
auto object_node = object_result.node();
if (object_node)
object_node = object_node->to_underlying();
if (!object_node)
{
AML_ERROR("SizeOf object is null");
return ParseResult::Failure;
}
if (object_node->type != AML::Node::Type::Package)
object_node = object_node->convert(AML::Node::ConvBuffer | AML::Node::ConvString);
if (!object_node)
{
AML_ERROR("SizeOf object is not Buffer, String or Package");
return ParseResult::Failure;
}
uint64_t size = 0;
switch (object_node->type)
{
case AML::Node::Type::Buffer:
size = static_cast<AML::Buffer*>(object_node.ptr())->buffer.size();
break;
case AML::Node::Type::String:
size = static_cast<AML::String*>(object_node.ptr())->string.size();
break;
case AML::Node::Type::Package:
size = static_cast<AML::Package*>(object_node.ptr())->elements.size();
break;
default:
ASSERT_NOT_REACHED();
}
return ParseResult(MUST(BAN::RefPtr<Integer>::create(size)));
}
};
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/Timer/Timer.h>
namespace Kernel::ACPI::AML
{
struct Sleep
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::SleepOp);
context.aml_data = context.aml_data.slice(2);
auto sleep_time_result = AML::parse_object(context);
if (!sleep_time_result.success())
return ParseResult::Failure;
auto sleep_time_node = sleep_time_result.node()
? sleep_time_result.node()->convert(AML::Node::ConvInteger)
: BAN::RefPtr<AML::Node>();
if (!sleep_time_node)
{
AML_ERROR("Sleep time cannot be evaluated to an integer");
return ParseResult::Failure;
}
const auto sleep_time_value = static_cast<AML::Integer*>(sleep_time_node.ptr())->value;
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Sleeping for {} ms", sleep_time_value);
#endif
SystemTimer::get().sleep_ms(sleep_time_value);
return ParseResult::Success;
}
};
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct Store
{
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 1);
ASSERT(static_cast<Byte>(context.aml_data[0]) == Byte::StoreOp);
context.aml_data = context.aml_data.slice(1);
auto source_result = AML::parse_object(context);
if (!source_result.success())
return ParseResult::Failure;
auto source = source_result.node();
if (!source)
{
AML_ERROR("Store source is null");
return ParseResult::Failure;
}
auto destination_result = AML::parse_object(context);
if (!destination_result.success())
return ParseResult::Failure;
auto destination = destination_result.node();
if (!destination)
{
AML_ERROR("Store destination is null");
return ParseResult::Failure;
}
#if AML_DEBUG_LEVEL >= 2
AML_DEBUG_PRINTLN("Storing {");
source->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("} to {");
destination->debug_print(1);
AML_DEBUG_PRINTLN("");
AML_DEBUG_PRINTLN("}");
#endif
if (auto stored = destination->store(source))
return ParseResult(stored);
return ParseResult::Failure;
}
};
}

View File

@@ -0,0 +1,69 @@
#pragma once
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/Node.h>
#include <kernel/ACPI/AML/ParseContext.h>
namespace Kernel::ACPI::AML
{
struct String final : public AML::Node
{
BAN::Vector<uint8_t> string;
String() : Node(Node::Type::String) {}
String(BAN::StringView string)
: Node(Node::Type::String)
{
MUST(this->string.resize(string.size()));
for (size_t i = 0; i < string.size(); i++)
this->string[i] = string[i];
}
BAN::Optional<bool> logical_compare(BAN::RefPtr<AML::Node> node, AML::Byte binaryop);
BAN::RefPtr<AML::Node> convert(uint8_t mask) override;
BAN::RefPtr<AML::Node> copy() override
{
auto new_string = MUST(BAN::RefPtr<AML::String>::create());
MUST(new_string->string.resize(this->string.size()));
for (size_t i = 0; i < this->string.size(); i++)
new_string->string[i] = this->string[i];
return new_string;
}
BAN::RefPtr<AML::Node> store(BAN::RefPtr<AML::Node> node) override
{
ASSERT(node);
auto conv_node = node->convert(AML::Node::ConvString);
if (!conv_node)
{
AML_ERROR("Could not convert to String");
return {};
}
auto* string_node = static_cast<AML::String*>(conv_node.ptr());
MUST(string.resize(string_node->string.size()));
for (size_t i = 0; i < string.size(); i++)
string[i] = string_node->string[i];
return string_node->copy();
}
BAN::StringView string_view() const
{
return BAN::StringView(reinterpret_cast<const char*>(string.data()), string.size());
}
static ParseResult parse(ParseContext& context);
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("String \"{}\"", string_view());
}
private:
BAN::RefPtr<AML::Buffer> as_buffer();
};
}

View File

@@ -0,0 +1,64 @@
#pragma once
#include <BAN/Endianness.h>
#include <kernel/ACPI/AML/Bytes.h>
#include <kernel/ACPI/AML/ParseContext.h>
#include <kernel/ACPI/AML/Pkg.h>
#include <kernel/ACPI/AML/Scope.h>
namespace Kernel::ACPI::AML
{
struct ThermalZone final : public AML::Scope
{
ThermalZone(NameSeg name)
: Scope(Node::Type::ThermalZone, name)
{}
BAN::RefPtr<AML::Node> convert(uint8_t) override { return {}; }
static ParseResult parse(ParseContext& context)
{
ASSERT(context.aml_data.size() >= 2);
ASSERT(static_cast<AML::Byte>(context.aml_data[0]) == AML::Byte::ExtOpPrefix);
ASSERT(static_cast<AML::ExtOp>(context.aml_data[1]) == AML::ExtOp::ThermalZoneOp);
context.aml_data = context.aml_data.slice(2);
auto opt_thermal_zone_pkg = AML::parse_pkg(context.aml_data);
if (!opt_thermal_zone_pkg.has_value())
return ParseResult::Failure;
auto thermal_zone_pkg = opt_thermal_zone_pkg.value();
auto name = NameString::parse(thermal_zone_pkg);
if (!name.has_value())
return ParseResult::Failure;
auto thermal_zone = MUST(BAN::RefPtr<ThermalZone>::create(name->path.back()));
if (!Namespace::root_namespace()->add_named_object(context, name.value(), thermal_zone))
return ParseResult::Success;
#if AML_DEBUG_LEVEL >= 2
thermal_zone->debug_print(0);
AML_DEBUG_PRINTLN("");
#endif
return thermal_zone->enter_context_and_parse_term_list(context, name.value(), thermal_zone_pkg);
}
virtual void debug_print(int indent) const override
{
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("ThermalZone {} {", name);
Namespace::root_namespace()->for_each_child(scope,
[&](const auto&, const auto& child)
{
child->debug_print(indent + 1);
AML_DEBUG_PRINTLN("");
}
);
AML_DEBUG_PRINT_INDENT(indent);
AML_DEBUG_PRINT("}");
}
};
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <BAN/Formatter.h>
#include <kernel/Debug.h>
// AML_DEBUG_LEVEL:
// 0: No debug output
// 1: Dump AML after parsing
// 2: Dump AML while parsing
#define AML_DEBUG_LEVEL 0
#define AML_TODO(...) \
do { \
BAN::Formatter::print(Debug::putchar, "\e[33mTODO: "); \
BAN::Formatter::print(Debug::putchar, __VA_ARGS__); \
BAN::Formatter::println(Debug::putchar, "\e[m"); \
} while (0)
#define AML_ERROR(...) \
do { \
BAN::Formatter::print(Debug::putchar, "\e[31mERROR: "); \
BAN::Formatter::print(Debug::putchar, __VA_ARGS__); \
BAN::Formatter::println(Debug::putchar, "\e[m"); \
} while (0)
#define AML_PRINT(...) BAN::Formatter::println(Debug::putchar, __VA_ARGS__)
#define AML_DEBUG_PRINT_INDENT(indent) \
do { \
for (int i = 0; i < (indent) * 2; i++) \
AML_DEBUG_PUTC(' '); \
} while (0)
#define AML_DEBUG_PUTC(c) Debug::putchar(c)
#define AML_DEBUG_PRINT(...) BAN::Formatter::print(Debug::putchar, __VA_ARGS__)
#define AML_DEBUG_PRINTLN(...) BAN::Formatter::println(Debug::putchar, __VA_ARGS__)

Some files were not shown because too many files have changed in this diff Show More