Compare commits
40 Commits
7badcf80cf
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8dc199738 | ||
| 74127c0f45 | |||
| 28499b890c | |||
| 2f45349658 | |||
| fde085e04b | |||
| 77ca525552 | |||
| 3b83daef17 | |||
| f37d9dbdb1 | |||
| b7cedad891 | |||
| cdf0de34fb | |||
| 03fccdffe1 | |||
| 1602b195c5 | |||
| 1486ad7aa5 | |||
| ab8bcbec3e | |||
| 0e00b72df6 | |||
| a63818ec33 | |||
| cf2e8ffaff | |||
| b5647ff258 | |||
| bef53c726b | |||
| 6b43cadf3a | |||
| b74812d669 | |||
| eea0154f18 | |||
| ea4c34fc0b | |||
| 558ed8fd44 | |||
| 8665195350 | |||
| 72a24a0d38 | |||
| 0ee50032f3 | |||
| 40f3546aca | |||
| a8e496310b | |||
| fe613e4274 | |||
| 3264dcee44 | |||
| 71649ffe09 | |||
| 5b7b2d7ac3 | |||
| 8e543195b1 | |||
| a24ec0da2b | |||
| e6284c3cf3 | |||
|
|
f527aca9d0 | ||
|
|
5fce3b64cc | ||
| c04ad65f7f | |||
| af17b29414 |
@@ -1,49 +1,154 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/HashSet.h>
|
||||||
#include <BAN/LinkedList.h>
|
|
||||||
#include <BAN/Vector.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH = BAN::hash<Key>>
|
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>>
|
||||||
class HashMap
|
class HashMap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Entry
|
struct Entry
|
||||||
{
|
{
|
||||||
template<typename... Args>
|
const Key key;
|
||||||
Entry(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
|
||||||
: key(key)
|
|
||||||
, value(forward<Args>(args)...)
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
Entry(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
|
||||||
: key(BAN::move(key))
|
|
||||||
, value(forward<Args>(args)...)
|
|
||||||
{}
|
|
||||||
|
|
||||||
Key key;
|
|
||||||
T value;
|
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)...)
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using key_type = Key;
|
using key_type = Key;
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
using iterator = IteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
using iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::iterator, HashMap, Entry>;
|
||||||
using const_iterator = ConstIteratorDouble<Entry, Vector, LinkedList, HashMap>;
|
using const_iterator = HashMapIterator<typename HashSet<Entry, EntryHash, EntryComp>::const_iterator, HashMap, const Entry>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashMap() = default;
|
HashMap() = default;
|
||||||
HashMap(const HashMap<Key, T, HASH>&);
|
~HashMap() { clear(); }
|
||||||
HashMap(HashMap<Key, T, HASH>&&);
|
|
||||||
~HashMap();
|
|
||||||
|
|
||||||
HashMap<Key, T, HASH>& operator=(const HashMap<Key, T, HASH>&);
|
HashMap(const HashMap& other) { *this = other; }
|
||||||
HashMap<Key, T, HASH>& operator=(HashMap<Key, T, HASH>&&);
|
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, const T& value) { return emplace(key, value); }
|
||||||
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
|
ErrorOr<iterator> insert(const Key& key, T&& value) { return emplace(key, move(value)); }
|
||||||
@@ -57,263 +162,100 @@ namespace BAN
|
|||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
ErrorOr<iterator> emplace(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
{ return emplace(Key(key), forward<Args>(args)...); }
|
{ return emplace(Key(key), BAN::forward<Args>(args)...); }
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<iterator> emplace(Key&&, Args&&...) requires is_constructible_v<T, 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);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
ErrorOr<iterator> emplace_or_assign(const Key& key, Args&&... args) requires is_constructible_v<T, Args...>
|
||||||
{ return emplace_or_assign(Key(key), forward<Args>(args)...); }
|
{ return emplace_or_assign(Key(key), BAN::forward<Args>(args)...); }
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
ErrorOr<iterator> emplace_or_assign(Key&&, Args&&...) requires is_constructible_v<T, 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()); }
|
auto it = TRY(m_hash_set.insert(Entry { BAN::move(key), T(BAN::forward<Args>(args)...) }));
|
||||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
return iterator(it);
|
||||||
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()); }
|
|
||||||
|
|
||||||
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&);
|
iterator remove(iterator it)
|
||||||
void remove(iterator it);
|
{
|
||||||
void clear();
|
return iterator(m_hash_set.remove(it.m_iterator));
|
||||||
|
}
|
||||||
|
|
||||||
T& operator[](const Key&);
|
template<detail::HashMapFindable<Key, HASH, COMP> U>
|
||||||
const T& operator[](const Key&) const;
|
iterator find(const U& key)
|
||||||
|
{
|
||||||
|
return iterator(m_hash_set.find(key));
|
||||||
|
}
|
||||||
|
|
||||||
iterator find(const Key& key);
|
template<detail::HashMapFindable<Key, HASH, COMP> U>
|
||||||
const_iterator find(const Key& key) const;
|
const_iterator find(const U& key) const
|
||||||
bool contains(const Key&) const;
|
{
|
||||||
|
return const_iterator(m_hash_set.find(key));
|
||||||
|
}
|
||||||
|
|
||||||
bool empty() const;
|
void clear()
|
||||||
size_type size() const;
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
HashSet<Entry, EntryHash, EntryComp> m_hash_set;
|
||||||
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>
|
|
||||||
template<typename... Args>
|
|
||||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
|
||||||
{
|
|
||||||
ASSERT(!contains(key));
|
|
||||||
TRY(rebucket(m_size + 1));
|
|
||||||
|
|
||||||
auto bucket_it = get_bucket_iterator(key);
|
|
||||||
TRY(bucket_it->emplace_back(move(key), forward<Args>(args)...));
|
|
||||||
m_size++;
|
|
||||||
|
|
||||||
return iterator(m_buckets.end(), bucket_it, prev(bucket_it->end(), 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Key, typename T, typename HASH>
|
|
||||||
template<typename... Args>
|
|
||||||
ErrorOr<typename HashMap<Key, T, HASH>::iterator> HashMap<Key, T, HASH>::emplace_or_assign(Key&& key, Args&&... args) requires is_constructible_v<T, Args...>
|
|
||||||
{
|
|
||||||
if (empty())
|
|
||||||
return emplace(move(key), forward<Args>(args)...);
|
|
||||||
|
|
||||||
auto bucket_it = get_bucket_iterator(key);
|
|
||||||
for (auto entry_it = bucket_it->begin(); entry_it != bucket_it->end(); entry_it++)
|
|
||||||
{
|
|
||||||
if (entry_it->key != key)
|
|
||||||
continue;
|
|
||||||
entry_it->value = T(forward<Args>(args)...);
|
|
||||||
return iterator(m_buckets.end(), bucket_it, entry_it);
|
|
||||||
}
|
|
||||||
|
|
||||||
return emplace(move(key), forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
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_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,198 +2,358 @@
|
|||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
#include <BAN/Iterators.h>
|
|
||||||
#include <BAN/LinkedList.h>
|
|
||||||
#include <BAN/Math.h>
|
#include <BAN/Math.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/New.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename T, typename HASH = hash<T>>
|
template<typename HashSet, typename Bucket, typename T>
|
||||||
class HashSet
|
class HashSetIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = T;
|
HashSetIterator() = default;
|
||||||
using size_type = size_t;
|
|
||||||
using iterator = IteratorDouble<T, Vector, LinkedList, HashSet>;
|
const T& operator*() const
|
||||||
using const_iterator = ConstIteratorDouble<T, Vector, LinkedList, HashSet>;
|
{
|
||||||
|
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>>
|
||||||
|
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>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
HashSet() = default;
|
HashSet() = default;
|
||||||
HashSet(const HashSet&);
|
~HashSet() { clear(); }
|
||||||
HashSet(HashSet&&);
|
|
||||||
|
|
||||||
HashSet& operator=(const HashSet&);
|
HashSet(const HashSet& other) { *this = other; }
|
||||||
HashSet& operator=(HashSet&&);
|
HashSet& operator=(const HashSet& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
|
||||||
ErrorOr<void> insert(const T&);
|
MUST(reserve(other.size()));
|
||||||
ErrorOr<void> insert(T&&);
|
for (auto& bucket : other)
|
||||||
void remove(const T&);
|
MUST(insert(bucket));
|
||||||
void clear();
|
|
||||||
|
|
||||||
ErrorOr<void> reserve(size_type);
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
iterator begin() { return iterator(m_buckets.end(), m_buckets.begin()); }
|
HashSet(HashSet&& other) { *this = BAN::move(other); }
|
||||||
iterator end() { return iterator(m_buckets.end(), m_buckets.end()); }
|
HashSet& operator=(HashSet&& other)
|
||||||
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()); }
|
clear();
|
||||||
|
|
||||||
bool contains(const T&) const;
|
m_buckets = other.m_buckets;
|
||||||
|
m_capacity = other.m_capacity;
|
||||||
|
m_size = other.m_size;
|
||||||
|
m_removed = other.m_removed;
|
||||||
|
|
||||||
size_type size() const;
|
other.m_buckets = nullptr;
|
||||||
bool empty() const;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> rebucket(size_type);
|
ErrorOr<void> rehash(size_type new_capacity)
|
||||||
LinkedList<T>& get_bucket(const T&);
|
{
|
||||||
const LinkedList<T>& get_bucket(const T&) const;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<LinkedList<T>> m_buckets;
|
Bucket* m_buckets { nullptr };
|
||||||
size_type m_size = 0;
|
size_type m_capacity { 0 };
|
||||||
|
size_type m_size { 0 };
|
||||||
|
size_type m_removed { 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,22 @@ namespace BAN::Math
|
|||||||
return (x & (x - 1)) == 0;
|
return (x & (x - 1)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<integral T> requires(sizeof(T) <= 8)
|
||||||
|
inline constexpr T round_up_to_power_of_two(T x)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
template<integral T>
|
template<integral T>
|
||||||
__attribute__((always_inline))
|
__attribute__((always_inline))
|
||||||
inline constexpr bool will_multiplication_overflow(T a, T b)
|
inline constexpr bool will_multiplication_overflow(T a, T b)
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#include <BAN/Atomic.h>
|
#include <BAN/Atomic.h>
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
#include <BAN/Move.h>
|
#include <BAN/Move.h>
|
||||||
#include <BAN/NoCopyMove.h>
|
#include <BAN/NoCopyMove.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
@@ -129,14 +129,9 @@ namespace BAN
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
T* ptr() { ASSERT(!empty()); return m_pointer; }
|
T* ptr() const { return m_pointer; }
|
||||||
const T* ptr() const { ASSERT(!empty()); return m_pointer; }
|
T& operator*() const { ASSERT(!empty()); return *ptr(); }
|
||||||
|
T* operator->() const { ASSERT(!empty()); return ptr(); }
|
||||||
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; }
|
||||||
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
|
bool operator!=(RefPtr other) const { return m_pointer != other.m_pointer; }
|
||||||
@@ -158,4 +153,13 @@ namespace BAN
|
|||||||
friend class RefPtr;
|
friend class RefPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct hash<RefPtr<T>>
|
||||||
|
{
|
||||||
|
constexpr hash_t operator()(const RefPtr<T>& ptr) const
|
||||||
|
{
|
||||||
|
return hash<T*>()(ptr.ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Hash.h>
|
||||||
#include <BAN/NoCopyMove.h>
|
#include <BAN/NoCopyMove.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
@@ -53,32 +54,12 @@ namespace BAN
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
T& operator*()
|
T* ptr() const { return m_pointer; }
|
||||||
{
|
T& operator*() const { ASSERT(!empty()); return *ptr(); }
|
||||||
ASSERT(m_pointer);
|
T* operator->() const { ASSERT(!empty()); return ptr(); }
|
||||||
return *m_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& operator*() const
|
bool empty() const { return m_pointer == nullptr; }
|
||||||
{
|
explicit operator bool() const { return m_pointer; }
|
||||||
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()
|
void clear()
|
||||||
{
|
{
|
||||||
@@ -87,8 +68,6 @@ namespace BAN
|
|||||||
m_pointer = nullptr;
|
m_pointer = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const { return m_pointer != nullptr; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* m_pointer = nullptr;
|
T* m_pointer = nullptr;
|
||||||
|
|
||||||
@@ -96,4 +75,13 @@ namespace BAN
|
|||||||
friend class UniqPtr;
|
friend class UniqPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct hash<UniqPtr<T>>
|
||||||
|
{
|
||||||
|
constexpr hash_t operator()(const UniqPtr<T>& ptr) const
|
||||||
|
{
|
||||||
|
return hash<T*>()(ptr.ptr());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ extern uint8_t g_kernel_writable_end[];
|
|||||||
extern uint8_t g_userspace_start[];
|
extern uint8_t g_userspace_start[];
|
||||||
extern uint8_t g_userspace_end[];
|
extern uint8_t g_userspace_end[];
|
||||||
|
|
||||||
|
extern uint64_t g_boot_fast_page_pt[];
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ namespace Kernel
|
|||||||
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
|
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
|
||||||
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
|
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
|
||||||
|
|
||||||
static bool s_is_post_heap_done = false;
|
static bool s_is_initialized = false;
|
||||||
|
|
||||||
static PageTable* s_kernel = nullptr;
|
static PageTable* s_kernel = nullptr;
|
||||||
static bool s_has_nxe = false;
|
static bool s_has_nxe = false;
|
||||||
@@ -33,6 +35,28 @@ namespace Kernel
|
|||||||
|
|
||||||
static paddr_t s_global_pdpte = 0;
|
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)
|
static inline PageTable::flags_t parse_flags(uint64_t entry)
|
||||||
{
|
{
|
||||||
using Flags = PageTable::Flags;
|
using Flags = PageTable::Flags;
|
||||||
@@ -51,31 +75,22 @@ namespace Kernel
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::initialize_pre_heap()
|
void PageTable::initialize_fast_page()
|
||||||
|
{
|
||||||
|
s_fast_page_pt = g_boot_fast_page_pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void detect_cpu_features()
|
||||||
{
|
{
|
||||||
if (CPUID::has_nxe())
|
if (CPUID::has_nxe())
|
||||||
s_has_nxe = true;
|
s_has_nxe = true;
|
||||||
|
|
||||||
if (CPUID::has_pge())
|
if (CPUID::has_pge())
|
||||||
s_has_pge = true;
|
s_has_pge = true;
|
||||||
|
|
||||||
if (CPUID::has_pat())
|
if (CPUID::has_pat())
|
||||||
s_has_pat = true;
|
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::initialize_post_heap()
|
void PageTable::enable_cpu_features()
|
||||||
{
|
|
||||||
s_is_post_heap_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::initial_load()
|
|
||||||
{
|
{
|
||||||
if (s_has_nxe)
|
if (s_has_nxe)
|
||||||
{
|
{
|
||||||
@@ -116,8 +131,56 @@ namespace Kernel
|
|||||||
"movl %%eax, %%cr0;"
|
"movl %%eax, %%cr0;"
|
||||||
::: "rax"
|
::: "rax"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
load();
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
PageTable& PageTable::kernel()
|
PageTable& PageTable::kernel()
|
||||||
@@ -131,40 +194,12 @@ namespace Kernel
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t* allocate_zeroed_page_aligned_page()
|
void PageTable::map_kernel_memory()
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
|
// 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(
|
map_range_at(
|
||||||
V2P(g_kernel_start),
|
V2P(g_kernel_start),
|
||||||
(vaddr_t)g_kernel_start,
|
reinterpret_cast<vaddr_t>(g_kernel_start),
|
||||||
g_kernel_end - g_kernel_start,
|
g_kernel_end - g_kernel_start,
|
||||||
Flags::Present
|
Flags::Present
|
||||||
);
|
);
|
||||||
@@ -172,7 +207,7 @@ namespace Kernel
|
|||||||
// Map executable kernel memory as executable
|
// Map executable kernel memory as executable
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(g_kernel_execute_start),
|
V2P(g_kernel_execute_start),
|
||||||
(vaddr_t)g_kernel_execute_start,
|
reinterpret_cast<vaddr_t>(g_kernel_execute_start),
|
||||||
g_kernel_execute_end - g_kernel_execute_start,
|
g_kernel_execute_end - g_kernel_execute_start,
|
||||||
Flags::Execute | Flags::Present
|
Flags::Execute | Flags::Present
|
||||||
);
|
);
|
||||||
@@ -180,7 +215,7 @@ namespace Kernel
|
|||||||
// Map writable kernel memory as writable
|
// Map writable kernel memory as writable
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(g_kernel_writable_start),
|
V2P(g_kernel_writable_start),
|
||||||
(vaddr_t)g_kernel_writable_start,
|
reinterpret_cast<vaddr_t>(g_kernel_writable_start),
|
||||||
g_kernel_writable_end - g_kernel_writable_start,
|
g_kernel_writable_end - g_kernel_writable_start,
|
||||||
Flags::ReadWrite | Flags::Present
|
Flags::ReadWrite | Flags::Present
|
||||||
);
|
);
|
||||||
@@ -188,70 +223,34 @@ namespace Kernel
|
|||||||
// Map userspace memory
|
// Map userspace memory
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(g_userspace_start),
|
V2P(g_userspace_start),
|
||||||
(vaddr_t)g_userspace_start,
|
reinterpret_cast<vaddr_t>(g_userspace_start),
|
||||||
g_userspace_end - g_userspace_start,
|
g_userspace_end - g_userspace_start,
|
||||||
Flags::Execute | Flags::UserSupervisor | Flags::Present
|
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;
|
|
||||||
|
|
||||||
const uint64_t* pdpt = P2V(m_highest_paging_struct);
|
|
||||||
ASSERT(pdpt[pdpte] & Flags::Present);
|
|
||||||
|
|
||||||
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
ASSERT(!(pd[pde] & Flags::Present));
|
|
||||||
pd[pde] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
ASSERT(pt[pte] == 0);
|
|
||||||
pt[pte] = Flags::Reserved;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::map_fast_page(paddr_t paddr)
|
void PageTable::map_fast_page(paddr_t paddr)
|
||||||
{
|
{
|
||||||
ASSERT(s_kernel);
|
ASSERT(paddr && paddr % PAGE_SIZE == 0);
|
||||||
ASSERT(paddr);
|
|
||||||
ASSERT(paddr % PAGE_SIZE == 0);
|
|
||||||
|
|
||||||
|
ASSERT(s_fast_page_pt);
|
||||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||||
|
|
||||||
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
|
ASSERT(!(*s_fast_page_pt & Flags::Present));
|
||||||
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
|
s_fast_page_pt[0] = paddr | Flags::ReadWrite | Flags::Present;
|
||||||
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
|
|
||||||
|
|
||||||
uint64_t* pdpt = P2V(s_kernel->m_highest_paging_struct);
|
asm volatile("invlpg (%0)" :: "r"(fast_page()));
|
||||||
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
|
|
||||||
ASSERT(!(pt[pte] & Flags::Present));
|
|
||||||
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::unmap_fast_page()
|
void PageTable::unmap_fast_page()
|
||||||
{
|
{
|
||||||
ASSERT(s_kernel);
|
ASSERT(s_fast_page_pt);
|
||||||
|
|
||||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||||
|
|
||||||
constexpr uint64_t pdpte = (fast_page() >> 30) & 0x1FF;
|
ASSERT((*s_fast_page_pt & Flags::Present));
|
||||||
constexpr uint64_t pde = (fast_page() >> 21) & 0x1FF;
|
s_fast_page_pt[0] = 0;
|
||||||
constexpr uint64_t pte = (fast_page() >> 12) & 0x1FF;
|
|
||||||
|
|
||||||
uint64_t* pdpt = P2V(s_kernel->m_highest_paging_struct);
|
asm volatile("invlpg (%0)" :: "r"(fast_page()));
|
||||||
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
|
|
||||||
ASSERT(pt[pte] & Flags::Present);
|
|
||||||
pt[pte] = Flags::Reserved;
|
|
||||||
|
|
||||||
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
||||||
@@ -260,25 +259,23 @@ namespace Kernel
|
|||||||
PageTable* page_table = new PageTable;
|
PageTable* page_table = new PageTable;
|
||||||
if (page_table == nullptr)
|
if (page_table == nullptr)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
page_table->map_kernel_memory();
|
|
||||||
return page_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::map_kernel_memory()
|
uint64_t* pdpt = allocate_zeroed_page_aligned_page();
|
||||||
{
|
if (pdpt == nullptr)
|
||||||
ASSERT(s_kernel);
|
{
|
||||||
ASSERT(s_global_pdpte);
|
delete page_table;
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
ASSERT(m_highest_paging_struct == 0);
|
page_table->m_highest_paging_struct = V2P(pdpt);
|
||||||
m_highest_paging_struct = V2P(kmalloc(32, 32, true));
|
|
||||||
ASSERT(m_highest_paging_struct);
|
|
||||||
|
|
||||||
uint64_t* pdpt = P2V(m_highest_paging_struct);
|
|
||||||
pdpt[0] = 0;
|
pdpt[0] = 0;
|
||||||
pdpt[1] = 0;
|
pdpt[1] = 0;
|
||||||
pdpt[2] = 0;
|
pdpt[2] = 0;
|
||||||
pdpt[3] = s_global_pdpte | Flags::Present;
|
pdpt[3] = s_global_pdpte | Flags::Present;
|
||||||
static_assert(KERNEL_OFFSET == 0xC0000000);
|
static_assert(KERNEL_OFFSET == 0xC0000000);
|
||||||
|
|
||||||
|
return page_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
PageTable::~PageTable()
|
PageTable::~PageTable()
|
||||||
@@ -318,7 +315,7 @@ namespace Kernel
|
|||||||
const bool is_userspace = (vaddr < KERNEL_OFFSET);
|
const bool is_userspace = (vaddr < KERNEL_OFFSET);
|
||||||
if (is_userspace && this != &PageTable::current())
|
if (is_userspace && this != &PageTable::current())
|
||||||
;
|
;
|
||||||
else if (pages <= 32 || !s_is_post_heap_done)
|
else if (pages <= 32 || !s_is_initialized)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < pages; i++, vaddr += PAGE_SIZE)
|
for (size_t i = 0; i < pages; i++, vaddr += PAGE_SIZE)
|
||||||
asm volatile("invlpg (%0)" :: "r"(vaddr));
|
asm volatile("invlpg (%0)" :: "r"(vaddr));
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ signal_trampoline:
|
|||||||
pushl %eax
|
pushl %eax
|
||||||
pushl %ebp
|
pushl %ebp
|
||||||
|
|
||||||
movl 80(%esp), %eax
|
movl 84(%esp), %eax
|
||||||
pushl %eax; addl $4, (%esp)
|
pushl %eax; addl $4, (%esp)
|
||||||
pushl (%eax)
|
pushl (%eax)
|
||||||
|
|
||||||
|
|||||||
@@ -98,8 +98,7 @@ bananboot_end:
|
|||||||
boot_pdpt:
|
boot_pdpt:
|
||||||
.long V2P(boot_pd) + (PG_PRESENT)
|
.long V2P(boot_pd) + (PG_PRESENT)
|
||||||
.long 0
|
.long 0
|
||||||
.quad 0
|
.skip 2 * 8
|
||||||
.quad 0
|
|
||||||
.long V2P(boot_pd) + (PG_PRESENT)
|
.long V2P(boot_pd) + (PG_PRESENT)
|
||||||
.long 0
|
.long 0
|
||||||
.align 4096
|
.align 4096
|
||||||
@@ -112,13 +111,16 @@ boot_pd:
|
|||||||
.endr
|
.endr
|
||||||
boot_pts:
|
boot_pts:
|
||||||
.set i, 0
|
.set i, 0
|
||||||
.rept 512
|
.rept 511
|
||||||
.rept 512
|
.rept 512
|
||||||
.long i + (PG_READ_WRITE | PG_PRESENT)
|
.long i + (PG_READ_WRITE | PG_PRESENT)
|
||||||
.long 0
|
.long 0
|
||||||
.set i, i + 0x1000
|
.set i, i + 0x1000
|
||||||
.endr
|
.endr
|
||||||
.endr
|
.endr
|
||||||
|
.global g_boot_fast_page_pt
|
||||||
|
g_boot_fast_page_pt:
|
||||||
|
.skip 512 * 8
|
||||||
|
|
||||||
boot_gdt:
|
boot_gdt:
|
||||||
.quad 0x0000000000000000 # null descriptor
|
.quad 0x0000000000000000 # null descriptor
|
||||||
@@ -274,7 +276,7 @@ system_halt:
|
|||||||
jmp 1b
|
jmp 1b
|
||||||
|
|
||||||
|
|
||||||
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
|
#define AP_REL(vaddr) ((vaddr) - ap_trampoline + 0xF000)
|
||||||
|
|
||||||
.section .ap_init, "ax"
|
.section .ap_init, "ax"
|
||||||
|
|
||||||
@@ -284,21 +286,27 @@ ap_trampoline:
|
|||||||
jmp 1f
|
jmp 1f
|
||||||
|
|
||||||
.align 8
|
.align 8
|
||||||
ap_stack_ptr:
|
ap_stack_paddr:
|
||||||
|
.skip 4
|
||||||
|
ap_stack_vaddr:
|
||||||
|
.skip 4
|
||||||
|
ap_prepare_paging:
|
||||||
|
.skip 4
|
||||||
|
ap_page_table:
|
||||||
|
.skip 4
|
||||||
|
ap_ready:
|
||||||
.skip 4
|
.skip 4
|
||||||
ap_stack_loaded:
|
|
||||||
.skip 1
|
|
||||||
|
|
||||||
1: cli; cld
|
1: cli; cld
|
||||||
ljmpl $0x00, $AP_V2P(ap_cs_clear)
|
ljmpl $0x00, $AP_REL(ap_cs_clear)
|
||||||
|
|
||||||
ap_cs_clear:
|
ap_cs_clear:
|
||||||
# load ap gdt and enter protected mode
|
# load ap gdt and enter protected mode
|
||||||
lgdt AP_V2P(ap_gdtr)
|
lgdt AP_REL(ap_gdtr)
|
||||||
movl %cr0, %eax
|
movl %cr0, %eax
|
||||||
orb $1, %al
|
orb $1, %al
|
||||||
movl %eax, %cr0
|
movl %eax, %cr0
|
||||||
ljmpl $0x08, $AP_V2P(ap_protected_mode)
|
ljmpl $0x08, $AP_REL(ap_protected_mode)
|
||||||
|
|
||||||
.code32
|
.code32
|
||||||
ap_protected_mode:
|
ap_protected_mode:
|
||||||
@@ -307,8 +315,7 @@ ap_protected_mode:
|
|||||||
movw %ax, %ss
|
movw %ax, %ss
|
||||||
movw %ax, %es
|
movw %ax, %es
|
||||||
|
|
||||||
movl AP_V2P(ap_stack_ptr), %esp
|
movl AP_REL(ap_stack_paddr), %esp
|
||||||
movb $1, AP_V2P(ap_stack_loaded)
|
|
||||||
|
|
||||||
leal V2P(enable_sse), %ecx; call *%ecx
|
leal V2P(enable_sse), %ecx; call *%ecx
|
||||||
leal V2P(enable_tsc), %ecx; call *%ecx
|
leal V2P(enable_tsc), %ecx; call *%ecx
|
||||||
@@ -316,24 +323,28 @@ ap_protected_mode:
|
|||||||
|
|
||||||
# load boot gdt and enter long mode
|
# load boot gdt and enter long mode
|
||||||
lgdt V2P(boot_gdtr)
|
lgdt V2P(boot_gdtr)
|
||||||
ljmpl $0x08, $AP_V2P(ap_flush_gdt)
|
ljmpl $0x08, $AP_REL(ap_flush_gdt)
|
||||||
|
|
||||||
ap_flush_gdt:
|
ap_flush_gdt:
|
||||||
# move stack pointer to higher half
|
movl $ap_higher_half, %ecx
|
||||||
movl %esp, %esp
|
|
||||||
addl $KERNEL_OFFSET, %esp
|
|
||||||
|
|
||||||
# jump to higher half
|
|
||||||
leal ap_higher_half, %ecx
|
|
||||||
jmp *%ecx
|
jmp *%ecx
|
||||||
|
|
||||||
ap_higher_half:
|
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
|
# clear rbp for stacktrace
|
||||||
xorl %ebp, %ebp
|
xorl %ebp, %ebp
|
||||||
|
|
||||||
1: pause
|
1: pause
|
||||||
cmpb $0, g_ap_startup_done
|
cmpb $0, g_ap_startup_done
|
||||||
jz 1b
|
je 1b
|
||||||
|
|
||||||
lock incb g_ap_running_count
|
lock incb g_ap_running_count
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ SECTIONS
|
|||||||
*(.bananboot)
|
*(.bananboot)
|
||||||
*(.text.*)
|
*(.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)
|
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
|
||||||
{
|
{
|
||||||
g_userspace_start = .;
|
g_userspace_start = .;
|
||||||
*(.userspace)
|
*(.userspace)
|
||||||
g_userspace_end = .;
|
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)
|
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#include <kernel/CPUID.h>
|
#include <kernel/CPUID.h>
|
||||||
#include <kernel/Lock/SpinLock.h>
|
#include <kernel/Lock/SpinLock.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Memory/kmalloc.h>
|
|
||||||
#include <kernel/Memory/PageTable.h>
|
#include <kernel/Memory/PageTable.h>
|
||||||
|
|
||||||
extern uint8_t g_kernel_start[];
|
extern uint8_t g_kernel_start[];
|
||||||
@@ -17,13 +16,15 @@ extern uint8_t g_kernel_writable_end[];
|
|||||||
extern uint8_t g_userspace_start[];
|
extern uint8_t g_userspace_start[];
|
||||||
extern uint8_t g_userspace_end[];
|
extern uint8_t g_userspace_end[];
|
||||||
|
|
||||||
|
extern uint64_t g_boot_fast_page_pt[];
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
SpinLock PageTable::s_fast_page_lock;
|
SpinLock PageTable::s_fast_page_lock;
|
||||||
|
|
||||||
static constexpr vaddr_t s_hhdm_offset = 0xFFFF800000000000;
|
static constexpr vaddr_t s_hhdm_offset = 0xFFFF800000000000;
|
||||||
static bool s_is_post_heap_done = false;
|
static bool s_is_initialized = false;
|
||||||
|
|
||||||
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
|
constexpr uint64_t s_page_flag_mask = 0x8000000000000FFF;
|
||||||
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
|
constexpr uint64_t s_page_addr_mask = ~s_page_flag_mask;
|
||||||
@@ -35,6 +36,8 @@ namespace Kernel
|
|||||||
|
|
||||||
static paddr_t s_global_pml4_entries[512] { 0 };
|
static paddr_t s_global_pml4_entries[512] { 0 };
|
||||||
|
|
||||||
|
static uint64_t* s_fast_page_pt { nullptr };
|
||||||
|
|
||||||
static constexpr inline bool is_canonical(uintptr_t addr)
|
static constexpr inline bool is_canonical(uintptr_t addr)
|
||||||
{
|
{
|
||||||
constexpr uintptr_t mask = 0xFFFF800000000000;
|
constexpr uintptr_t mask = 0xFFFF800000000000;
|
||||||
@@ -54,68 +57,27 @@ namespace Kernel
|
|||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FuncsKmalloc
|
static paddr_t allocate_zeroed_page_aligned_page()
|
||||||
{
|
{
|
||||||
static paddr_t allocate_zeroed_page_aligned_page()
|
const paddr_t paddr = Heap::get().take_free_page();
|
||||||
{
|
ASSERT(paddr);
|
||||||
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
|
memset(reinterpret_cast<void*>(paddr + s_hhdm_offset), 0, PAGE_SIZE);
|
||||||
ASSERT(page);
|
return paddr;
|
||||||
memset(page, 0, PAGE_SIZE);
|
}
|
||||||
return kmalloc_paddr_of(reinterpret_cast<vaddr_t>(page)).value();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unallocate_page(paddr_t paddr)
|
static void unallocate_page(paddr_t paddr)
|
||||||
{
|
|
||||||
kfree(reinterpret_cast<void*>(kmalloc_vaddr_of(paddr).value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static paddr_t V2P(vaddr_t vaddr)
|
|
||||||
{
|
|
||||||
return vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t* P2V(paddr_t paddr)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<uint64_t*>(paddr - g_boot_info.kernel_paddr + KERNEL_OFFSET);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FuncsHHDM
|
|
||||||
{
|
{
|
||||||
static paddr_t allocate_zeroed_page_aligned_page()
|
Heap::get().release_page(paddr);
|
||||||
{
|
}
|
||||||
const paddr_t paddr = Heap::get().take_free_page();
|
|
||||||
ASSERT(paddr);
|
|
||||||
memset(reinterpret_cast<void*>(paddr + s_hhdm_offset), 0, PAGE_SIZE);
|
|
||||||
return paddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unallocate_page(paddr_t paddr)
|
static uint64_t* P2V(paddr_t paddr)
|
||||||
{
|
{
|
||||||
Heap::get().release_page(paddr);
|
ASSERT(paddr != 0);
|
||||||
}
|
ASSERT(!BAN::Math::will_addition_overflow(paddr, s_hhdm_offset));
|
||||||
|
return reinterpret_cast<uint64_t*>(paddr + s_hhdm_offset);
|
||||||
|
}
|
||||||
|
|
||||||
static paddr_t V2P(vaddr_t vaddr)
|
static PageTable::flags_t parse_flags(uint64_t entry)
|
||||||
{
|
|
||||||
ASSERT(vaddr >= s_hhdm_offset);
|
|
||||||
ASSERT(vaddr < KERNEL_OFFSET);
|
|
||||||
return vaddr - s_hhdm_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t* P2V(paddr_t paddr)
|
|
||||||
{
|
|
||||||
ASSERT(paddr != 0);
|
|
||||||
ASSERT(!BAN::Math::will_addition_overflow(paddr, s_hhdm_offset));
|
|
||||||
return reinterpret_cast<uint64_t*>(paddr + s_hhdm_offset);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static paddr_t (*allocate_zeroed_page_aligned_page)() = &FuncsKmalloc::allocate_zeroed_page_aligned_page;
|
|
||||||
static void (*unallocate_page)(paddr_t) = &FuncsKmalloc::unallocate_page;
|
|
||||||
static paddr_t (*V2P)(vaddr_t) = &FuncsKmalloc::V2P;
|
|
||||||
static uint64_t* (*P2V)(paddr_t) = &FuncsKmalloc::P2V;
|
|
||||||
|
|
||||||
static inline PageTable::flags_t parse_flags(uint64_t entry)
|
|
||||||
{
|
{
|
||||||
using Flags = PageTable::Flags;
|
using Flags = PageTable::Flags;
|
||||||
|
|
||||||
@@ -137,7 +99,7 @@ namespace Kernel
|
|||||||
// 0: 4 KiB
|
// 0: 4 KiB
|
||||||
// 1: 2 MiB
|
// 1: 2 MiB
|
||||||
// 2: 1 GiB
|
// 2: 1 GiB
|
||||||
static void init_map_hhdm_page(paddr_t pml4, paddr_t paddr, uint8_t page_size)
|
static void map_hhdm_page(paddr_t pml4, paddr_t paddr, uint8_t page_size)
|
||||||
{
|
{
|
||||||
ASSERT(0 <= page_size && page_size <= 2);
|
ASSERT(0 <= page_size && page_size <= 2);
|
||||||
|
|
||||||
@@ -184,7 +146,7 @@ namespace Kernel
|
|||||||
const uint64_t noexec_flag = s_has_nxe ? (static_cast<uint64_t>(1) << 63) : 0;
|
const uint64_t noexec_flag = s_has_nxe ? (static_cast<uint64_t>(1) << 63) : 0;
|
||||||
|
|
||||||
const paddr_t pdpt = get_or_allocate_entry(pml4, pml4e, noexec_flag);
|
const paddr_t pdpt = get_or_allocate_entry(pml4, pml4e, noexec_flag);
|
||||||
s_global_pml4_entries[pml4e] = pdpt | hhdm_flags;
|
s_global_pml4_entries[pml4e] = pdpt | hhdm_flags | noexec_flag;
|
||||||
|
|
||||||
paddr_t lowest_paddr = pdpt;
|
paddr_t lowest_paddr = pdpt;
|
||||||
uint16_t lowest_entry = pdpte;
|
uint16_t lowest_entry = pdpte;
|
||||||
@@ -207,23 +169,11 @@ namespace Kernel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_map_hhdm(paddr_t pml4)
|
static void initialize_hhdm(paddr_t pml4)
|
||||||
{
|
{
|
||||||
for (const auto& entry : g_boot_info.memory_map_entries)
|
for (const auto& entry : g_boot_info.memory_map_entries)
|
||||||
{
|
{
|
||||||
bool should_map = false;
|
if (entry.type != MemoryMapEntry::Type::Available)
|
||||||
switch (entry.type)
|
|
||||||
{
|
|
||||||
case MemoryMapEntry::Type::Available:
|
|
||||||
should_map = true;
|
|
||||||
break;
|
|
||||||
case MemoryMapEntry::Type::ACPIReclaim:
|
|
||||||
case MemoryMapEntry::Type::ACPINVS:
|
|
||||||
case MemoryMapEntry::Type::Reserved:
|
|
||||||
should_map = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!should_map)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
constexpr size_t one_gib = 1024 * 1024 * 1024;
|
constexpr size_t one_gib = 1024 * 1024 * 1024;
|
||||||
@@ -235,156 +185,39 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
if (s_has_gib && paddr % one_gib == 0 && paddr + one_gib <= entry_end)
|
if (s_has_gib && paddr % one_gib == 0 && paddr + one_gib <= entry_end)
|
||||||
{
|
{
|
||||||
init_map_hhdm_page(pml4, paddr, 2);
|
map_hhdm_page(pml4, paddr, 2);
|
||||||
paddr += one_gib;
|
paddr += one_gib;
|
||||||
}
|
}
|
||||||
else if (paddr % two_mib == 0 && paddr + two_mib <= entry_end)
|
else if (paddr % two_mib == 0 && paddr + two_mib <= entry_end)
|
||||||
{
|
{
|
||||||
init_map_hhdm_page(pml4, paddr, 1);
|
map_hhdm_page(pml4, paddr, 1);
|
||||||
paddr += two_mib;
|
paddr += two_mib;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
init_map_hhdm_page(pml4, paddr, 0);
|
map_hhdm_page(pml4, paddr, 0);
|
||||||
paddr += PAGE_SIZE;
|
paddr += PAGE_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static paddr_t copy_page_from_kmalloc_to_heap(paddr_t kmalloc_paddr)
|
void PageTable::initialize_fast_page()
|
||||||
{
|
{
|
||||||
const paddr_t heap_paddr = Heap::get().take_free_page();
|
s_fast_page_pt = g_boot_fast_page_pt;
|
||||||
ASSERT(heap_paddr);
|
|
||||||
|
|
||||||
const vaddr_t kmalloc_vaddr = kmalloc_vaddr_of(kmalloc_paddr).value();
|
|
||||||
|
|
||||||
PageTable::with_fast_page(heap_paddr, [kmalloc_vaddr] {
|
|
||||||
memcpy(PageTable::fast_page_as_ptr(), reinterpret_cast<void*>(kmalloc_vaddr), PAGE_SIZE);
|
|
||||||
});
|
|
||||||
|
|
||||||
return heap_paddr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void copy_paging_structure_to_heap(uint64_t* old_table, uint64_t* new_table, int depth)
|
static void detect_cpu_features()
|
||||||
{
|
|
||||||
if (depth == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
constexpr uint64_t page_flag_mask = 0x8000000000000FFF;
|
|
||||||
constexpr uint64_t page_addr_mask = ~page_flag_mask;
|
|
||||||
|
|
||||||
for (uint16_t index = 0; index < 512; index++)
|
|
||||||
{
|
|
||||||
const uint64_t old_entry = old_table[index];
|
|
||||||
if (old_entry == 0)
|
|
||||||
{
|
|
||||||
new_table[index] = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const paddr_t old_paddr = old_entry & page_addr_mask;
|
|
||||||
const paddr_t new_paddr = copy_page_from_kmalloc_to_heap(old_paddr);
|
|
||||||
new_table[index] = new_paddr | (old_entry & page_flag_mask);
|
|
||||||
|
|
||||||
uint64_t* next_old_table = reinterpret_cast<uint64_t*>(old_paddr + s_hhdm_offset);
|
|
||||||
uint64_t* next_new_table = reinterpret_cast<uint64_t*>(new_paddr + s_hhdm_offset);
|
|
||||||
copy_paging_structure_to_heap(next_old_table, next_new_table, depth - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_kmalloc_paging_structure(uint64_t* table, int depth)
|
|
||||||
{
|
|
||||||
if (depth == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
constexpr uint64_t page_flag_mask = 0x8000000000000FFF;
|
|
||||||
constexpr uint64_t page_addr_mask = ~page_flag_mask;
|
|
||||||
|
|
||||||
for (uint16_t index = 0; index < 512; index++)
|
|
||||||
{
|
|
||||||
const uint64_t entry = table[index];
|
|
||||||
if (entry == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const paddr_t paddr = entry & page_addr_mask;
|
|
||||||
|
|
||||||
uint64_t* next_table = reinterpret_cast<uint64_t*>(paddr + s_hhdm_offset);
|
|
||||||
free_kmalloc_paging_structure(next_table, depth - 1);
|
|
||||||
|
|
||||||
kfree(reinterpret_cast<void*>(kmalloc_vaddr_of(paddr).value()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::initialize_pre_heap()
|
|
||||||
{
|
{
|
||||||
if (CPUID::has_nxe())
|
if (CPUID::has_nxe())
|
||||||
s_has_nxe = true;
|
s_has_nxe = true;
|
||||||
|
|
||||||
if (CPUID::has_pge())
|
if (CPUID::has_pge())
|
||||||
s_has_pge = true;
|
s_has_pge = true;
|
||||||
|
|
||||||
if (CPUID::has_1gib_pages())
|
if (CPUID::has_1gib_pages())
|
||||||
s_has_gib = true;
|
s_has_gib = true;
|
||||||
|
|
||||||
ASSERT(s_kernel == nullptr);
|
|
||||||
s_kernel = new PageTable();
|
|
||||||
ASSERT(s_kernel);
|
|
||||||
|
|
||||||
s_kernel->m_highest_paging_struct = allocate_zeroed_page_aligned_page();
|
|
||||||
s_kernel->prepare_fast_page();
|
|
||||||
s_kernel->initialize_kernel();
|
|
||||||
|
|
||||||
for (auto pml4e : s_global_pml4_entries)
|
|
||||||
ASSERT(pml4e == 0);
|
|
||||||
const uint64_t* pml4 = P2V(s_kernel->m_highest_paging_struct);
|
|
||||||
s_global_pml4_entries[511] = pml4[511];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::initialize_post_heap()
|
void PageTable::enable_cpu_features()
|
||||||
{
|
|
||||||
ASSERT(s_kernel);
|
|
||||||
|
|
||||||
init_map_hhdm(s_kernel->m_highest_paging_struct);
|
|
||||||
|
|
||||||
const paddr_t old_pml4_paddr = s_kernel->m_highest_paging_struct;
|
|
||||||
const paddr_t new_pml4_paddr = copy_page_from_kmalloc_to_heap(old_pml4_paddr);
|
|
||||||
|
|
||||||
uint64_t* old_pml4 = reinterpret_cast<uint64_t*>(kmalloc_vaddr_of(old_pml4_paddr).value());
|
|
||||||
uint64_t* new_pml4 = reinterpret_cast<uint64_t*>(new_pml4_paddr + s_hhdm_offset);
|
|
||||||
|
|
||||||
const paddr_t old_pdpt_paddr = old_pml4[511] & s_page_addr_mask;
|
|
||||||
const paddr_t new_pdpt_paddr = Heap::get().take_free_page();
|
|
||||||
ASSERT(new_pdpt_paddr);
|
|
||||||
|
|
||||||
uint64_t* old_pdpt = reinterpret_cast<uint64_t*>(old_pdpt_paddr + s_hhdm_offset);
|
|
||||||
uint64_t* new_pdpt = reinterpret_cast<uint64_t*>(new_pdpt_paddr + s_hhdm_offset);
|
|
||||||
copy_paging_structure_to_heap(old_pdpt, new_pdpt, 2);
|
|
||||||
|
|
||||||
new_pml4[511] = new_pdpt_paddr | (old_pml4[511] & s_page_flag_mask);
|
|
||||||
s_global_pml4_entries[511] = new_pml4[511];
|
|
||||||
|
|
||||||
s_kernel->m_highest_paging_struct = new_pml4_paddr;
|
|
||||||
s_kernel->load();
|
|
||||||
|
|
||||||
free_kmalloc_paging_structure(old_pdpt, 2);
|
|
||||||
kfree(reinterpret_cast<void*>(kmalloc_vaddr_of(old_pdpt_paddr).value()));
|
|
||||||
kfree(reinterpret_cast<void*>(kmalloc_vaddr_of(old_pml4_paddr).value()));
|
|
||||||
|
|
||||||
allocate_zeroed_page_aligned_page = &FuncsHHDM::allocate_zeroed_page_aligned_page;
|
|
||||||
unallocate_page = &FuncsHHDM::unallocate_page;
|
|
||||||
V2P = &FuncsHHDM::V2P;
|
|
||||||
P2V = &FuncsHHDM::P2V;
|
|
||||||
|
|
||||||
s_is_post_heap_done = true;
|
|
||||||
|
|
||||||
// This is a hack to unmap fast page. fast page pt is copied
|
|
||||||
// while it is mapped, so we need to manually unmap it
|
|
||||||
SpinLockGuard _(s_fast_page_lock);
|
|
||||||
unmap_fast_page();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::initial_load()
|
|
||||||
{
|
{
|
||||||
if (s_has_nxe)
|
if (s_has_nxe)
|
||||||
{
|
{
|
||||||
@@ -423,8 +256,63 @@ namespace Kernel
|
|||||||
"movq %%rax, %%cr0;"
|
"movq %%rax, %%cr0;"
|
||||||
::: "rax"
|
::: "rax"
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
load();
|
void PageTable::initialize_and_load()
|
||||||
|
{
|
||||||
|
detect_cpu_features();
|
||||||
|
enable_cpu_features();
|
||||||
|
|
||||||
|
const paddr_t boot_pml4_paddr = ({
|
||||||
|
paddr_t paddr;
|
||||||
|
asm volatile("movq %%cr3, %0" : "=r"(paddr));
|
||||||
|
paddr;
|
||||||
|
});
|
||||||
|
|
||||||
|
initialize_hhdm(boot_pml4_paddr);
|
||||||
|
|
||||||
|
ASSERT(s_kernel == nullptr);
|
||||||
|
s_kernel = new PageTable();
|
||||||
|
ASSERT(s_kernel != nullptr);
|
||||||
|
|
||||||
|
s_kernel->m_highest_paging_struct = allocate_zeroed_page_aligned_page();
|
||||||
|
ASSERT(s_kernel->m_highest_paging_struct);
|
||||||
|
|
||||||
|
uint64_t* pml4 = P2V(s_kernel->m_highest_paging_struct);
|
||||||
|
memcpy(pml4, s_global_pml4_entries, sizeof(s_global_pml4_entries));
|
||||||
|
s_kernel->map_kernel_memory();
|
||||||
|
s_global_pml4_entries[511] = pml4[511];
|
||||||
|
|
||||||
|
// update fast page pt
|
||||||
|
{
|
||||||
|
constexpr vaddr_t uc_vaddr = uncanonicalize(fast_page());
|
||||||
|
constexpr uint16_t pml4e = (uc_vaddr >> 39) & 0x1FF;
|
||||||
|
constexpr uint16_t pdpte = (uc_vaddr >> 30) & 0x1FF;
|
||||||
|
constexpr uint16_t pde = (uc_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))
|
||||||
|
{
|
||||||
|
table[entry] = allocate_zeroed_page_aligned_page();
|
||||||
|
ASSERT(table[entry]);
|
||||||
|
}
|
||||||
|
|
||||||
|
table[entry] |= flags;
|
||||||
|
|
||||||
|
return table[entry] & s_page_addr_mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
const paddr_t pml4 = s_kernel->m_highest_paging_struct;
|
||||||
|
const paddr_t pdpt = get_or_allocate_entry(pml4, pml4e, Flags::ReadWrite | Flags::Present);
|
||||||
|
const paddr_t pd = get_or_allocate_entry(pdpt, pdpte, Flags::ReadWrite | Flags::Present);
|
||||||
|
s_fast_page_pt = P2V(get_or_allocate_entry(pd, pde, Flags::ReadWrite | Flags::Present));
|
||||||
|
}
|
||||||
|
|
||||||
|
s_kernel->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
PageTable& PageTable::kernel()
|
PageTable& PageTable::kernel()
|
||||||
@@ -440,12 +328,12 @@ namespace Kernel
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::initialize_kernel()
|
void PageTable::map_kernel_memory()
|
||||||
{
|
{
|
||||||
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
|
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
|
||||||
const vaddr_t kernel_start = reinterpret_cast<vaddr_t>(g_kernel_start);
|
const vaddr_t kernel_start = reinterpret_cast<vaddr_t>(g_kernel_start);
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(kernel_start),
|
kernel_start - KERNEL_OFFSET + g_boot_info.kernel_paddr,
|
||||||
kernel_start,
|
kernel_start,
|
||||||
g_kernel_end - g_kernel_start,
|
g_kernel_end - g_kernel_start,
|
||||||
Flags::Present
|
Flags::Present
|
||||||
@@ -454,7 +342,7 @@ namespace Kernel
|
|||||||
// Map executable kernel memory as executable
|
// Map executable kernel memory as executable
|
||||||
const vaddr_t kernel_execute_start = reinterpret_cast<vaddr_t>(g_kernel_execute_start);
|
const vaddr_t kernel_execute_start = reinterpret_cast<vaddr_t>(g_kernel_execute_start);
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(kernel_execute_start),
|
kernel_execute_start - KERNEL_OFFSET + g_boot_info.kernel_paddr,
|
||||||
kernel_execute_start,
|
kernel_execute_start,
|
||||||
g_kernel_execute_end - g_kernel_execute_start,
|
g_kernel_execute_end - g_kernel_execute_start,
|
||||||
Flags::Execute | Flags::Present
|
Flags::Execute | Flags::Present
|
||||||
@@ -463,7 +351,7 @@ namespace Kernel
|
|||||||
// Map writable kernel memory as writable
|
// Map writable kernel memory as writable
|
||||||
const vaddr_t kernel_writable_start = reinterpret_cast<vaddr_t>(g_kernel_writable_start);
|
const vaddr_t kernel_writable_start = reinterpret_cast<vaddr_t>(g_kernel_writable_start);
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(kernel_writable_start),
|
kernel_writable_start - KERNEL_OFFSET + g_boot_info.kernel_paddr,
|
||||||
kernel_writable_start,
|
kernel_writable_start,
|
||||||
g_kernel_writable_end - g_kernel_writable_start,
|
g_kernel_writable_end - g_kernel_writable_start,
|
||||||
Flags::ReadWrite | Flags::Present
|
Flags::ReadWrite | Flags::Present
|
||||||
@@ -472,114 +360,58 @@ namespace Kernel
|
|||||||
// Map userspace memory
|
// Map userspace memory
|
||||||
const vaddr_t userspace_start = reinterpret_cast<vaddr_t>(g_userspace_start);
|
const vaddr_t userspace_start = reinterpret_cast<vaddr_t>(g_userspace_start);
|
||||||
map_range_at(
|
map_range_at(
|
||||||
V2P(userspace_start),
|
userspace_start - KERNEL_OFFSET + g_boot_info.kernel_paddr,
|
||||||
userspace_start,
|
userspace_start,
|
||||||
g_userspace_end - g_userspace_start,
|
g_userspace_end - g_userspace_start,
|
||||||
Flags::Execute | Flags::UserSupervisor | Flags::Present
|
Flags::Execute | Flags::UserSupervisor | Flags::Present
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::prepare_fast_page()
|
|
||||||
{
|
|
||||||
constexpr vaddr_t uc_vaddr = uncanonicalize(fast_page());
|
|
||||||
constexpr uint64_t pml4e = (uc_vaddr >> 39) & 0x1FF;
|
|
||||||
constexpr uint64_t pdpte = (uc_vaddr >> 30) & 0x1FF;
|
|
||||||
constexpr uint64_t pde = (uc_vaddr >> 21) & 0x1FF;
|
|
||||||
constexpr uint64_t pte = (uc_vaddr >> 12) & 0x1FF;
|
|
||||||
|
|
||||||
uint64_t* pml4 = P2V(m_highest_paging_struct);
|
|
||||||
ASSERT(!(pml4[pml4e] & Flags::Present));
|
|
||||||
pml4[pml4e] = allocate_zeroed_page_aligned_page() | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
|
|
||||||
ASSERT(!(pdpt[pdpte] & Flags::Present));
|
|
||||||
pdpt[pdpte] = allocate_zeroed_page_aligned_page() | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
ASSERT(!(pd[pde] & Flags::Present));
|
|
||||||
pd[pde] = allocate_zeroed_page_aligned_page() | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
ASSERT(pt[pte] == 0);
|
|
||||||
pt[pte] = Flags::Reserved;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PageTable::map_fast_page(paddr_t paddr)
|
void PageTable::map_fast_page(paddr_t paddr)
|
||||||
{
|
{
|
||||||
ASSERT(s_kernel);
|
ASSERT(paddr && paddr % PAGE_SIZE == 0);
|
||||||
ASSERT(paddr);
|
|
||||||
ASSERT(paddr % PAGE_SIZE == 0);
|
|
||||||
|
|
||||||
|
ASSERT(s_fast_page_pt);
|
||||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||||
|
|
||||||
constexpr vaddr_t uc_vaddr = uncanonicalize(fast_page());
|
ASSERT(!(*s_fast_page_pt & Flags::Present));
|
||||||
constexpr uint64_t pml4e = (uc_vaddr >> 39) & 0x1FF;
|
s_fast_page_pt[0] = paddr | Flags::ReadWrite | Flags::Present;
|
||||||
constexpr uint64_t pdpte = (uc_vaddr >> 30) & 0x1FF;
|
|
||||||
constexpr uint64_t pde = (uc_vaddr >> 21) & 0x1FF;
|
|
||||||
constexpr uint64_t pte = (uc_vaddr >> 12) & 0x1FF;
|
|
||||||
|
|
||||||
const uint64_t* pml4 = P2V(s_kernel->m_highest_paging_struct);
|
asm volatile("invlpg (%0)" :: "r"(fast_page()));
|
||||||
const uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
|
|
||||||
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
|
|
||||||
ASSERT(!(pt[pte] & Flags::Present));
|
|
||||||
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
|
|
||||||
|
|
||||||
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::unmap_fast_page()
|
void PageTable::unmap_fast_page()
|
||||||
{
|
{
|
||||||
ASSERT(s_kernel);
|
ASSERT(s_fast_page_pt);
|
||||||
|
|
||||||
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
ASSERT(s_fast_page_lock.current_processor_has_lock());
|
||||||
|
|
||||||
constexpr vaddr_t uc_vaddr = uncanonicalize(fast_page());
|
ASSERT((*s_fast_page_pt & Flags::Present));
|
||||||
constexpr uint64_t pml4e = (uc_vaddr >> 39) & 0x1FF;
|
s_fast_page_pt[0] = 0;
|
||||||
constexpr uint64_t pdpte = (uc_vaddr >> 30) & 0x1FF;
|
|
||||||
constexpr uint64_t pde = (uc_vaddr >> 21) & 0x1FF;
|
|
||||||
constexpr uint64_t pte = (uc_vaddr >> 12) & 0x1FF;
|
|
||||||
|
|
||||||
const uint64_t* pml4 = P2V(s_kernel->m_highest_paging_struct);
|
asm volatile("invlpg (%0)" :: "r"(fast_page()));
|
||||||
const uint64_t* pdpt = P2V(pml4[pml4e] & s_page_addr_mask);
|
|
||||||
const uint64_t* pd = P2V(pdpt[pdpte] & s_page_addr_mask);
|
|
||||||
uint64_t* pt = P2V(pd[pde] & s_page_addr_mask);
|
|
||||||
|
|
||||||
ASSERT(pt[pte] & Flags::Present);
|
|
||||||
pt[pte] = Flags::Reserved;
|
|
||||||
|
|
||||||
asm volatile("invlpg (%0)" :: "r"(fast_page()) : "memory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
||||||
{
|
{
|
||||||
SpinLockGuard _(s_kernel->m_lock);
|
SpinLockGuard _(s_kernel->m_lock);
|
||||||
|
|
||||||
PageTable* page_table = new PageTable;
|
PageTable* page_table = new PageTable;
|
||||||
if (page_table == nullptr)
|
if (page_table == nullptr)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
page_table->map_kernel_memory();
|
|
||||||
|
page_table->m_highest_paging_struct = allocate_zeroed_page_aligned_page();
|
||||||
|
if (page_table->m_highest_paging_struct == 0)
|
||||||
|
{
|
||||||
|
delete page_table;
|
||||||
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t* pml4 = P2V(page_table->m_highest_paging_struct);
|
||||||
|
memcpy(pml4, s_global_pml4_entries, sizeof(s_global_pml4_entries));
|
||||||
|
|
||||||
return page_table;
|
return page_table;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageTable::map_kernel_memory()
|
|
||||||
{
|
|
||||||
ASSERT(s_kernel);
|
|
||||||
ASSERT(s_global_pml4_entries[511]);
|
|
||||||
ASSERT(m_highest_paging_struct == 0);
|
|
||||||
m_highest_paging_struct = allocate_zeroed_page_aligned_page();
|
|
||||||
|
|
||||||
PageTable::with_fast_page(m_highest_paging_struct, [] {
|
|
||||||
for (size_t i = 0; i < 512; i++)
|
|
||||||
{
|
|
||||||
if (s_global_pml4_entries[i] == 0)
|
|
||||||
continue;
|
|
||||||
ASSERT(i >= 256);
|
|
||||||
PageTable::fast_page_as_sized<uint64_t>(i) = s_global_pml4_entries[i];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
PageTable::~PageTable()
|
PageTable::~PageTable()
|
||||||
{
|
{
|
||||||
if (m_highest_paging_struct == 0)
|
if (m_highest_paging_struct == 0)
|
||||||
@@ -624,7 +456,7 @@ namespace Kernel
|
|||||||
const bool is_userspace = (vaddr < KERNEL_OFFSET);
|
const bool is_userspace = (vaddr < KERNEL_OFFSET);
|
||||||
if (is_userspace && this != &PageTable::current())
|
if (is_userspace && this != &PageTable::current())
|
||||||
;
|
;
|
||||||
else if (pages <= 32 || !s_is_post_heap_done)
|
else if (pages <= 32 || !s_is_initialized)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < pages; i++, vaddr += PAGE_SIZE)
|
for (size_t i = 0; i < pages; i++, vaddr += PAGE_SIZE)
|
||||||
asm volatile("invlpg (%0)" :: "r"(vaddr));
|
asm volatile("invlpg (%0)" :: "r"(vaddr));
|
||||||
|
|||||||
@@ -97,27 +97,25 @@ bananboot_end:
|
|||||||
.align 4096
|
.align 4096
|
||||||
boot_pml4:
|
boot_pml4:
|
||||||
.quad V2P(boot_pdpt_lo) + (PG_READ_WRITE | PG_PRESENT)
|
.quad V2P(boot_pdpt_lo) + (PG_READ_WRITE | PG_PRESENT)
|
||||||
.rept 510
|
.skip 510 * 8
|
||||||
.quad 0
|
|
||||||
.endr
|
|
||||||
.quad V2P(boot_pdpt_hi) + (PG_READ_WRITE | PG_PRESENT)
|
.quad V2P(boot_pdpt_hi) + (PG_READ_WRITE | PG_PRESENT)
|
||||||
boot_pdpt_lo:
|
boot_pdpt_lo:
|
||||||
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
|
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
|
||||||
.rept 511
|
.skip 511 * 8
|
||||||
.quad 0
|
|
||||||
.endr
|
|
||||||
boot_pdpt_hi:
|
boot_pdpt_hi:
|
||||||
.rept 510
|
.skip 510 * 8
|
||||||
.quad 0
|
|
||||||
.endr
|
|
||||||
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
|
.quad V2P(boot_pd) + (PG_READ_WRITE | PG_PRESENT)
|
||||||
.quad 0
|
.skip 8
|
||||||
boot_pd:
|
boot_pd:
|
||||||
.set i, 0
|
.set i, 0
|
||||||
.rept 512
|
.rept 511
|
||||||
.quad i + (PG_PAGE_SIZE | PG_READ_WRITE | PG_PRESENT)
|
.quad i + (PG_PAGE_SIZE | PG_READ_WRITE | PG_PRESENT)
|
||||||
.set i, i + 0x200000
|
.set i, i + 0x200000
|
||||||
.endr
|
.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:
|
boot_gdt:
|
||||||
.quad 0x0000000000000000 # null descriptor
|
.quad 0x0000000000000000 # null descriptor
|
||||||
@@ -273,7 +271,7 @@ system_halt:
|
|||||||
jmp 1b
|
jmp 1b
|
||||||
|
|
||||||
|
|
||||||
#define AP_V2P(vaddr) ((vaddr) - ap_trampoline + 0xF000)
|
#define AP_REL(vaddr) ((vaddr) - ap_trampoline + 0xF000)
|
||||||
|
|
||||||
.section .ap_init, "ax"
|
.section .ap_init, "ax"
|
||||||
|
|
||||||
@@ -283,21 +281,27 @@ ap_trampoline:
|
|||||||
jmp 1f
|
jmp 1f
|
||||||
|
|
||||||
.align 8
|
.align 8
|
||||||
ap_stack_ptr:
|
ap_stack_paddr:
|
||||||
.skip 4
|
.skip 8
|
||||||
ap_stack_loaded:
|
ap_stack_vaddr:
|
||||||
.skip 1
|
.skip 8
|
||||||
|
ap_prepare_paging:
|
||||||
|
.skip 8
|
||||||
|
ap_page_table:
|
||||||
|
.skip 8
|
||||||
|
ap_ready:
|
||||||
|
.skip 8
|
||||||
|
|
||||||
1: cli; cld
|
1: cli; cld
|
||||||
ljmpl $0x00, $AP_V2P(ap_cs_clear)
|
ljmpl $0x00, $AP_REL(ap_cs_clear)
|
||||||
|
|
||||||
ap_cs_clear:
|
ap_cs_clear:
|
||||||
# load ap gdt and enter protected mode
|
# load ap gdt and enter protected mode
|
||||||
lgdt AP_V2P(ap_gdtr)
|
lgdt AP_REL(ap_gdtr)
|
||||||
movl %cr0, %eax
|
movl %cr0, %eax
|
||||||
orb $1, %al
|
orb $1, %al
|
||||||
movl %eax, %cr0
|
movl %eax, %cr0
|
||||||
ljmpl $0x08, $AP_V2P(ap_protected_mode)
|
ljmpl $0x08, $AP_REL(ap_protected_mode)
|
||||||
|
|
||||||
.code32
|
.code32
|
||||||
ap_protected_mode:
|
ap_protected_mode:
|
||||||
@@ -306,8 +310,7 @@ ap_protected_mode:
|
|||||||
movw %ax, %ss
|
movw %ax, %ss
|
||||||
movw %ax, %es
|
movw %ax, %es
|
||||||
|
|
||||||
movl AP_V2P(ap_stack_ptr), %esp
|
movl AP_REL(ap_stack_paddr), %esp
|
||||||
movb $1, AP_V2P(ap_stack_loaded)
|
|
||||||
|
|
||||||
leal V2P(enable_sse), %ecx; call *%ecx
|
leal V2P(enable_sse), %ecx; call *%ecx
|
||||||
leal V2P(enable_tsc), %ecx; call *%ecx
|
leal V2P(enable_tsc), %ecx; call *%ecx
|
||||||
@@ -315,28 +318,34 @@ ap_protected_mode:
|
|||||||
|
|
||||||
# load boot gdt and enter long mode
|
# load boot gdt and enter long mode
|
||||||
lgdt V2P(boot_gdtr)
|
lgdt V2P(boot_gdtr)
|
||||||
ljmpl $0x08, $AP_V2P(ap_long_mode)
|
ljmpl $0x08, $AP_REL(ap_long_mode)
|
||||||
|
|
||||||
.code64
|
.code64
|
||||||
ap_long_mode:
|
ap_long_mode:
|
||||||
# move stack pointer to higher half
|
movq $ap_higher_half, %rax
|
||||||
movl %esp, %esp
|
jmp *%rax
|
||||||
addq $KERNEL_OFFSET, %rsp
|
|
||||||
|
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
|
||||||
|
|
||||||
# clear rbp for stacktrace
|
# clear rbp for stacktrace
|
||||||
xorq %rbp, %rbp
|
xorq %rbp, %rbp
|
||||||
|
|
||||||
xorb %al, %al
|
|
||||||
1: pause
|
1: pause
|
||||||
cmpb %al, g_ap_startup_done
|
cmpb $0, g_ap_startup_done
|
||||||
jz 1b
|
je 1b
|
||||||
|
|
||||||
lock incb g_ap_running_count
|
lock incb g_ap_running_count
|
||||||
|
|
||||||
# jump to ap_main in higher half
|
call ap_main
|
||||||
movabsq $ap_main, %rcx
|
jmp system_halt
|
||||||
call *%rcx
|
|
||||||
jmp V2P(system_halt)
|
|
||||||
|
|
||||||
ap_gdt:
|
ap_gdt:
|
||||||
.quad 0x0000000000000000 # null descriptor
|
.quad 0x0000000000000000 # null descriptor
|
||||||
|
|||||||
@@ -15,17 +15,17 @@ SECTIONS
|
|||||||
*(.bananboot)
|
*(.bananboot)
|
||||||
*(.text.*)
|
*(.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)
|
.userspace ALIGN(4K) : AT(ADDR(.userspace) - KERNEL_OFFSET)
|
||||||
{
|
{
|
||||||
g_userspace_start = .;
|
g_userspace_start = .;
|
||||||
*(.userspace)
|
*(.userspace)
|
||||||
g_userspace_end = .;
|
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)
|
.rodata ALIGN(4K) : AT(ADDR(.rodata) - KERNEL_OFFSET)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Kernel::API
|
|||||||
|
|
||||||
struct SharedPage
|
struct SharedPage
|
||||||
{
|
{
|
||||||
uint8_t __sequence[0x100];
|
uint16_t gdt_cpu_offset;
|
||||||
|
|
||||||
uint32_t features;
|
uint32_t features;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
#define _kas_globbers
|
#define _kas_globbers
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define _kas_argument_var(index, value) register long _kas_a##index asm(_ban_stringify(_ban_get(index, _kas_arguments))) = (long)value;
|
#define _kas_argument_var(index, value) register long _kas_a##index asm(_ban_stringify(_ban_get(index, _kas_arguments))) = (long)(value);
|
||||||
#define _kas_dummy_var(index, value) register long _kas_d##index asm(#value);
|
#define _kas_dummy_var(index, value) register long _kas_d##index asm(#value);
|
||||||
#define _kas_input(index, _) "r"(_kas_a##index)
|
#define _kas_input(index, _) "r"(_kas_a##index)
|
||||||
#define _kas_output(index, _) , "=r"(_kas_d##index)
|
#define _kas_output(index, _) , "=r"(_kas_d##index)
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Array.h>
|
|
||||||
#include <BAN/CircularQueue.h>
|
|
||||||
#include <BAN/HashMap.h>
|
#include <BAN/HashMap.h>
|
||||||
#include <BAN/HashSet.h>
|
|
||||||
#include <kernel/FS/Inode.h>
|
#include <kernel/FS/Inode.h>
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
@@ -53,57 +49,52 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<void> fsync_impl() override { return {}; }
|
BAN::ErrorOr<void> fsync_impl() override { return {}; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct InodeRefPtrHash
|
|
||||||
{
|
|
||||||
BAN::hash_t operator()(const BAN::RefPtr<Inode>& inode)
|
|
||||||
{
|
|
||||||
return BAN::hash<const Inode*>()(inode.ptr());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ListenEventList
|
struct ListenEventList
|
||||||
{
|
{
|
||||||
BAN::Array<epoll_event, OPEN_MAX> events;
|
ListenEventList() = default;
|
||||||
uint32_t bitmap[(OPEN_MAX + 31) / 32] {};
|
|
||||||
|
ListenEventList(const ListenEventList&) = delete;
|
||||||
|
ListenEventList& operator=(const ListenEventList&) = delete;
|
||||||
|
|
||||||
|
ListenEventList(ListenEventList&& other)
|
||||||
|
: events(BAN::move(other.events))
|
||||||
|
{}
|
||||||
|
ListenEventList& operator=(ListenEventList&& other)
|
||||||
|
{
|
||||||
|
events = BAN::move(other.events);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::HashMap<int, epoll_event> events;
|
||||||
|
|
||||||
bool has_fd(int fd) const
|
bool has_fd(int fd) const
|
||||||
{
|
{
|
||||||
// For some reason having (fd < 0 || ...) makes GCC 15.1.0
|
return events.contains(fd);
|
||||||
// think bitmap access can be out of bounds...
|
|
||||||
if (static_cast<size_t>(fd) >= events.size())
|
|
||||||
return false;
|
|
||||||
return bitmap[fd / 32] & (1u << (fd % 32));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
for (auto val : bitmap)
|
return events.empty();
|
||||||
if (val != 0)
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_fd(int fd, epoll_event event)
|
BAN::ErrorOr<void> add_fd(int fd, epoll_event event)
|
||||||
{
|
{
|
||||||
ASSERT(!has_fd(fd));
|
TRY(events.insert(fd, event));
|
||||||
bitmap[fd / 32] |= (1u << (fd % 32));
|
return {};
|
||||||
events[fd] = event;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_fd(int fd)
|
void remove_fd(int fd)
|
||||||
{
|
{
|
||||||
ASSERT(has_fd(fd));
|
events.remove(fd);
|
||||||
bitmap[fd / 32] &= ~(1u << (fd % 32));
|
|
||||||
events[fd] = {};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThreadBlocker m_thread_blocker;
|
ThreadBlocker m_thread_blocker;
|
||||||
SpinLock m_ready_lock;
|
SpinLock m_ready_lock;
|
||||||
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t, InodeRefPtrHash> m_ready_events;
|
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t> m_ready_events;
|
||||||
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t, InodeRefPtrHash> m_processing_events;
|
BAN::HashMap<BAN::RefPtr<Inode>, uint32_t> m_processing_events;
|
||||||
BAN::HashMap<BAN::RefPtr<Inode>, ListenEventList, InodeRefPtrHash> m_listening_events;
|
BAN::HashMap<BAN::RefPtr<Inode>, ListenEventList> m_listening_events;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,6 +133,12 @@ namespace Kernel
|
|||||||
void set_gsbase(uintptr_t addr);
|
void set_gsbase(uintptr_t addr);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static uint16_t cpu_index_offset() { return m_cpu_index_offset; }
|
||||||
|
void set_cpu_index(uint8_t index)
|
||||||
|
{
|
||||||
|
write_entry(m_cpu_index_offset, 0, index, 0xF2, 0x4);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GDT() = default;
|
GDT() = default;
|
||||||
|
|
||||||
@@ -151,11 +157,13 @@ namespace Kernel
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
#if ARCH(x86_64)
|
#if ARCH(x86_64)
|
||||||
BAN::Array<SegmentDescriptor, 8> m_gdt; // null, kernel code, kernel data, user code (32 bit), user data, user code (64 bit), tss low, tss high
|
BAN::Array<SegmentDescriptor, 9> m_gdt; // null, kernel code, kernel data, user code (32 bit), user data, user code (64 bit), cpu-index, tss low, tss high
|
||||||
static constexpr uint16_t m_tss_offset = 0x30;
|
static constexpr uint16_t m_cpu_index_offset = 0x30;
|
||||||
|
static constexpr uint16_t m_tss_offset = 0x38;
|
||||||
#elif ARCH(i686)
|
#elif ARCH(i686)
|
||||||
BAN::Array<SegmentDescriptor, 9> m_gdt; // null, kernel code, kernel data, user code, user data, processor data, fsbase, gsbase, tss
|
BAN::Array<SegmentDescriptor, 10> m_gdt; // null, kernel code, kernel data, user code, user data, processor data, fsbase, gsbase, cpu-index, tss
|
||||||
static constexpr uint16_t m_tss_offset = 0x40;
|
static constexpr uint16_t m_cpu_index_offset = 0x40;
|
||||||
|
static constexpr uint16_t m_tss_offset = 0x48;
|
||||||
#endif
|
#endif
|
||||||
TaskStateSegment m_tss;
|
TaskStateSegment m_tss;
|
||||||
const GDTR m_gdtr {
|
const GDTR m_gdtr {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ namespace Kernel
|
|||||||
uint8_t back() const
|
uint8_t back() const
|
||||||
{
|
{
|
||||||
ASSERT(!empty());
|
ASSERT(!empty());
|
||||||
return reinterpret_cast<const uint8_t*>(m_vaddr)[m_tail + m_size];
|
return reinterpret_cast<const uint8_t*>(m_vaddr)[m_tail + m_size - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const { return m_size == 0; }
|
bool empty() const { return m_size == 0; }
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Kernel
|
|||||||
class DMARegion
|
class DMARegion
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<DMARegion>> create(size_t size);
|
static BAN::ErrorOr<BAN::UniqPtr<DMARegion>> create(size_t size, PageTable::MemoryType type = PageTable::MemoryType::Uncached);
|
||||||
~DMARegion();
|
~DMARegion();
|
||||||
|
|
||||||
size_t size() const { return m_size; }
|
size_t size() const { return m_size; }
|
||||||
|
|||||||
@@ -9,12 +9,6 @@
|
|||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
struct AddressRange
|
|
||||||
{
|
|
||||||
vaddr_t start;
|
|
||||||
vaddr_t end;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MemoryRegion
|
class MemoryRegion
|
||||||
{
|
{
|
||||||
BAN_NON_COPYABLE(MemoryRegion);
|
BAN_NON_COPYABLE(MemoryRegion);
|
||||||
|
|||||||
@@ -46,13 +46,22 @@ namespace Kernel
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void initialize_pre_heap();
|
static void initialize_fast_page();
|
||||||
static void initialize_post_heap();
|
static void initialize_and_load();
|
||||||
|
|
||||||
|
static void enable_cpu_features();
|
||||||
|
|
||||||
static PageTable& kernel();
|
static PageTable& kernel();
|
||||||
static PageTable& current() { return *reinterpret_cast<PageTable*>(Processor::get_current_page_table()); }
|
static PageTable& current() { return *reinterpret_cast<PageTable*>(Processor::get_current_page_table()); }
|
||||||
|
|
||||||
static constexpr vaddr_t fast_page() { return KERNEL_OFFSET; }
|
static constexpr vaddr_t fast_page()
|
||||||
|
{
|
||||||
|
#if ARCH(x86_64)
|
||||||
|
return 0xffffffffbfe00000;
|
||||||
|
#elif ARCH(i686)
|
||||||
|
return 0xffe00000;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
template<with_fast_page_callback F>
|
template<with_fast_page_callback F>
|
||||||
static void with_fast_page(paddr_t paddr, F callback)
|
static void with_fast_page(paddr_t paddr, F callback)
|
||||||
@@ -121,7 +130,6 @@ namespace Kernel
|
|||||||
vaddr_t reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX);
|
vaddr_t reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address = UINTPTR_MAX);
|
||||||
|
|
||||||
void load();
|
void load();
|
||||||
void initial_load();
|
|
||||||
|
|
||||||
void invalidate_page(vaddr_t addr, bool send_smp_message) { invalidate_range(addr, 1, send_smp_message); }
|
void invalidate_page(vaddr_t addr, bool send_smp_message) { invalidate_range(addr, 1, send_smp_message); }
|
||||||
void invalidate_range(vaddr_t addr, size_t pages, bool send_smp_message);
|
void invalidate_range(vaddr_t addr, size_t pages, bool send_smp_message);
|
||||||
@@ -129,14 +137,14 @@ namespace Kernel
|
|||||||
InterruptState lock() const { return m_lock.lock(); }
|
InterruptState lock() const { return m_lock.lock(); }
|
||||||
void unlock(InterruptState state) const { m_lock.unlock(state); }
|
void unlock(InterruptState state) const { m_lock.unlock(state); }
|
||||||
|
|
||||||
|
paddr_t paddr() const { return m_highest_paging_struct; }
|
||||||
|
|
||||||
void debug_dump();
|
void debug_dump();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PageTable() = default;
|
PageTable() = default;
|
||||||
uint64_t get_page_data(vaddr_t) const;
|
uint64_t get_page_data(vaddr_t) const;
|
||||||
void initialize_kernel();
|
|
||||||
void map_kernel_memory();
|
void map_kernel_memory();
|
||||||
void prepare_fast_page();
|
|
||||||
|
|
||||||
static void map_fast_page(paddr_t);
|
static void map_fast_page(paddr_t);
|
||||||
static void unmap_fast_page();
|
static void unmap_fast_page();
|
||||||
|
|||||||
@@ -23,4 +23,10 @@ namespace Kernel
|
|||||||
using vaddr_t = uintptr_t;
|
using vaddr_t = uintptr_t;
|
||||||
using paddr_t = uint64_t;
|
using paddr_t = uint64_t;
|
||||||
|
|
||||||
|
struct AddressRange
|
||||||
|
{
|
||||||
|
vaddr_t start;
|
||||||
|
vaddr_t end;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,42 +14,27 @@ namespace Kernel
|
|||||||
BAN_NON_MOVABLE(VirtualRange);
|
BAN_NON_MOVABLE(VirtualRange);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create virtual range to fixed virtual address
|
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr_range(PageTable&, AddressRange address_range, size_t, PageTable::flags_t flags, bool add_guard_pages);
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr(PageTable&, vaddr_t, size_t, PageTable::flags_t flags, bool preallocate_pages, bool add_guard_pages);
|
|
||||||
// Create virtual range to virtual address range
|
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr_range(PageTable&, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t, PageTable::flags_t flags, bool preallocate_pages, bool add_guard_pages);
|
|
||||||
~VirtualRange();
|
~VirtualRange();
|
||||||
|
|
||||||
vaddr_t vaddr() const { return m_vaddr + (m_has_guard_pages ? PAGE_SIZE : 0); }
|
vaddr_t vaddr() const { return m_vaddr + (m_has_guard_pages ? PAGE_SIZE : 0); }
|
||||||
size_t size() const { return m_size - (m_has_guard_pages ? 2 * PAGE_SIZE : 0); }
|
size_t size() const { return m_size - (m_has_guard_pages ? 2 * PAGE_SIZE : 0); }
|
||||||
PageTable::flags_t flags() const { return m_flags; }
|
PageTable::flags_t flags() const { return m_flags; }
|
||||||
|
|
||||||
paddr_t paddr_of(vaddr_t vaddr) const
|
paddr_t paddr_of(vaddr_t vaddr) const { return m_page_table.physical_address_of(vaddr & PAGE_ADDR_MASK); }
|
||||||
{
|
|
||||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
|
||||||
const size_t index = (vaddr - this->vaddr()) / PAGE_SIZE;
|
|
||||||
ASSERT(index < m_paddrs.size());
|
|
||||||
const paddr_t paddr = m_paddrs[index];
|
|
||||||
ASSERT(paddr);
|
|
||||||
return paddr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool contains(vaddr_t address) const { return vaddr() <= address && address < vaddr() + size(); }
|
bool contains(vaddr_t address) const { return vaddr() <= address && address < vaddr() + size(); }
|
||||||
|
|
||||||
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t address);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VirtualRange(PageTable&, bool preallocated, bool has_guard_pages, vaddr_t, size_t, PageTable::flags_t);
|
VirtualRange(PageTable&, bool has_guard_pages, vaddr_t, size_t, PageTable::flags_t);
|
||||||
BAN::ErrorOr<void> initialize();
|
BAN::ErrorOr<void> initialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PageTable& m_page_table;
|
PageTable& m_page_table;
|
||||||
const bool m_preallocated;
|
|
||||||
const bool m_has_guard_pages;
|
const bool m_has_guard_pages;
|
||||||
const vaddr_t m_vaddr;
|
const vaddr_t m_vaddr;
|
||||||
const size_t m_size;
|
const size_t m_size;
|
||||||
const PageTable::flags_t m_flags;
|
const PageTable::flags_t m_flags;
|
||||||
BAN::Vector<paddr_t> m_paddrs;
|
|
||||||
SpinLock m_lock;
|
SpinLock m_lock;
|
||||||
|
|
||||||
friend class BAN::UniqPtr<VirtualRange>;
|
friend class BAN::UniqPtr<VirtualRange>;
|
||||||
|
|||||||
@@ -13,4 +13,3 @@ void* kmalloc(size_t size, size_t align, bool force_identity_map = false);
|
|||||||
void kfree(void*);
|
void kfree(void*);
|
||||||
|
|
||||||
BAN::Optional<Kernel::paddr_t> kmalloc_paddr_of(Kernel::vaddr_t);
|
BAN::Optional<Kernel::paddr_t> kmalloc_paddr_of(Kernel::vaddr_t);
|
||||||
BAN::Optional<Kernel::vaddr_t> kmalloc_vaddr_of(Kernel::paddr_t);
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <BAN/Atomic.h>
|
#include <BAN/Atomic.h>
|
||||||
#include <BAN/ForwardList.h>
|
#include <BAN/ForwardList.h>
|
||||||
|
#include <BAN/Math.h>
|
||||||
|
|
||||||
#include <kernel/API/SharedPage.h>
|
#include <kernel/API/SharedPage.h>
|
||||||
#include <kernel/Arch.h>
|
#include <kernel/Arch.h>
|
||||||
@@ -58,6 +59,8 @@ namespace Kernel
|
|||||||
static Processor& create(ProcessorID id);
|
static Processor& create(ProcessorID id);
|
||||||
static Processor& initialize();
|
static Processor& initialize();
|
||||||
|
|
||||||
|
void allocate_stack();
|
||||||
|
|
||||||
static ProcessorID current_id() { return read_gs_sized<ProcessorID>(offsetof(Processor, m_id)); }
|
static ProcessorID current_id() { return read_gs_sized<ProcessorID>(offsetof(Processor, m_id)); }
|
||||||
static uint8_t current_index() { return read_gs_sized<uint8_t>(offsetof(Processor, m_index)); }
|
static uint8_t current_index() { return read_gs_sized<uint8_t>(offsetof(Processor, m_index)); }
|
||||||
static ProcessorID id_from_index(size_t index);
|
static ProcessorID id_from_index(size_t index);
|
||||||
@@ -100,11 +103,8 @@ namespace Kernel
|
|||||||
handle_smp_messages();
|
handle_smp_messages();
|
||||||
}
|
}
|
||||||
|
|
||||||
static uintptr_t current_stack_bottom() { return read_gs_sized<uintptr_t>(offsetof(Processor, m_stack)); }
|
vaddr_t stack_top_vaddr() const { return m_stack_vaddr + s_stack_size; }
|
||||||
static uintptr_t current_stack_top() { return current_stack_bottom() + s_stack_size; }
|
paddr_t stack_top_paddr() const { return m_stack_paddr + s_stack_size; }
|
||||||
|
|
||||||
uintptr_t stack_bottom() const { return reinterpret_cast<uintptr_t>(m_stack); }
|
|
||||||
uintptr_t stack_top() const { return stack_bottom() + s_stack_size; }
|
|
||||||
|
|
||||||
static void set_thread_syscall_stack(vaddr_t vaddr) { write_gs_sized<vaddr_t>(offsetof(Processor, m_thread_syscall_stack), vaddr); }
|
static void set_thread_syscall_stack(vaddr_t vaddr) { write_gs_sized<vaddr_t>(offsetof(Processor, m_thread_syscall_stack), vaddr); }
|
||||||
|
|
||||||
@@ -140,11 +140,7 @@ namespace Kernel
|
|||||||
static void disable_sse()
|
static void disable_sse()
|
||||||
{
|
{
|
||||||
uintptr_t dummy;
|
uintptr_t dummy;
|
||||||
#if ARCH(x86_64)
|
asm volatile("mov %%cr0, %0; or $0x08, %0; mov %0, %%cr0" : "=r"(dummy));
|
||||||
asm volatile("movq %%cr0, %0; orq $0x08, %0; movq %0, %%cr0" : "=r"(dummy));
|
|
||||||
#elif ARCH(i686)
|
|
||||||
asm volatile("movl %%cr0, %0; orl $0x08, %0; movl %0, %%cr0" : "=r"(dummy));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enable_sse()
|
static void enable_sse()
|
||||||
@@ -169,35 +165,17 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static T read_gs_sized(uintptr_t offset) requires(sizeof(T) <= 8)
|
static T read_gs_sized(uintptr_t offset) requires(sizeof(T) <= 8 && BAN::Math::is_power_of_two(sizeof(T)))
|
||||||
{
|
{
|
||||||
#define __ASM_INPUT(operation) asm volatile(operation " %%gs:%a[offset], %[result]" : [result]"=r"(result) : [offset]"ir"(offset))
|
T value;
|
||||||
T result;
|
asm volatile("mov %%gs:%a[offset], %[value]" : [value]"=r"(value) : [offset]"ir"(offset));
|
||||||
if constexpr(sizeof(T) == 8)
|
return value;
|
||||||
__ASM_INPUT("movq");
|
|
||||||
if constexpr(sizeof(T) == 4)
|
|
||||||
__ASM_INPUT("movl");
|
|
||||||
if constexpr(sizeof(T) == 2)
|
|
||||||
__ASM_INPUT("movw");
|
|
||||||
if constexpr(sizeof(T) == 1)
|
|
||||||
__ASM_INPUT("movb");
|
|
||||||
return result;
|
|
||||||
#undef __ASM_INPUT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void write_gs_sized(uintptr_t offset, T value) requires(sizeof(T) <= 8)
|
static void write_gs_sized(uintptr_t offset, T value) requires(sizeof(T) <= 8 && BAN::Math::is_power_of_two(sizeof(T)))
|
||||||
{
|
{
|
||||||
#define __ASM_INPUT(operation) asm volatile(operation " %[value], %%gs:%a[offset]" :: [value]"r"(value), [offset]"ir"(offset) : "memory")
|
asm volatile("mov %[value], %%gs:%a[offset]" :: [value]"r"(value), [offset]"ir"(offset) : "memory");
|
||||||
if constexpr(sizeof(T) == 8)
|
|
||||||
__ASM_INPUT("movq");
|
|
||||||
if constexpr(sizeof(T) == 4)
|
|
||||||
__ASM_INPUT("movl");
|
|
||||||
if constexpr(sizeof(T) == 2)
|
|
||||||
__ASM_INPUT("movw");
|
|
||||||
if constexpr(sizeof(T) == 1)
|
|
||||||
__ASM_INPUT("movb");
|
|
||||||
#undef __ASM_INPUT
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -215,8 +193,9 @@ namespace Kernel
|
|||||||
|
|
||||||
Thread* m_sse_thread { nullptr };
|
Thread* m_sse_thread { nullptr };
|
||||||
|
|
||||||
static constexpr size_t s_stack_size { 4096 };
|
static constexpr size_t s_stack_size { PAGE_SIZE };
|
||||||
void* m_stack { nullptr };
|
vaddr_t m_stack_vaddr { 0 };
|
||||||
|
paddr_t m_stack_paddr { 0 };
|
||||||
|
|
||||||
GDT* m_gdt { nullptr };
|
GDT* m_gdt { nullptr };
|
||||||
IDT* m_idt { nullptr };
|
IDT* m_idt { nullptr };
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ namespace Kernel
|
|||||||
|
|
||||||
uint64_t wake_time_ns { static_cast<uint64_t>(-1) };
|
uint64_t wake_time_ns { static_cast<uint64_t>(-1) };
|
||||||
|
|
||||||
SpinLock blocker_lock;
|
BAN::Atomic<ThreadBlocker*> blocker { nullptr };
|
||||||
ThreadBlocker* blocker { nullptr };
|
SchedulerQueueNode* block_chain_prev { nullptr };
|
||||||
|
SchedulerQueueNode* block_chain_next { nullptr };
|
||||||
|
|
||||||
ProcessorID processor_id { PROCESSOR_NONE };
|
ProcessorID processor_id { PROCESSOR_NONE };
|
||||||
bool blocked { false };
|
bool blocked { false };
|
||||||
|
|||||||
@@ -178,9 +178,7 @@ namespace Kernel
|
|||||||
bool m_is_userspace { false };
|
bool m_is_userspace { false };
|
||||||
bool m_delete_process { false };
|
bool m_delete_process { false };
|
||||||
|
|
||||||
bool m_has_custom_fsbase { false };
|
|
||||||
vaddr_t m_fsbase { 0 };
|
vaddr_t m_fsbase { 0 };
|
||||||
bool m_has_custom_gsbase { false };
|
|
||||||
vaddr_t m_gsbase { 0 };
|
vaddr_t m_gsbase { 0 };
|
||||||
|
|
||||||
SchedulerQueue::Node* m_scheduler_node { nullptr };
|
SchedulerQueue::Node* m_scheduler_node { nullptr };
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ namespace Kernel
|
|||||||
void block_with_wake_time_ns(uint64_t wake_time_ns, BaseMutex*);
|
void block_with_wake_time_ns(uint64_t wake_time_ns, BaseMutex*);
|
||||||
void unblock();
|
void unblock();
|
||||||
|
|
||||||
|
|
||||||
void block_with_timeout_ms(uint64_t timeout_ms, BaseMutex* mutex)
|
void block_with_timeout_ms(uint64_t timeout_ms, BaseMutex* mutex)
|
||||||
{
|
{
|
||||||
ASSERT(!BAN::Math::will_multiplication_overflow<uint64_t>(timeout_ms, 1'000'000));
|
ASSERT(!BAN::Math::will_multiplication_overflow<uint64_t>(timeout_ms, 1'000'000));
|
||||||
@@ -29,14 +28,12 @@ namespace Kernel
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void add_thread_to_block_queue(SchedulerQueue::Node*);
|
void add_thread_to_block_queue(SchedulerQueue::Node*);
|
||||||
void remove_blocked_thread(SchedulerQueue::Node*);
|
void remove_thread_from_block_queue(SchedulerQueue::Node*);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SchedulerQueue::Node* m_block_chain { nullptr };
|
||||||
SpinLock m_lock;
|
SpinLock m_lock;
|
||||||
|
|
||||||
SchedulerQueue::Node* m_block_chain[32] {};
|
|
||||||
size_t m_block_chain_length { 0 };
|
|
||||||
|
|
||||||
friend class Scheduler;
|
friend class Scheduler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -293,9 +293,25 @@ namespace Kernel
|
|||||||
dprintln("Trying to enable processor (lapic id {})", processor.apic_id);
|
dprintln("Trying to enable processor (lapic id {})", processor.apic_id);
|
||||||
|
|
||||||
auto& proc = Kernel::Processor::create(ProcessorID(processor.apic_id));
|
auto& proc = Kernel::Processor::create(ProcessorID(processor.apic_id));
|
||||||
|
proc.allocate_stack();
|
||||||
|
|
||||||
|
struct ap_init_info_t
|
||||||
|
{
|
||||||
|
uintptr_t stack_paddr;
|
||||||
|
uintptr_t stack_vaddr;
|
||||||
|
uintptr_t prepare_paging;
|
||||||
|
uintptr_t page_table;
|
||||||
|
uintptr_t ready;
|
||||||
|
};
|
||||||
|
|
||||||
PageTable::with_fast_page(ap_init_paddr, [&] {
|
PageTable::with_fast_page(ap_init_paddr, [&] {
|
||||||
PageTable::fast_page_as_sized<uint32_t>(2) = kmalloc_paddr_of(proc.stack_top()).value();
|
PageTable::fast_page_as<ap_init_info_t>(8) = {
|
||||||
PageTable::fast_page_as_sized<uint8_t>(13) = 0;
|
.stack_paddr = static_cast<uintptr_t>(proc.stack_top_paddr()),
|
||||||
|
.stack_vaddr = proc.stack_top_vaddr(),
|
||||||
|
.prepare_paging = reinterpret_cast<uintptr_t>(&PageTable::enable_cpu_features),
|
||||||
|
.page_table = static_cast<uintptr_t>(PageTable::kernel().paddr()),
|
||||||
|
.ready = 0,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
write_to_local_apic(LAPIC_ERROR_REG, 0x00);
|
write_to_local_apic(LAPIC_ERROR_REG, 0x00);
|
||||||
@@ -334,12 +350,9 @@ namespace Kernel
|
|||||||
|
|
||||||
// give processor upto 100 * 100 us + 200 us to boot
|
// give processor upto 100 * 100 us + 200 us to boot
|
||||||
PageTable::with_fast_page(ap_init_paddr, [&] {
|
PageTable::with_fast_page(ap_init_paddr, [&] {
|
||||||
for (int i = 0; i < 100; i++)
|
for (int i = 0; i < 100; i++, udelay(100))
|
||||||
{
|
if (__atomic_load_n(&PageTable::fast_page_as<ap_init_info_t>(8).ready, __ATOMIC_SEQ_CST))
|
||||||
if (__atomic_load_n(&PageTable::fast_page_as_sized<uint8_t>(13), __ATOMIC_SEQ_CST))
|
|
||||||
break;
|
break;
|
||||||
udelay(100);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
initialized_aps++;
|
initialized_aps++;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
#include <kernel/Terminal/TTY.h>
|
#include <kernel/Terminal/TTY.h>
|
||||||
#include <kernel/Timer/Timer.h>
|
#include <kernel/Timer/Timer.h>
|
||||||
|
|
||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
|
|
||||||
#include <LibDEFLATE/Compressor.h>
|
#include <LibDEFLATE/Compressor.h>
|
||||||
#include <LibQR/QRCode.h>
|
#include <LibQR/QRCode.h>
|
||||||
|
|
||||||
@@ -14,6 +16,13 @@
|
|||||||
|
|
||||||
bool g_disable_debug = false;
|
bool g_disable_debug = false;
|
||||||
|
|
||||||
|
extern "C" bool safe_user_memcpy(void*, const void*, size_t);
|
||||||
|
|
||||||
|
namespace Kernel
|
||||||
|
{
|
||||||
|
extern bool g_safe_user_alloc_nonexisting;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Debug
|
namespace Debug
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -45,48 +54,25 @@ namespace Debug
|
|||||||
|
|
||||||
SpinLockGuard _(s_debug_lock);
|
SpinLockGuard _(s_debug_lock);
|
||||||
|
|
||||||
const stackframe* frame = reinterpret_cast<const stackframe*>(bp);
|
const bool temp = g_safe_user_alloc_nonexisting;
|
||||||
|
g_safe_user_alloc_nonexisting = false;
|
||||||
void* first_ip = frame->ip;
|
BAN::ScopeGuard alloc_restore([temp] { g_safe_user_alloc_nonexisting = temp; });
|
||||||
void* last_ip = 0;
|
|
||||||
bool first = true;
|
|
||||||
|
|
||||||
BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n");
|
BAN::Formatter::print(Debug::putchar, "\e[36mStack trace:\r\n");
|
||||||
|
|
||||||
if (ip != 0)
|
if (ip != 0)
|
||||||
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(ip));
|
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(ip));
|
||||||
|
|
||||||
while (frame)
|
stackframe frame;
|
||||||
|
if (!safe_user_memcpy(&frame, reinterpret_cast<void*>(bp), sizeof(stackframe)))
|
||||||
|
;
|
||||||
|
else for (size_t depth = 0; depth < 64; depth++)
|
||||||
{
|
{
|
||||||
if (!PageTable::is_valid_pointer((vaddr_t)frame))
|
BAN::Formatter::print(Debug::putchar, " {}\r\n", reinterpret_cast<void*>(frame.ip));
|
||||||
{
|
if (frame.bp == nullptr || !safe_user_memcpy(&frame, frame.bp, sizeof(stackframe)))
|
||||||
derrorln("invalid pointer {H}", (vaddr_t)frame);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if (PageTable::current().is_page_free((vaddr_t)frame & PAGE_ADDR_MASK))
|
|
||||||
{
|
|
||||||
derrorln(" {} not mapped", frame);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::Formatter::print(Debug::putchar, " {}\r\n", (void*)frame->ip);
|
|
||||||
|
|
||||||
if (!first && frame->ip == first_ip)
|
|
||||||
{
|
|
||||||
derrorln("looping kernel panic :(");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (!first && frame->ip == last_ip)
|
|
||||||
{
|
|
||||||
derrorln("repeating stack trace");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_ip = frame->ip;
|
|
||||||
frame = frame->bp;
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::Formatter::print(Debug::putchar, "\e[m");
|
BAN::Formatter::print(Debug::putchar, "\e[m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,10 +84,10 @@ namespace Kernel
|
|||||||
|
|
||||||
m_video_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
m_video_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET, UINTPTR_MAX,
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
BAN::Math::div_round_up<size_t>(m_width * m_height * (BANAN_FB_BPP / 8), PAGE_SIZE) * PAGE_SIZE,
|
BAN::Math::div_round_up<size_t>(m_width * m_height * (BANAN_FB_BPP / 8), PAGE_SIZE) * PAGE_SIZE,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, false
|
false
|
||||||
));
|
));
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace Kernel
|
|||||||
|
|
||||||
Epoll::~Epoll()
|
Epoll::~Epoll()
|
||||||
{
|
{
|
||||||
for (auto [inode, _] : m_listening_events)
|
for (auto& [inode, _] : m_listening_events)
|
||||||
inode->del_epoll(this);
|
inode->del_epoll(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ namespace Kernel
|
|||||||
|
|
||||||
if (!contains_inode)
|
if (!contains_inode)
|
||||||
TRY(inode->add_epoll(this));
|
TRY(inode->add_epoll(this));
|
||||||
it->value.add_fd(fd, event);
|
TRY(it->value.add_fd(fd, event));
|
||||||
|
|
||||||
SpinLockGuard _(m_ready_lock);
|
SpinLockGuard _(m_ready_lock);
|
||||||
auto ready_it = m_ready_events.find(inode);
|
auto ready_it = m_ready_events.find(inode);
|
||||||
@@ -144,9 +144,8 @@ namespace Kernel
|
|||||||
|
|
||||||
{
|
{
|
||||||
uint32_t listen_mask = EPOLLHUP | EPOLLERR;
|
uint32_t listen_mask = EPOLLHUP | EPOLLERR;
|
||||||
for (size_t fd = 0; fd < listen.events.size(); fd++)
|
for (const auto& [_, events] : listen.events)
|
||||||
if (listen.has_fd(fd))
|
listen_mask |= events.events;
|
||||||
listen_mask |= listen.events[fd].events;
|
|
||||||
events &= listen_mask;
|
events &= listen_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,11 +170,10 @@ namespace Kernel
|
|||||||
|
|
||||||
#undef REMOVE_IT
|
#undef REMOVE_IT
|
||||||
|
|
||||||
for (size_t fd = 0; fd < listen.events.size() && event_count < event_span.size(); fd++)
|
for (auto& [_, listen_event] : listen.events)
|
||||||
{
|
{
|
||||||
if (!listen.has_fd(fd))
|
if (event_count >= event_span.size())
|
||||||
continue;
|
break;
|
||||||
auto& listen_event = listen.events[fd];
|
|
||||||
|
|
||||||
const auto new_events = (listen_event.events | EPOLLHUP | EPOLLERR) & events;
|
const auto new_events = (listen_event.events | EPOLLHUP | EPOLLERR) & events;
|
||||||
if (new_events == 0)
|
if (new_events == 0)
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ namespace Kernel
|
|||||||
gdt->write_entry(0x38, 0x00000000, 0x00000, 0xF2, 0xC); // gsbase
|
gdt->write_entry(0x38, 0x00000000, 0x00000, 0xF2, 0xC); // gsbase
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
gdt->write_entry(m_cpu_index_offset, 0, 0, 0, 0);
|
||||||
|
|
||||||
gdt->write_tss();
|
gdt->write_tss();
|
||||||
|
|
||||||
return gdt;
|
return gdt;
|
||||||
|
|||||||
@@ -191,6 +191,8 @@ namespace Kernel
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool g_safe_user_alloc_nonexisting { true };
|
||||||
|
|
||||||
extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack* interrupt_stack, const Registers* regs)
|
extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack* interrupt_stack, const Registers* regs)
|
||||||
{
|
{
|
||||||
if (g_paniced)
|
if (g_paniced)
|
||||||
@@ -214,6 +216,16 @@ namespace Kernel
|
|||||||
PageFaultError page_fault_error;
|
PageFaultError page_fault_error;
|
||||||
page_fault_error.raw = error;
|
page_fault_error.raw = error;
|
||||||
|
|
||||||
|
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip);
|
||||||
|
|
||||||
|
if (!g_safe_user_alloc_nonexisting) for (const auto& safe_user : s_safe_user_page_faults)
|
||||||
|
{
|
||||||
|
if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
|
||||||
|
continue;
|
||||||
|
interrupt_stack->ip = reinterpret_cast<vaddr_t>(safe_user.ip_fault);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Processor::set_interrupt_state(InterruptState::Enabled);
|
Processor::set_interrupt_state(InterruptState::Enabled);
|
||||||
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write, page_fault_error.instruction);
|
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2, page_fault_error.write, page_fault_error.instruction);
|
||||||
Processor::set_interrupt_state(InterruptState::Disabled);
|
Processor::set_interrupt_state(InterruptState::Disabled);
|
||||||
@@ -234,8 +246,7 @@ namespace Kernel
|
|||||||
if (result.value())
|
if (result.value())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip);
|
if (g_safe_user_alloc_nonexisting) for (const auto& safe_user : s_safe_user_page_faults)
|
||||||
for (const auto& safe_user : s_safe_user_page_faults)
|
|
||||||
{
|
{
|
||||||
if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
|
if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
|
||||||
continue;
|
continue;
|
||||||
@@ -282,7 +293,7 @@ namespace Kernel
|
|||||||
|
|
||||||
const char* process_name = (tid && Thread::current().has_process())
|
const char* process_name = (tid && Thread::current().has_process())
|
||||||
? Process::current().name()
|
? Process::current().name()
|
||||||
: nullptr;
|
: "";
|
||||||
|
|
||||||
#if ARCH(x86_64)
|
#if ARCH(x86_64)
|
||||||
dwarnln(
|
dwarnln(
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<DMARegion>> DMARegion::create(size_t size)
|
BAN::ErrorOr<BAN::UniqPtr<DMARegion>> DMARegion::create(size_t size, PageTable::MemoryType type)
|
||||||
{
|
{
|
||||||
size_t needed_pages = BAN::Math::div_round_up<size_t>(size, PAGE_SIZE);
|
size_t needed_pages = BAN::Math::div_round_up<size_t>(size, PAGE_SIZE);
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ namespace Kernel
|
|||||||
vaddr_guard.disable();
|
vaddr_guard.disable();
|
||||||
paddr_guard.disable();
|
paddr_guard.disable();
|
||||||
|
|
||||||
PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::ReadWrite | PageTable::Flags::Present, PageTable::MemoryType::Uncached);
|
PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::ReadWrite | PageTable::Flags::Present, type);
|
||||||
|
|
||||||
return BAN::UniqPtr<DMARegion>::adopt(region_ptr);
|
return BAN::UniqPtr<DMARegion>::adopt(region_ptr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,12 @@ namespace Kernel
|
|||||||
ASSERT(success);
|
ASSERT(success);
|
||||||
|
|
||||||
for (size_t i = 0; i < pages.size(); i++)
|
for (size_t i = 0; i < pages.size(); i++)
|
||||||
if (pages[i])
|
{
|
||||||
sync(i);
|
if (pages[i] == 0)
|
||||||
|
continue;
|
||||||
|
sync(i);
|
||||||
|
Heap::get().release_page(pages[i]);
|
||||||
|
}
|
||||||
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,47 @@
|
|||||||
|
#include <BAN/ScopeGuard.h>
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Memory/VirtualRange.h>
|
#include <kernel/Memory/VirtualRange.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr(PageTable& page_table, vaddr_t vaddr, size_t size, PageTable::flags_t flags, bool preallocate_pages, bool add_guard_pages)
|
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr_range(PageTable& page_table, AddressRange address_range, size_t size, PageTable::flags_t flags, bool add_guard_pages)
|
||||||
{
|
|
||||||
ASSERT(size % PAGE_SIZE == 0);
|
|
||||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
|
||||||
ASSERT(vaddr > 0);
|
|
||||||
|
|
||||||
if (add_guard_pages)
|
|
||||||
{
|
|
||||||
vaddr -= PAGE_SIZE;
|
|
||||||
size += 2 * PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = TRY(BAN::UniqPtr<VirtualRange>::create(page_table, preallocate_pages, add_guard_pages, vaddr, size, flags));
|
|
||||||
ASSERT(page_table.reserve_range(vaddr, size));
|
|
||||||
TRY(result->initialize());
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> VirtualRange::create_to_vaddr_range(PageTable& page_table, vaddr_t vaddr_start, vaddr_t vaddr_end, size_t size, PageTable::flags_t flags, bool preallocate_pages, bool add_guard_pages)
|
|
||||||
{
|
{
|
||||||
if (add_guard_pages)
|
if (add_guard_pages)
|
||||||
size += 2 * PAGE_SIZE;
|
size += 2 * PAGE_SIZE;
|
||||||
|
|
||||||
ASSERT(size % PAGE_SIZE == 0);
|
ASSERT(size % PAGE_SIZE == 0);
|
||||||
ASSERT(vaddr_start > 0);
|
|
||||||
ASSERT(vaddr_start + size <= vaddr_end);
|
|
||||||
|
|
||||||
// Align vaddr range to page boundaries
|
// Align vaddr range to page boundaries
|
||||||
if (size_t rem = vaddr_start % PAGE_SIZE)
|
if (const size_t rem = address_range.start % PAGE_SIZE)
|
||||||
vaddr_start += PAGE_SIZE - rem;
|
address_range.start += PAGE_SIZE - rem;
|
||||||
if (size_t rem = vaddr_end % PAGE_SIZE)
|
if (const size_t rem = address_range.end % PAGE_SIZE)
|
||||||
vaddr_end -= rem;
|
address_range.end -= rem;
|
||||||
ASSERT(vaddr_start < vaddr_end);
|
|
||||||
ASSERT(vaddr_end - vaddr_start + 1 >= size / PAGE_SIZE);
|
|
||||||
|
|
||||||
const vaddr_t vaddr = page_table.reserve_free_contiguous_pages(size / PAGE_SIZE, vaddr_start, vaddr_end);
|
const vaddr_t vaddr = page_table.reserve_free_contiguous_pages(size / PAGE_SIZE, address_range.start, address_range.end);
|
||||||
if (vaddr == 0)
|
if (vaddr == 0)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
ASSERT(vaddr >= vaddr_start);
|
|
||||||
ASSERT(vaddr + size <= vaddr_end);
|
|
||||||
|
|
||||||
auto result_or_error = BAN::UniqPtr<VirtualRange>::create(page_table, preallocate_pages, add_guard_pages, vaddr, size, flags);
|
BAN::ScopeGuard vaddr_cleaner([&page_table, vaddr, size] {
|
||||||
if (result_or_error.is_error())
|
|
||||||
{
|
|
||||||
page_table.unmap_range(vaddr, size);
|
page_table.unmap_range(vaddr, size);
|
||||||
return result_or_error.release_error();
|
});
|
||||||
}
|
|
||||||
|
|
||||||
auto result = result_or_error.release_value();
|
auto result = TRY(BAN::UniqPtr<VirtualRange>::create(
|
||||||
|
page_table,
|
||||||
|
add_guard_pages,
|
||||||
|
vaddr,
|
||||||
|
size,
|
||||||
|
flags)
|
||||||
|
);
|
||||||
TRY(result->initialize());
|
TRY(result->initialize());
|
||||||
|
|
||||||
|
vaddr_cleaner.disable();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualRange::VirtualRange(PageTable& page_table, bool preallocated, bool has_guard_pages, vaddr_t vaddr, size_t size, PageTable::flags_t flags)
|
VirtualRange::VirtualRange(PageTable& page_table, bool has_guard_pages, vaddr_t vaddr, size_t size, PageTable::flags_t flags)
|
||||||
: m_page_table(page_table)
|
: m_page_table(page_table)
|
||||||
, m_preallocated(preallocated)
|
|
||||||
, m_has_guard_pages(has_guard_pages)
|
, m_has_guard_pages(has_guard_pages)
|
||||||
, m_vaddr(vaddr)
|
, m_vaddr(vaddr)
|
||||||
, m_size(size)
|
, m_size(size)
|
||||||
@@ -71,70 +51,26 @@ namespace Kernel
|
|||||||
VirtualRange::~VirtualRange()
|
VirtualRange::~VirtualRange()
|
||||||
{
|
{
|
||||||
ASSERT(m_vaddr);
|
ASSERT(m_vaddr);
|
||||||
m_page_table.unmap_range(m_vaddr, m_size);
|
for (size_t off = 0; off < size(); off += PAGE_SIZE)
|
||||||
|
if (const auto paddr = m_page_table.physical_address_of(vaddr() + off))
|
||||||
for (paddr_t paddr : m_paddrs)
|
|
||||||
if (paddr != 0)
|
|
||||||
Heap::get().release_page(paddr);
|
Heap::get().release_page(paddr);
|
||||||
|
m_page_table.unmap_range(m_vaddr, m_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<void> VirtualRange::initialize()
|
BAN::ErrorOr<void> VirtualRange::initialize()
|
||||||
{
|
{
|
||||||
TRY(m_paddrs.resize(size() / PAGE_SIZE, 0));
|
|
||||||
|
|
||||||
if (!m_preallocated)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const size_t page_count = size() / PAGE_SIZE;
|
const size_t page_count = size() / PAGE_SIZE;
|
||||||
for (size_t i = 0; i < page_count; i++)
|
for (size_t i = 0; i < page_count; i++)
|
||||||
{
|
{
|
||||||
m_paddrs[i] = Heap::get().take_free_page();
|
const auto paddr = Heap::get().take_free_page();
|
||||||
if (m_paddrs[i] == 0)
|
if (paddr == 0)
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
return BAN::Error::from_errno(ENOMEM);
|
||||||
m_page_table.map_page_at(m_paddrs[i], vaddr() + i * PAGE_SIZE, m_flags);
|
PageTable::with_fast_page(paddr, [] {
|
||||||
|
memset(PageTable::fast_page_as_ptr(), 0, PAGE_SIZE);
|
||||||
|
});
|
||||||
|
m_page_table.map_page_at(paddr, vaddr() + i * PAGE_SIZE, m_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (&PageTable::current() == &m_page_table || &PageTable::kernel() == &m_page_table)
|
|
||||||
memset(reinterpret_cast<void*>(vaddr()), 0, size());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const size_t page_count = size() / PAGE_SIZE;
|
|
||||||
for (size_t i = 0; i < page_count; i++)
|
|
||||||
{
|
|
||||||
PageTable::with_fast_page(m_paddrs[i], [&] {
|
|
||||||
memset(PageTable::fast_page_as_ptr(), 0, PAGE_SIZE);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<bool> VirtualRange::allocate_page_for_demand_paging(vaddr_t vaddr)
|
|
||||||
{
|
|
||||||
ASSERT(contains(vaddr));
|
|
||||||
vaddr &= PAGE_ADDR_MASK;
|
|
||||||
|
|
||||||
if (m_preallocated)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
SpinLockGuard _(m_lock);
|
|
||||||
|
|
||||||
const size_t index = (vaddr - this->vaddr()) / PAGE_SIZE;
|
|
||||||
if (m_paddrs[index])
|
|
||||||
return false;
|
|
||||||
|
|
||||||
m_paddrs[index] = Heap::get().take_free_page();
|
|
||||||
if (m_paddrs[index] == 0)
|
|
||||||
return BAN::Error::from_errno(ENOMEM);
|
|
||||||
|
|
||||||
PageTable::with_fast_page(m_paddrs[index], []{
|
|
||||||
memset(PageTable::fast_page_as_ptr(), 0x00, PAGE_SIZE);
|
|
||||||
});
|
|
||||||
|
|
||||||
m_page_table.map_page_at(m_paddrs[index], vaddr, m_flags);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -436,12 +436,3 @@ BAN::Optional<Kernel::paddr_t> kmalloc_paddr_of(Kernel::vaddr_t vaddr)
|
|||||||
return {};
|
return {};
|
||||||
return vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
|
return vaddr - KERNEL_OFFSET + g_boot_info.kernel_paddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::Optional<Kernel::vaddr_t> kmalloc_vaddr_of(Kernel::paddr_t paddr)
|
|
||||||
{
|
|
||||||
using namespace Kernel;
|
|
||||||
const vaddr_t vaddr = paddr + KERNEL_OFFSET - g_boot_info.kernel_paddr;
|
|
||||||
if (!is_kmalloc_vaddr(vaddr))
|
|
||||||
return {};
|
|
||||||
return vaddr;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> E1000::initialize_rx()
|
BAN::ErrorOr<void> E1000::initialize_rx()
|
||||||
{
|
{
|
||||||
m_rx_buffer_region = TRY(DMARegion::create(E1000_RX_BUFFER_SIZE * E1000_RX_DESCRIPTOR_COUNT));
|
m_rx_buffer_region = TRY(DMARegion::create(E1000_RX_BUFFER_SIZE * E1000_RX_DESCRIPTOR_COUNT, PageTable::MemoryType::Normal));
|
||||||
m_rx_descriptor_region = TRY(DMARegion::create(sizeof(e1000_rx_desc) * E1000_RX_DESCRIPTOR_COUNT));
|
m_rx_descriptor_region = TRY(DMARegion::create(sizeof(e1000_rx_desc) * E1000_RX_DESCRIPTOR_COUNT));
|
||||||
|
|
||||||
auto* rx_descriptors = reinterpret_cast<volatile e1000_rx_desc*>(m_rx_descriptor_region->vaddr());
|
auto* rx_descriptors = reinterpret_cast<volatile e1000_rx_desc*>(m_rx_descriptor_region->vaddr());
|
||||||
@@ -207,7 +207,7 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> E1000::initialize_tx()
|
BAN::ErrorOr<void> E1000::initialize_tx()
|
||||||
{
|
{
|
||||||
m_tx_buffer_region = TRY(DMARegion::create(E1000_TX_BUFFER_SIZE * E1000_TX_DESCRIPTOR_COUNT));
|
m_tx_buffer_region = TRY(DMARegion::create(E1000_TX_BUFFER_SIZE * E1000_TX_DESCRIPTOR_COUNT, PageTable::MemoryType::Normal));
|
||||||
m_tx_descriptor_region = TRY(DMARegion::create(sizeof(e1000_tx_desc) * E1000_TX_DESCRIPTOR_COUNT));
|
m_tx_descriptor_region = TRY(DMARegion::create(sizeof(e1000_tx_desc) * E1000_TX_DESCRIPTOR_COUNT));
|
||||||
|
|
||||||
auto* tx_descriptors = reinterpret_cast<volatile e1000_tx_desc*>(m_tx_descriptor_region->vaddr());
|
auto* tx_descriptors = reinterpret_cast<volatile e1000_tx_desc*>(m_tx_descriptor_region->vaddr());
|
||||||
@@ -304,7 +304,7 @@ namespace Kernel
|
|||||||
// FIXME: there isnt really any reason to wait for transmission
|
// FIXME: there isnt really any reason to wait for transmission
|
||||||
write32(REG_TDT, (tx_current + 1) % E1000_TX_DESCRIPTOR_COUNT);
|
write32(REG_TDT, (tx_current + 1) % E1000_TX_DESCRIPTOR_COUNT);
|
||||||
while (descriptor.status == 0)
|
while (descriptor.status == 0)
|
||||||
continue;
|
Processor::pause();
|
||||||
|
|
||||||
dprintln_if(DEBUG_E1000, "sent {} bytes", packet_size);
|
dprintln_if(DEBUG_E1000, "sent {} bytes", packet_size);
|
||||||
|
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ namespace Kernel
|
|||||||
|
|
||||||
loopback->m_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
loopback->m_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET,
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
BAN::numeric_limits<vaddr_t>::max(),
|
|
||||||
buffer_size * buffer_count,
|
buffer_size * buffer_count,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, false
|
false
|
||||||
));
|
));
|
||||||
|
|
||||||
auto* thread = TRY(Thread::create_kernel([](void* loopback_ptr) {
|
auto* thread = TRY(Thread::create_kernel([](void* loopback_ptr) {
|
||||||
|
|||||||
@@ -18,16 +18,20 @@ namespace Kernel
|
|||||||
|
|
||||||
const uint16_t* buffer_u16 = reinterpret_cast<const uint16_t*>(buffer.data());
|
const uint16_t* buffer_u16 = reinterpret_cast<const uint16_t*>(buffer.data());
|
||||||
for (size_t j = 0; j < buffer.size() / 2; j++)
|
for (size_t j = 0; j < buffer.size() / 2; j++)
|
||||||
checksum += BAN::host_to_network_endian(buffer_u16[j]);
|
checksum += buffer_u16[j];
|
||||||
if (buffer.size() % 2 == 0)
|
|
||||||
continue;
|
if (buffer.size() % 2)
|
||||||
ASSERT(i == buffers.size() - 1);
|
{
|
||||||
checksum += buffer[buffer.size() - 1] << 8;
|
// NOTE: we only allow last buffer to be odd-length
|
||||||
|
ASSERT(i == buffers.size() - 1);
|
||||||
|
checksum += buffer[buffer.size() - 1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (checksum >> 16)
|
while (checksum >> 16)
|
||||||
checksum = (checksum >> 16) + (checksum & 0xFFFF);
|
checksum = (checksum & 0xFFFF) + (checksum >> 16);
|
||||||
return ~(uint16_t)checksum;
|
|
||||||
|
return BAN::host_to_network_endian<uint16_t>(~checksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> RTL8169::initialize_rx()
|
BAN::ErrorOr<void> RTL8169::initialize_rx()
|
||||||
{
|
{
|
||||||
m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * s_buffer_size));
|
m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * s_buffer_size, PageTable::MemoryType::Normal));
|
||||||
m_rx_descriptor_region = TRY(DMARegion::create(m_rx_descriptor_count * sizeof(RTL8169Descriptor)));
|
m_rx_descriptor_region = TRY(DMARegion::create(m_rx_descriptor_count * sizeof(RTL8169Descriptor)));
|
||||||
|
|
||||||
for (size_t i = 0; i < m_rx_descriptor_count; i++)
|
for (size_t i = 0; i < m_rx_descriptor_count; i++)
|
||||||
@@ -144,7 +144,7 @@ namespace Kernel
|
|||||||
|
|
||||||
BAN::ErrorOr<void> RTL8169::initialize_tx()
|
BAN::ErrorOr<void> RTL8169::initialize_tx()
|
||||||
{
|
{
|
||||||
m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * s_buffer_size));
|
m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * s_buffer_size, PageTable::MemoryType::Normal));
|
||||||
m_tx_descriptor_region = TRY(DMARegion::create(m_tx_descriptor_count * sizeof(RTL8169Descriptor)));
|
m_tx_descriptor_region = TRY(DMARegion::create(m_tx_descriptor_count * sizeof(RTL8169Descriptor)));
|
||||||
|
|
||||||
for (size_t i = 0; i < m_tx_descriptor_count; i++)
|
for (size_t i = 0; i < m_tx_descriptor_count; i++)
|
||||||
|
|||||||
@@ -14,11 +14,10 @@ namespace Kernel
|
|||||||
auto socket = TRY(BAN::RefPtr<UDPSocket>::create(network_layer, info));
|
auto socket = TRY(BAN::RefPtr<UDPSocket>::create(network_layer, info));
|
||||||
socket->m_packet_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
socket->m_packet_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET,
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
~(uintptr_t)0,
|
|
||||||
packet_buffer_size,
|
packet_buffer_size,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, false
|
false
|
||||||
));
|
));
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,15 +14,7 @@
|
|||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
struct UnixSocketHash
|
static BAN::HashMap<BAN::RefPtr<Inode>, BAN::WeakPtr<UnixDomainSocket>> s_bound_sockets;
|
||||||
{
|
|
||||||
BAN::hash_t operator()(const BAN::RefPtr<Inode>& socket)
|
|
||||||
{
|
|
||||||
return BAN::hash<const Inode*>{}(socket.ptr());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static BAN::HashMap<BAN::RefPtr<Inode>, BAN::WeakPtr<UnixDomainSocket>, UnixSocketHash> s_bound_sockets;
|
|
||||||
static Mutex s_bound_socket_lock;
|
static Mutex s_bound_socket_lock;
|
||||||
|
|
||||||
static constexpr size_t s_packet_buffer_size = 0x10000;
|
static constexpr size_t s_packet_buffer_size = 0x10000;
|
||||||
@@ -50,11 +42,10 @@ namespace Kernel
|
|||||||
auto socket = TRY(BAN::RefPtr<UnixDomainSocket>::create(socket_type, info));
|
auto socket = TRY(BAN::RefPtr<UnixDomainSocket>::create(socket_type, info));
|
||||||
socket->m_packet_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
socket->m_packet_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET,
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
~(uintptr_t)0,
|
|
||||||
s_packet_buffer_size,
|
s_packet_buffer_size,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, false
|
false
|
||||||
));
|
));
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#pragma GCC diagnostic ignored "-Wstack-usage="
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -3213,6 +3215,9 @@ namespace Kernel
|
|||||||
return BAN::Error::from_errno(ENOSYS);
|
return BAN::Error::from_errno(ENOSYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (op == FUTEX_WAIT && BAN::atomic_load(*addr) != val)
|
||||||
|
return BAN::Error::from_errno(EAGAIN);
|
||||||
|
|
||||||
uint64_t wake_time_ns = BAN::numeric_limits<uint64_t>::max();
|
uint64_t wake_time_ns = BAN::numeric_limits<uint64_t>::max();
|
||||||
|
|
||||||
if (user_abstime != nullptr)
|
if (user_abstime != nullptr)
|
||||||
@@ -3234,9 +3239,6 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op == FUTEX_WAIT && BAN::atomic_load(*addr) != val)
|
|
||||||
return BAN::Error::from_errno(EAGAIN);
|
|
||||||
|
|
||||||
futex_t* futex;
|
futex_t* futex;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -3312,7 +3314,6 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<long> Process::sys_set_fsbase(void* addr)
|
BAN::ErrorOr<long> Process::sys_set_fsbase(void* addr)
|
||||||
{
|
{
|
||||||
auto& thread = Thread::current();
|
auto& thread = Thread::current();
|
||||||
thread.m_has_custom_fsbase = true;
|
|
||||||
thread.set_fsbase(reinterpret_cast<vaddr_t>(addr));
|
thread.set_fsbase(reinterpret_cast<vaddr_t>(addr));
|
||||||
Processor::load_fsbase();
|
Processor::load_fsbase();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -3326,7 +3327,6 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<long> Process::sys_set_gsbase(void* addr)
|
BAN::ErrorOr<long> Process::sys_set_gsbase(void* addr)
|
||||||
{
|
{
|
||||||
auto& thread = Thread::current();
|
auto& thread = Thread::current();
|
||||||
thread.m_has_custom_gsbase = true;
|
|
||||||
thread.set_gsbase(reinterpret_cast<vaddr_t>(addr));
|
thread.set_gsbase(reinterpret_cast<vaddr_t>(addr));
|
||||||
Processor::load_gsbase();
|
Processor::load_gsbase();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -3355,9 +3355,8 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
LockGuard _(m_process_lock);
|
LockGuard _(m_process_lock);
|
||||||
|
|
||||||
// main thread cannot call pthread_exit
|
if (m_threads.size() == 1)
|
||||||
if (&Thread::current() == m_threads.front())
|
return sys_exit(0);
|
||||||
return BAN::Error::from_errno(EINVAL);
|
|
||||||
|
|
||||||
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
|
TRY(m_exited_pthreads.emplace_back(Thread::current().tid(), value));
|
||||||
|
|
||||||
@@ -3397,7 +3396,8 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
if (auto ret = check_thread(); ret.has_value())
|
if (auto ret = check_thread(); ret.has_value())
|
||||||
{
|
{
|
||||||
TRY(write_to_user(user_value, &ret.value(), sizeof(void*)));
|
if (user_value != nullptr)
|
||||||
|
TRY(write_to_user(user_value, &ret.value(), sizeof(void*)));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,6 @@ namespace Kernel
|
|||||||
ASSERT(processor.m_id == PROCESSOR_NONE);
|
ASSERT(processor.m_id == PROCESSOR_NONE);
|
||||||
processor.m_id = id;
|
processor.m_id = id;
|
||||||
|
|
||||||
processor.m_stack = kmalloc(s_stack_size, 4096, true);
|
|
||||||
ASSERT(processor.m_stack);
|
|
||||||
|
|
||||||
processor.m_gdt = GDT::create(&processor);
|
processor.m_gdt = GDT::create(&processor);
|
||||||
ASSERT(processor.m_gdt);
|
ASSERT(processor.m_gdt);
|
||||||
|
|
||||||
@@ -157,6 +154,21 @@ namespace Kernel
|
|||||||
return processor;
|
return processor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: I don't like this being a separate function but we need heap and page tables for this :)
|
||||||
|
void Processor::allocate_stack()
|
||||||
|
{
|
||||||
|
ASSERT(m_stack_paddr == 0);
|
||||||
|
ASSERT(m_stack_vaddr == 0);
|
||||||
|
|
||||||
|
m_stack_paddr = Heap::get().take_free_page();
|
||||||
|
ASSERT(m_stack_paddr);
|
||||||
|
|
||||||
|
m_stack_vaddr = PageTable::kernel().reserve_free_page(KERNEL_OFFSET);
|
||||||
|
ASSERT(m_stack_vaddr);
|
||||||
|
|
||||||
|
PageTable::kernel().map_page_at(m_stack_paddr, m_stack_vaddr, PageTable::ReadWrite | PageTable::Present);
|
||||||
|
}
|
||||||
|
|
||||||
void Processor::initialize_smp()
|
void Processor::initialize_smp()
|
||||||
{
|
{
|
||||||
const auto processor_id = current_id();
|
const auto processor_id = current_id();
|
||||||
@@ -205,8 +217,7 @@ namespace Kernel
|
|||||||
memset(reinterpret_cast<void*>(s_shared_page_vaddr), 0, PAGE_SIZE);
|
memset(reinterpret_cast<void*>(s_shared_page_vaddr), 0, PAGE_SIZE);
|
||||||
|
|
||||||
auto& shared_page = *reinterpret_cast<volatile API::SharedPage*>(s_shared_page_vaddr);
|
auto& shared_page = *reinterpret_cast<volatile API::SharedPage*>(s_shared_page_vaddr);
|
||||||
for (size_t i = 0; i <= 0xFF; i++)
|
shared_page.gdt_cpu_offset = GDT::cpu_index_offset();
|
||||||
shared_page.__sequence[i] = i;
|
|
||||||
shared_page.features = 0;
|
shared_page.features = 0;
|
||||||
|
|
||||||
ASSERT(Processor::count() + sizeof(Kernel::API::SharedPage) <= PAGE_SIZE);
|
ASSERT(Processor::count() + sizeof(Kernel::API::SharedPage) <= PAGE_SIZE);
|
||||||
@@ -565,7 +576,7 @@ namespace Kernel
|
|||||||
if (!scheduler().is_idle())
|
if (!scheduler().is_idle())
|
||||||
Thread::current().set_cpu_time_stop();
|
Thread::current().set_cpu_time_stop();
|
||||||
|
|
||||||
asm_yield_trampoline(Processor::current_stack_top());
|
asm_yield_trampoline(processor_info.stack_top_vaddr());
|
||||||
|
|
||||||
processor_info.m_start_ns = SystemTimer::get().ns_since_boot();
|
processor_info.m_start_ns = SystemTimer::get().ns_since_boot();
|
||||||
|
|
||||||
|
|||||||
@@ -307,15 +307,14 @@ namespace Kernel
|
|||||||
|
|
||||||
void Scheduler::wake_up_sleeping_threads()
|
void Scheduler::wake_up_sleeping_threads()
|
||||||
{
|
{
|
||||||
|
ASSERT(Processor::get_interrupt_state() == InterruptState::Disabled);
|
||||||
|
|
||||||
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
|
const uint64_t current_ns = SystemTimer::get().ns_since_boot();
|
||||||
while (!m_block_queue.empty() && current_ns >= m_block_queue.front()->wake_time_ns)
|
while (!m_block_queue.empty() && current_ns >= m_block_queue.front()->wake_time_ns)
|
||||||
{
|
{
|
||||||
auto* node = m_block_queue.pop_front();
|
auto* node = m_block_queue.pop_front();
|
||||||
{
|
if (auto* blocker = node->blocker.load())
|
||||||
SpinLockGuard _(node->blocker_lock);
|
blocker->remove_thread_from_block_queue(node);
|
||||||
if (node->blocker)
|
|
||||||
node->blocker->remove_blocked_thread(node);
|
|
||||||
}
|
|
||||||
node->blocked = false;
|
node->blocked = false;
|
||||||
update_most_loaded_node_queue(node, &m_run_queue);
|
update_most_loaded_node_queue(node, &m_run_queue);
|
||||||
m_run_queue.add_thread_to_back(node);
|
m_run_queue.add_thread_to_back(node);
|
||||||
@@ -368,11 +367,8 @@ namespace Kernel
|
|||||||
return;
|
return;
|
||||||
if (node != m_current)
|
if (node != m_current)
|
||||||
m_block_queue.remove_node(node);
|
m_block_queue.remove_node(node);
|
||||||
{
|
if (auto* blocker = node->blocker.load())
|
||||||
SpinLockGuard _(node->blocker_lock);
|
blocker->remove_thread_from_block_queue(node);
|
||||||
if (node->blocker)
|
|
||||||
node->blocker->remove_blocked_thread(node);
|
|
||||||
}
|
|
||||||
node->blocked = false;
|
node->blocked = false;
|
||||||
if (node != m_current)
|
if (node != m_current)
|
||||||
m_run_queue.add_thread_to_back(node);
|
m_run_queue.add_thread_to_back(node);
|
||||||
@@ -665,11 +661,8 @@ namespace Kernel
|
|||||||
m_current->blocked = true;
|
m_current->blocked = true;
|
||||||
m_current->wake_time_ns = wake_time_ns;
|
m_current->wake_time_ns = wake_time_ns;
|
||||||
|
|
||||||
{
|
if (blocker)
|
||||||
SpinLockGuard _(m_current->blocker_lock);
|
blocker->add_thread_to_block_queue(m_current);
|
||||||
if (blocker)
|
|
||||||
blocker->add_thread_to_block_queue(m_current);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_most_loaded_node_queue(m_current, &m_block_queue);
|
update_most_loaded_node_queue(m_current, &m_block_queue);
|
||||||
|
|
||||||
|
|||||||
@@ -42,10 +42,10 @@ namespace Kernel
|
|||||||
|
|
||||||
auto pts_master_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
auto pts_master_buffer = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET, static_cast<vaddr_t>(-1),
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
16 * PAGE_SIZE,
|
16 * PAGE_SIZE,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, false
|
false
|
||||||
));
|
));
|
||||||
auto pts_master = TRY(BAN::RefPtr<PseudoTerminalMaster>::create(BAN::move(pts_master_buffer), mode, uid, gid));
|
auto pts_master = TRY(BAN::RefPtr<PseudoTerminalMaster>::create(BAN::move(pts_master_buffer), mode, uid, gid));
|
||||||
DevFileSystem::get().remove_from_cache(pts_master);
|
DevFileSystem::get().remove_from_cache(pts_master);
|
||||||
|
|||||||
@@ -171,11 +171,10 @@ namespace Kernel
|
|||||||
// Initialize stack and registers
|
// Initialize stack and registers
|
||||||
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
PageTable::kernel(),
|
PageTable::kernel(),
|
||||||
KERNEL_OFFSET,
|
{ KERNEL_OFFSET, UINTPTR_MAX },
|
||||||
~(uintptr_t)0,
|
|
||||||
kernel_stack_size,
|
kernel_stack_size,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, true
|
true
|
||||||
));
|
));
|
||||||
|
|
||||||
// Initialize stack for returning
|
// Initialize stack for returning
|
||||||
@@ -208,10 +207,10 @@ namespace Kernel
|
|||||||
|
|
||||||
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
page_table,
|
page_table,
|
||||||
s_user_stack_addr_start, USERSPACE_END,
|
{ s_user_stack_addr_start, USERSPACE_END },
|
||||||
kernel_stack_size,
|
kernel_stack_size,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, true
|
true
|
||||||
));
|
));
|
||||||
|
|
||||||
auto userspace_stack = TRY(MemoryBackedRegion::create(
|
auto userspace_stack = TRY(MemoryBackedRegion::create(
|
||||||
@@ -304,22 +303,7 @@ namespace Kernel
|
|||||||
{
|
{
|
||||||
if (!is_userspace() || !has_process())
|
if (!is_userspace() || !has_process())
|
||||||
return;
|
return;
|
||||||
|
Processor::gdt().set_cpu_index(Processor::current_index());
|
||||||
#if ARCH(x86_64)
|
|
||||||
if (m_has_custom_gsbase)
|
|
||||||
return;
|
|
||||||
#elif ARCH(i686)
|
|
||||||
if (m_has_custom_fsbase)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const vaddr_t vaddr = process().shared_page_vaddr() + Processor::current_index();
|
|
||||||
|
|
||||||
#if ARCH(x86_64)
|
|
||||||
set_gsbase(vaddr);
|
|
||||||
#elif ARCH(i686)
|
|
||||||
set_fsbase(vaddr);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<Thread*> Thread::pthread_create(entry_t entry, void* arg)
|
BAN::ErrorOr<Thread*> Thread::pthread_create(entry_t entry, void* arg)
|
||||||
@@ -356,10 +340,10 @@ namespace Kernel
|
|||||||
|
|
||||||
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
thread->m_kernel_stack = TRY(VirtualRange::create_to_vaddr_range(
|
||||||
new_process->page_table(),
|
new_process->page_table(),
|
||||||
s_user_stack_addr_start, USERSPACE_END,
|
{ s_user_stack_addr_start, USERSPACE_END },
|
||||||
kernel_stack_size,
|
kernel_stack_size,
|
||||||
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
PageTable::Flags::ReadWrite | PageTable::Flags::Present,
|
||||||
true, true
|
true
|
||||||
));
|
));
|
||||||
|
|
||||||
// NOTE: copy [sp, stack_end] so fork return works
|
// NOTE: copy [sp, stack_end] so fork return works
|
||||||
|
|||||||
@@ -23,60 +23,64 @@ namespace Kernel
|
|||||||
|
|
||||||
void ThreadBlocker::unblock()
|
void ThreadBlocker::unblock()
|
||||||
{
|
{
|
||||||
decltype(m_block_chain) temp_block_chain;
|
SpinLockGuard _(m_lock);
|
||||||
size_t temp_block_chain_length { 0 };
|
|
||||||
|
|
||||||
|
for (auto* node = m_block_chain; node;)
|
||||||
{
|
{
|
||||||
SpinLockGuard _(m_lock);
|
auto* next = node->block_chain_next;
|
||||||
for (size_t i = 0; i < m_block_chain_length; i++)
|
|
||||||
temp_block_chain[i] = m_block_chain[i];
|
ASSERT(node->blocked);
|
||||||
temp_block_chain_length = m_block_chain_length;
|
ASSERT(node->blocker == this);
|
||||||
m_block_chain_length = 0;
|
|
||||||
|
node->blocker.store(nullptr);
|
||||||
|
node->block_chain_prev = nullptr;
|
||||||
|
node->block_chain_next = nullptr;
|
||||||
|
|
||||||
|
Processor::scheduler().unblock_thread(node);
|
||||||
|
|
||||||
|
node = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < temp_block_chain_length; i++)
|
m_block_chain = nullptr;
|
||||||
Processor::scheduler().unblock_thread(temp_block_chain[i]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadBlocker::add_thread_to_block_queue(SchedulerQueue::Node* node)
|
void ThreadBlocker::add_thread_to_block_queue(SchedulerQueue::Node* node)
|
||||||
{
|
{
|
||||||
ASSERT(node->blocker_lock.current_processor_has_lock());
|
|
||||||
|
|
||||||
SpinLockGuard _(m_lock);
|
SpinLockGuard _(m_lock);
|
||||||
|
|
||||||
ASSERT(m_block_chain_length < sizeof(m_block_chain) / sizeof(m_block_chain[0]));
|
|
||||||
|
|
||||||
ASSERT(node);
|
|
||||||
ASSERT(node->blocked);
|
ASSERT(node->blocked);
|
||||||
ASSERT(node->blocker == nullptr);
|
ASSERT(node->blocker == nullptr);
|
||||||
|
|
||||||
for (size_t i = 0 ; i < m_block_chain_length; i++)
|
node->blocker.store(this);
|
||||||
ASSERT(m_block_chain[i] != node);
|
node->block_chain_prev = nullptr;
|
||||||
m_block_chain[m_block_chain_length++] = node;
|
node->block_chain_next = m_block_chain;
|
||||||
|
|
||||||
node->blocker = this;
|
if (m_block_chain)
|
||||||
|
m_block_chain->block_chain_prev = node;
|
||||||
|
m_block_chain = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadBlocker::remove_blocked_thread(SchedulerQueue::Node* node)
|
void ThreadBlocker::remove_thread_from_block_queue(SchedulerQueue::Node* node)
|
||||||
{
|
{
|
||||||
ASSERT(node->blocker_lock.current_processor_has_lock());
|
|
||||||
|
|
||||||
SpinLockGuard _(m_lock);
|
SpinLockGuard _(m_lock);
|
||||||
|
|
||||||
ASSERT(node);
|
// NOTE: this is possible if we got here while another
|
||||||
|
// core was doing an unblock on this blocker
|
||||||
|
if (node->blocker.load() != this)
|
||||||
|
return;
|
||||||
ASSERT(node->blocked);
|
ASSERT(node->blocked);
|
||||||
ASSERT(node->blocker == this);
|
|
||||||
|
|
||||||
for (size_t i = 0 ; i < m_block_chain_length; i++)
|
if (node->block_chain_prev)
|
||||||
{
|
node->block_chain_prev->block_chain_next = node->block_chain_next;
|
||||||
if (m_block_chain[i] != node)
|
if (node->block_chain_next)
|
||||||
continue;
|
node->block_chain_next->block_chain_prev = node->block_chain_prev;
|
||||||
for (size_t j = i + 1; j < m_block_chain_length; j++)
|
|
||||||
m_block_chain[j - 1] = m_block_chain[j];
|
|
||||||
m_block_chain_length--;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->blocker = nullptr;
|
if (node == m_block_chain)
|
||||||
|
m_block_chain = node->block_chain_next;
|
||||||
|
|
||||||
|
node->blocker.store(nullptr);
|
||||||
|
node->block_chain_prev = nullptr;
|
||||||
|
node->block_chain_next = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,19 +126,20 @@ extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info)
|
|||||||
parse_boot_info(boot_magic, boot_info);
|
parse_boot_info(boot_magic, boot_info);
|
||||||
dprintln("boot info parsed");
|
dprintln("boot info parsed");
|
||||||
|
|
||||||
Processor::create(PROCESSOR_NONE);
|
auto& processor = Processor::create(PROCESSOR_NONE);
|
||||||
Processor::initialize();
|
Processor::initialize();
|
||||||
dprintln("BSP initialized");
|
dprintln("BSP initialized");
|
||||||
|
|
||||||
PageTable::initialize_pre_heap();
|
PageTable::initialize_fast_page();
|
||||||
PageTable::kernel().initial_load();
|
dprintln("fast page initialized");
|
||||||
dprintln("PageTable stage1 initialized");
|
|
||||||
|
|
||||||
Heap::initialize();
|
Heap::initialize();
|
||||||
dprintln("Heap initialzed");
|
dprintln("Heap initialized");
|
||||||
|
|
||||||
PageTable::initialize_post_heap();
|
PageTable::initialize_and_load();
|
||||||
dprintln("PageTable stage2 initialized");
|
dprintln("PageTable initialized");
|
||||||
|
|
||||||
|
processor.allocate_stack();
|
||||||
|
|
||||||
parse_command_line();
|
parse_command_line();
|
||||||
dprintln("command line parsed, root='{}', console='{}'", cmdline.root, cmdline.console);
|
dprintln("command line parsed, root='{}', console='{}'", cmdline.root, cmdline.console);
|
||||||
@@ -270,7 +271,7 @@ extern "C" void ap_main()
|
|||||||
using namespace Kernel;
|
using namespace Kernel;
|
||||||
|
|
||||||
Processor::initialize();
|
Processor::initialize();
|
||||||
PageTable::kernel().initial_load();
|
|
||||||
InterruptController::get().enable();
|
InterruptController::get().enable();
|
||||||
|
|
||||||
Processor::wait_until_processors_ready();
|
Processor::wait_until_processors_ready();
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#!/bin/bash ../install.sh
|
#!/bin/bash ../install.sh
|
||||||
|
|
||||||
NAME='ca-certificates'
|
NAME='ca-certificates'
|
||||||
VERSION='2025-12-02'
|
VERSION='2026.03.19'
|
||||||
DOWNLOAD_URL="https://curl.se/ca/cacert-$VERSION.pem#f1407d974c5ed87d544bd931a278232e13925177e239fca370619aba63c757b4"
|
DOWNLOAD_URL="https://curl.se/ca/cacert-${VERSION//./-}.pem#b6e66569cc3d438dd5abe514d0df50005d570bfc96c14dca8f768d020cb96171"
|
||||||
|
|
||||||
configure() {
|
configure() {
|
||||||
:
|
:
|
||||||
@@ -13,7 +13,10 @@ build() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
install() {
|
install() {
|
||||||
mkdir -p "$BANAN_SYSROOT/etc/ssl/certs"
|
rm -rf "$BANAN_SYSROOT/etc/cacert/extracted"
|
||||||
cp -v "../cacert-$VERSION.pem" "$BANAN_SYSROOT/etc/ssl/certs/ca-certificates.crt"
|
mkdir -p "$BANAN_SYSROOT/etc/cacert/extracted"
|
||||||
ln -svf "certs/ca-certificates.crt" "$BANAN_SYSROOT/etc/ssl/cert.pem"
|
|
||||||
|
cp -vf "../cacert-${VERSION//./-}.pem" "$BANAN_SYSROOT/etc/cacert/cacert.pem"
|
||||||
|
awk '/-----BEGIN CERTIFICATE-----/ {c=1;n++} c {print > sprintf("cert%03d.pem", n)} /-----END CERTIFICATE-----/ {c=0}' "../cacert-${VERSION//./-}.pem"
|
||||||
|
mv cert*.pem "$BANAN_SYSROOT/etc/cacert/extracted/"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
NAME='curl'
|
NAME='curl'
|
||||||
VERSION='8.17.0'
|
VERSION='8.17.0'
|
||||||
DOWNLOAD_URL="https://curl.se/download/curl-$VERSION.tar.xz#955f6e729ad6b3566260e8fef68620e76ba3c31acf0a18524416a185acf77992"
|
DOWNLOAD_URL="https://curl.se/download/curl-$VERSION.tar.xz#955f6e729ad6b3566260e8fef68620e76ba3c31acf0a18524416a185acf77992"
|
||||||
DEPENDENCIES=('ca-certificates' 'openssl' 'zlib' 'zstd')
|
DEPENDENCIES=('openssl' 'zlib' 'zstd')
|
||||||
CONFIG_SUB=('config.sub')
|
CONFIG_SUB=('config.sub')
|
||||||
CONFIGURE_OPTIONS=(
|
CONFIGURE_OPTIONS=(
|
||||||
'--disable-threaded-resolver'
|
'--disable-threaded-resolver'
|
||||||
@@ -16,6 +16,6 @@ CONFIGURE_OPTIONS=(
|
|||||||
'--with-zlib'
|
'--with-zlib'
|
||||||
'--with-zstd'
|
'--with-zstd'
|
||||||
'--without-libpsl'
|
'--without-libpsl'
|
||||||
'--with-ca-bundle=/etc/ssl/certs/ca-certificates.crt'
|
'--with-ca-path=/etc/ssl/certs'
|
||||||
'--without-ca-path'
|
'--with-ca-bundle=/etc/ssl/certs/ca-bundle.crt'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,9 +3,24 @@
|
|||||||
NAME='openssl'
|
NAME='openssl'
|
||||||
VERSION='3.6.0'
|
VERSION='3.6.0'
|
||||||
DOWNLOAD_URL="https://github.com/openssl/openssl/releases/download/openssl-$VERSION/openssl-$VERSION.tar.gz#b6a5f44b7eb69e3fa35dbf15524405b44837a481d43d81daddde3ff21fcbb8e9"
|
DOWNLOAD_URL="https://github.com/openssl/openssl/releases/download/openssl-$VERSION/openssl-$VERSION.tar.gz#b6a5f44b7eb69e3fa35dbf15524405b44837a481d43d81daddde3ff21fcbb8e9"
|
||||||
DEPENDENCIES=('zlib')
|
DEPENDENCIES=('ca-certificates' 'zlib')
|
||||||
MAKE_INSTALL_TARGETS=('install_sw' 'install_ssldirs')
|
MAKE_INSTALL_TARGETS=('install_sw' 'install_ssldirs')
|
||||||
|
|
||||||
configure() {
|
configure() {
|
||||||
./Configure --prefix=/usr --openssldir=/etc/ssl -DOPENSSL_USE_IPV6=0 no-asm no-tests banan_os-generic threads zlib
|
./Configure --prefix=/usr --openssldir=/etc/ssl -DOPENSSL_USE_IPV6=0 no-asm no-tests banan_os-generic threads zlib
|
||||||
}
|
}
|
||||||
|
|
||||||
|
post_install() {
|
||||||
|
rm -f "$BANAN_SYSROOT/etc/ssl/certs"/*
|
||||||
|
|
||||||
|
ln -svf "../cacert/cacert.pem" "$BANAN_SYSROOT/etc/ssl/cert.pem"
|
||||||
|
ln -svf "../../cacert/cacert.pem" "$BANAN_SYSROOT/etc/ssl/certs/ca-certificates.crt"
|
||||||
|
ln -svf "../../cacert/cacert.pem" "$BANAN_SYSROOT/etc/ssl/certs/ca-bundle.crt"
|
||||||
|
|
||||||
|
openssl rehash "$BANAN_SYSROOT/etc/cacert/extracted"
|
||||||
|
find "$BANAN_SYSROOT/etc/cacert/extracted" -type l -print0 |
|
||||||
|
while IFS= read -r -d '' link; do
|
||||||
|
ln -s "../../cacert/extracted/$(readlink "$link")" "$BANAN_SYSROOT/etc/ssl/certs/${link##*/}"
|
||||||
|
rm "$link"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|||||||
24
ports/xz/build.sh
Executable file
24
ports/xz/build.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash ../install.sh
|
||||||
|
|
||||||
|
NAME='xz'
|
||||||
|
VERSION='5.8.2'
|
||||||
|
DOWNLOAD_URL="https://github.com/tukaani-project/xz/releases/download/v5.8.2/xz-$VERSION.tar.xz#890966ec3f5d5cc151077879e157c0593500a522f413ac50ba26d22a9a145214"
|
||||||
|
|
||||||
|
configure() {
|
||||||
|
cmake --fresh -B build -S . -G Ninja \
|
||||||
|
--toolchain="$BANAN_TOOLCHAIN_DIR/Toolchain.txt" \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_SHARED_LIBS=ON \
|
||||||
|
-DXZ_NLS=OFF \
|
||||||
|
-DXZ_DOC=OFF \
|
||||||
|
|| exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cmake --build build ||exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
install() {
|
||||||
|
cmake --install build ||exit 1
|
||||||
|
}
|
||||||
@@ -3,14 +3,11 @@ set(USERSPACE_LIBRARIES
|
|||||||
LibC
|
LibC
|
||||||
LibClipboard
|
LibClipboard
|
||||||
LibDEFLATE
|
LibDEFLATE
|
||||||
LibDL
|
|
||||||
LibELF
|
LibELF
|
||||||
LibFont
|
LibFont
|
||||||
LibGUI
|
LibGUI
|
||||||
LibImage
|
LibImage
|
||||||
LibInput
|
LibInput
|
||||||
LibMath
|
|
||||||
LibPthread
|
|
||||||
LibQR
|
LibQR
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ set(LIBC_SOURCES
|
|||||||
add_library(objlibc OBJECT ${LIBC_SOURCES})
|
add_library(objlibc OBJECT ${LIBC_SOURCES})
|
||||||
target_compile_definitions(objlibc PRIVATE __arch=${BANAN_ARCH} __is_libc)
|
target_compile_definitions(objlibc PRIVATE __arch=${BANAN_ARCH} __is_libc)
|
||||||
|
|
||||||
target_compile_options(objlibc PRIVATE -O2 -g -Wstack-usage=512 -fno-exceptions -fno-rtti -fpic)
|
target_compile_options(objlibc PRIVATE -O2 -g -Wstack-usage=8192 -fno-exceptions -fno-rtti -fpic)
|
||||||
target_compile_options(objlibc PUBLIC -Wall -Wextra -Werror -Wno-error=stack-usage=)
|
target_compile_options(objlibc PUBLIC -Wall -Wextra -Werror -Wno-error=stack-usage=)
|
||||||
|
|
||||||
function(add_crtx crtx)
|
function(add_crtx crtx)
|
||||||
@@ -105,6 +105,13 @@ target_link_options(libc-shared PRIVATE -nolibc -nostdlib++)
|
|||||||
install(TARGETS libc-static OPTIONAL)
|
install(TARGETS libc-static OPTIONAL)
|
||||||
install(TARGETS libc-shared OPTIONAL)
|
install(TARGETS libc-shared OPTIONAL)
|
||||||
|
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.a\" \"${CMAKE_INSTALL_LIBDIR}/libdl.a\")")
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.so\" \"${CMAKE_INSTALL_LIBDIR}/libdl.so\")")
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.a\" \"${CMAKE_INSTALL_LIBDIR}/libm.a\")")
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.so\" \"${CMAKE_INSTALL_LIBDIR}/libm.so\")")
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.a\" \"${CMAKE_INSTALL_LIBDIR}/libpthread.a\")")
|
||||||
|
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink \"libc.so\" \"${CMAKE_INSTALL_LIBDIR}/libpthread.so\")")
|
||||||
|
|
||||||
set_target_properties(libc-static PROPERTIES OUTPUT_NAME libc)
|
set_target_properties(libc-static PROPERTIES OUTPUT_NAME libc)
|
||||||
set_target_properties(libc-shared PROPERTIES OUTPUT_NAME libc)
|
set_target_properties(libc-shared PROPERTIES OUTPUT_NAME libc)
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ __BEGIN_DECLS
|
|||||||
#define MAX_CANON _POSIX_MAX_CANON
|
#define MAX_CANON _POSIX_MAX_CANON
|
||||||
#define MAX_INPUT _POSIX_MAX_INPUT
|
#define MAX_INPUT _POSIX_MAX_INPUT
|
||||||
#define NAME_MAX 255
|
#define NAME_MAX 255
|
||||||
#define PATH_MAX _POSIX_PATH_MAX
|
#define PATH_MAX 4096
|
||||||
#define PIPE_BUF PAGE_SIZE
|
#define PIPE_BUF PAGE_SIZE
|
||||||
//#define POSIX_ALLOC_SIZE_MIN
|
//#define POSIX_ALLOC_SIZE_MIN
|
||||||
//#define POSIX_REC_INCR_XFER_SIZE
|
//#define POSIX_REC_INCR_XFER_SIZE
|
||||||
|
|||||||
@@ -169,6 +169,12 @@ void pthread_testcancel(void);
|
|||||||
void pthread_cleanup_pop(int execute);
|
void pthread_cleanup_pop(int execute);
|
||||||
void pthread_cleanup_push(void (*routine)(void*), void* arg);
|
void pthread_cleanup_push(void (*routine)(void*), void* arg);
|
||||||
|
|
||||||
|
#define _pthread_equal(t1, t2) ((t1) == (t2))
|
||||||
|
#define pthread_equal(t1, t2) _pthread_equal(t1, t2)
|
||||||
|
|
||||||
|
#define _pthread_self() (_get_uthread()->id)
|
||||||
|
#define pthread_self() _pthread_self()
|
||||||
|
|
||||||
#define _pthread_testcancel() do { \
|
#define _pthread_testcancel() do { \
|
||||||
struct uthread* uthread = _get_uthread(); \
|
struct uthread* uthread = _get_uthread(); \
|
||||||
if (__builtin_expect(uthread->cancel_state == PTHREAD_CANCEL_ENABLE, 1)) \
|
if (__builtin_expect(uthread->cancel_state == PTHREAD_CANCEL_ENABLE, 1)) \
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ int clearenv(void);
|
|||||||
div_t div(int numer, int denom);
|
div_t div(int numer, int denom);
|
||||||
double drand48(void);
|
double drand48(void);
|
||||||
double erand48(unsigned short xsubi[3]);
|
double erand48(unsigned short xsubi[3]);
|
||||||
void exit(int status);
|
void exit(int status) __attribute__((__noreturn__));
|
||||||
void free(void* ptr);
|
void free(void* ptr);
|
||||||
char* getenv(const char* name);
|
char* getenv(const char* name);
|
||||||
int getsubopt(char** optionp, char* const* keylistp, char** valuep);
|
int getsubopt(char** optionp, char* const* keylistp, char** valuep);
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ char* strsep(char** __restrict stringp, const char* __restrict delim);
|
|||||||
char* strsignal(int signum);
|
char* strsignal(int signum);
|
||||||
size_t strspn(const char* s1, const char* s2);
|
size_t strspn(const char* s1, const char* s2);
|
||||||
char* strstr(const char* s1, const char* s2);
|
char* strstr(const char* s1, const char* s2);
|
||||||
|
char* strcasestr(const char* haystack, const char* needle);
|
||||||
char* strtok(char* __restrict s, const char* __restrict sep);
|
char* strtok(char* __restrict s, const char* __restrict sep);
|
||||||
char* strtok_r(char* __restrict s, const char* __restrict sep, char** __restrict state);
|
char* strtok_r(char* __restrict s, const char* __restrict sep, char** __restrict state);
|
||||||
size_t strxfrm(char* __restrict s1, const char* __restrict s2, size_t n);
|
size_t strxfrm(char* __restrict s1, const char* __restrict s2, size_t n);
|
||||||
|
|||||||
@@ -274,6 +274,8 @@ static void floating_point_to_maybe_exponent_string(char* buffer, T value, bool
|
|||||||
|
|
||||||
extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun)(int, void*), void* data)
|
extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun)(int, void*), void* data)
|
||||||
{
|
{
|
||||||
|
char conversion[4096];
|
||||||
|
|
||||||
int written = 0;
|
int written = 0;
|
||||||
while (*format)
|
while (*format)
|
||||||
{
|
{
|
||||||
@@ -383,9 +385,6 @@ extern "C" int printf_impl(const char* format, va_list arguments, int (*putc_fun
|
|||||||
format--;
|
format--;
|
||||||
format++;
|
format++;
|
||||||
|
|
||||||
// FIXME: this should be thread-local to keep
|
|
||||||
// satisfy multithreaded requirement
|
|
||||||
static char conversion[4096];
|
|
||||||
const char* string = nullptr;
|
const char* string = nullptr;
|
||||||
|
|
||||||
int length = -1;
|
int length = -1;
|
||||||
|
|||||||
@@ -489,29 +489,29 @@ void pthread_exit(void* value_ptr)
|
|||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef pthread_equal
|
||||||
int pthread_equal(pthread_t t1, pthread_t t2)
|
int pthread_equal(pthread_t t1, pthread_t t2)
|
||||||
{
|
{
|
||||||
return t1 == t2;
|
return _pthread_equal(t1, t2);
|
||||||
}
|
}
|
||||||
|
#define pthread_equal(t1, t2) _pthread_equal(t1, t2)
|
||||||
|
|
||||||
|
#undef pthread_self
|
||||||
|
pthread_t pthread_self(void)
|
||||||
|
{
|
||||||
|
return _pthread_self();
|
||||||
|
}
|
||||||
|
#define pthread_self() _pthread_self()
|
||||||
|
|
||||||
int pthread_join(pthread_t thread, void** value_ptr)
|
int pthread_join(pthread_t thread, void** value_ptr)
|
||||||
{
|
{
|
||||||
pthread_testcancel();
|
do {
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
while (syscall(SYS_PTHREAD_JOIN, thread, value_ptr) == -1 && errno == EINTR)
|
|
||||||
{
|
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
errno = 0;
|
errno = 0;
|
||||||
}
|
} while (syscall(SYS_PTHREAD_JOIN, thread, value_ptr) == -1 && errno == EINTR);
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t pthread_self(void)
|
|
||||||
{
|
|
||||||
return _get_uthread()->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
|
int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
|
||||||
{
|
{
|
||||||
static_assert(PTHREAD_ONCE_INIT == 0);
|
static_assert(PTHREAD_ONCE_INIT == 0);
|
||||||
@@ -671,6 +671,7 @@ void pthread_testcancel(void)
|
|||||||
{
|
{
|
||||||
_pthread_testcancel();
|
_pthread_testcancel();
|
||||||
}
|
}
|
||||||
|
#define pthread_testcancel() _pthread_testcancel()
|
||||||
|
|
||||||
int pthread_getschedparam(pthread_t thread, int* __restrict policy, struct sched_param* __restrict param)
|
int pthread_getschedparam(pthread_t thread, int* __restrict policy, struct sched_param* __restrict param)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,12 +27,7 @@ int sched_getcpu(void)
|
|||||||
{
|
{
|
||||||
if (g_shared_page == nullptr)
|
if (g_shared_page == nullptr)
|
||||||
return -1;
|
return -1;
|
||||||
|
uint16_t limit;
|
||||||
uint8_t cpu;
|
asm volatile("lsl %1, %0" : "=r"(limit) : "r"(g_shared_page->gdt_cpu_offset));
|
||||||
#if defined(__x86_64__)
|
return limit;
|
||||||
asm volatile("movb %%gs:0, %0" : "=r"(cpu));
|
|
||||||
#elif defined(__i686__)
|
|
||||||
asm volatile("movb %%fs:0, %0" : "=q"(cpu));
|
|
||||||
#endif
|
|
||||||
return cpu;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ void exit(int status)
|
|||||||
__cxa_finalize(nullptr);
|
__cxa_finalize(nullptr);
|
||||||
fflush(nullptr);
|
fflush(nullptr);
|
||||||
_exit(status);
|
_exit(status);
|
||||||
ASSERT_NOT_REACHED();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _Exit(int status)
|
void _Exit(int status)
|
||||||
@@ -828,35 +827,40 @@ void qsort(void* base, size_t nel, size_t width, int (*compar)(const void*, cons
|
|||||||
|
|
||||||
// Constants and algorithm from https://en.wikipedia.org/wiki/Permuted_congruential_generator
|
// Constants and algorithm from https://en.wikipedia.org/wiki/Permuted_congruential_generator
|
||||||
|
|
||||||
static uint64_t s_rand_state = 0x4d595df4d0f33173;
|
|
||||||
static constexpr uint64_t s_rand_multiplier = 6364136223846793005;
|
static constexpr uint64_t s_rand_multiplier = 6364136223846793005;
|
||||||
static constexpr uint64_t s_rand_increment = 1442695040888963407;
|
static constexpr uint64_t s_rand_increment = 1442695040888963407;
|
||||||
|
static uint64_t s_rand_state;
|
||||||
|
|
||||||
static constexpr uint32_t rotr32(uint32_t x, unsigned r)
|
static constexpr uint32_t rotr32(uint32_t x, unsigned r)
|
||||||
{
|
{
|
||||||
return x >> r | x << (-r & 31);
|
return x >> r | x << (-r & 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rand_r(unsigned* seed)
|
template<BAN::integral T>
|
||||||
|
static inline int rand_impl(T& state)
|
||||||
{
|
{
|
||||||
uint64_t x = *seed | (static_cast<uint64_t>(*seed) << 32);
|
uint64_t x;
|
||||||
unsigned count = (unsigned)(x >> 59);
|
if constexpr (sizeof(T) == 8) x = state;
|
||||||
|
if constexpr (sizeof(T) == 4) x = state * 0x0000000100000001;
|
||||||
|
if constexpr (sizeof(T) == 2) x = state * 0x0001000100010001;
|
||||||
|
if constexpr (sizeof(T) == 1) x = state * 0x0101010101010101;
|
||||||
|
|
||||||
*seed = x * s_rand_multiplier + s_rand_increment;
|
const unsigned count = x >> 59;
|
||||||
|
|
||||||
|
state = x * s_rand_multiplier + s_rand_increment;
|
||||||
x ^= x >> 18;
|
x ^= x >> 18;
|
||||||
|
|
||||||
return rotr32(x >> 27, count) % RAND_MAX;
|
return rotr32(x >> 27, count) % RAND_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int rand_r(unsigned* seed)
|
||||||
|
{
|
||||||
|
return rand_impl(*seed);
|
||||||
|
}
|
||||||
|
|
||||||
int rand(void)
|
int rand(void)
|
||||||
{
|
{
|
||||||
uint64_t x = s_rand_state;
|
return rand_impl(s_rand_state);
|
||||||
unsigned count = (unsigned)(x >> 59);
|
|
||||||
|
|
||||||
s_rand_state = x * s_rand_multiplier + s_rand_increment;
|
|
||||||
x ^= x >> 18;
|
|
||||||
|
|
||||||
return rotr32(x >> 27, count) % RAND_MAX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void srand(unsigned int seed)
|
void srand(unsigned int seed)
|
||||||
@@ -953,4 +957,5 @@ void init_default_random()
|
|||||||
{
|
{
|
||||||
static char buffer[128];
|
static char buffer[128];
|
||||||
initstate(1, buffer, 128);
|
initstate(1, buffer, 128);
|
||||||
|
srand(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,6 +304,17 @@ char* strstr(const char* haystack, const char* needle)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* strcasestr(const char* haystack, const char* needle)
|
||||||
|
{
|
||||||
|
const size_t needle_len = strlen(needle);
|
||||||
|
if (needle_len == 0)
|
||||||
|
return const_cast<char*>(haystack);
|
||||||
|
for (size_t i = 0; haystack[i]; i++)
|
||||||
|
if (strncasecmp(haystack + i, needle, needle_len) == 0)
|
||||||
|
return const_cast<char*>(haystack + i);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#define CHAR_UCHAR(ch) \
|
#define CHAR_UCHAR(ch) \
|
||||||
static_cast<unsigned char>(ch)
|
static_cast<unsigned char>(ch)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <langinfo.h>
|
#include <langinfo.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -29,13 +30,9 @@ int clock_gettime(clockid_t clock_id, struct timespec* tp)
|
|||||||
|
|
||||||
const auto get_cpu =
|
const auto get_cpu =
|
||||||
[]() -> uint8_t {
|
[]() -> uint8_t {
|
||||||
uint8_t cpu;
|
uint16_t limit;
|
||||||
#if defined(__x86_64__)
|
asm volatile("lsl %1, %0" : "=r"(limit) : "r"(g_shared_page->gdt_cpu_offset));
|
||||||
asm volatile("movb %%gs:0, %0" : "=r"(cpu));
|
return limit;
|
||||||
#elif defined(__i686__)
|
|
||||||
asm volatile("movb %%fs:0, %0" : "=q"(cpu));
|
|
||||||
#endif
|
|
||||||
return cpu;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
@@ -626,3 +623,91 @@ size_t strftime(char* __restrict s, size_t maxsize, const char* __restrict forma
|
|||||||
s[len++] = '\0';
|
s[len++] = '\0';
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_tm_name(const char* buf, const char** out, int ab, int full, size_t count) {
|
||||||
|
int tm_name = -1;
|
||||||
|
for(size_t i = 0; i < count; ++i) {
|
||||||
|
const char *smol = nl_langinfo(ab + i),
|
||||||
|
*big = nl_langinfo(full + i);
|
||||||
|
|
||||||
|
size_t smol_len = strlen(smol),
|
||||||
|
big_len = strlen(big);
|
||||||
|
if(strncasecmp(buf, big, big_len) == 0) {
|
||||||
|
buf += big_len;
|
||||||
|
tm_name = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(strncasecmp(buf, smol, smol_len) == 0) {
|
||||||
|
buf += smol_len;
|
||||||
|
tm_name = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*out = buf;
|
||||||
|
return tm_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* strptime(const char* buf, const char* format, struct tm* tm) {
|
||||||
|
for(;;) {
|
||||||
|
while(*format && *format != '%') {
|
||||||
|
if(isspace(*format)) {
|
||||||
|
while(isspace(*buf)) buf++;
|
||||||
|
format++;
|
||||||
|
} else if(*buf++ != *format++) return NULL;
|
||||||
|
}
|
||||||
|
if(*format == '\0') break;
|
||||||
|
format++;
|
||||||
|
char fmt_chr = *format++;
|
||||||
|
switch(fmt_chr) {
|
||||||
|
case '%': if(*buf != '%') return NULL; break;
|
||||||
|
case 'a': case 'A': {
|
||||||
|
int day = parse_tm_name(buf, &buf, ABDAY_1, DAY_1, 7);
|
||||||
|
if(day == -1) return NULL;
|
||||||
|
tm->tm_wday = day;
|
||||||
|
} break;
|
||||||
|
case 'b': case 'B': case 'h': {
|
||||||
|
int mon = parse_tm_name(buf, &buf, ABMON_1, MON_1, 12);
|
||||||
|
if(mon == -1) return NULL;
|
||||||
|
tm->tm_mon = mon;
|
||||||
|
} break;
|
||||||
|
case 'd': case 'e': {
|
||||||
|
errno = 0;
|
||||||
|
long day = strtol(buf, (char**)&buf, 10);
|
||||||
|
if(errno) return NULL;
|
||||||
|
if(day < 1 || day > 31) return NULL;
|
||||||
|
tm->tm_mday = day;
|
||||||
|
} break;
|
||||||
|
case 'Y': {
|
||||||
|
errno = 0;
|
||||||
|
long year = strtol(buf, (char**)&buf, 10);
|
||||||
|
if(errno) return NULL;
|
||||||
|
tm->tm_year = year - 1900;
|
||||||
|
} break;
|
||||||
|
case 'H': {
|
||||||
|
errno = 0;
|
||||||
|
long hour = strtol(buf, (char**)&buf, 10);
|
||||||
|
if(errno) return NULL;
|
||||||
|
if(hour < 0 || hour > 23) return NULL;
|
||||||
|
tm->tm_hour = hour;
|
||||||
|
} break;
|
||||||
|
case 'M': {
|
||||||
|
errno = 0;
|
||||||
|
long minute = strtol(buf, (char**)&buf, 10);
|
||||||
|
if(errno) return NULL;
|
||||||
|
if(minute < 0 || minute > 59) return NULL;
|
||||||
|
tm->tm_min = minute;
|
||||||
|
} break;
|
||||||
|
case 'S': {
|
||||||
|
errno = 0;
|
||||||
|
long sec = strtol(buf, (char**)&buf, 10);
|
||||||
|
if(errno) return NULL;
|
||||||
|
if(sec < 0 || sec > 60) return NULL;
|
||||||
|
tm->tm_sec = sec;
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
derrorln("TODO: strptime fmt {}", fmt_chr);
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (char*)buf;
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,10 +24,17 @@ int wcwidth(wchar_t wc)
|
|||||||
return wc != '\0';
|
return wc != '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int mbsinit(const mbstate_t* ps)
|
||||||
|
{
|
||||||
|
(void)ps;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
wchar_t* wcstok(wchar_t* __restrict, const wchar_t* __restrict, wchar_t** __restrict) { ASSERT_NOT_REACHED(); }
|
wchar_t* wcstok(wchar_t* __restrict, const wchar_t* __restrict, wchar_t** __restrict) { ASSERT_NOT_REACHED(); }
|
||||||
long wcstol(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
|
long wcstol(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
|
||||||
unsigned long wcstoul(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
|
unsigned long wcstoul(const wchar_t* __restrict, wchar_t** __restrict, int) { ASSERT_NOT_REACHED(); }
|
||||||
int swprintf(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...) { ASSERT_NOT_REACHED(); }
|
int swprintf(wchar_t* __restrict, size_t, const wchar_t* __restrict, ...) { ASSERT_NOT_REACHED(); }
|
||||||
|
size_t wcsrtombs(char* __restrict, const wchar_t** __restrict, size_t, mbstate_t* __restrict) { ASSERT_NOT_REACHED(); }
|
||||||
|
|
||||||
size_t wcrtomb(char* __restrict s, wchar_t ws, mbstate_t* __restrict ps)
|
size_t wcrtomb(char* __restrict s, wchar_t ws, mbstate_t* __restrict ps)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
set(SOURCES
|
|
||||||
dummy.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(libdl-static STATIC ${SOURCES})
|
|
||||||
add_library(libdl-shared SHARED ${SOURCES})
|
|
||||||
|
|
||||||
target_link_options(libdl-static PRIVATE -nolibc)
|
|
||||||
target_link_options(libdl-shared PRIVATE -nolibc)
|
|
||||||
|
|
||||||
banan_link_library(libdl-static libc)
|
|
||||||
banan_link_library(libdl-shared libc)
|
|
||||||
|
|
||||||
set_target_properties(libdl-static PROPERTIES OUTPUT_NAME libdl)
|
|
||||||
set_target_properties(libdl-shared PROPERTIES OUTPUT_NAME libdl)
|
|
||||||
|
|
||||||
install(TARGETS libdl-static OPTIONAL)
|
|
||||||
install(TARGETS libdl-shared OPTIONAL)
|
|
||||||
@@ -19,20 +19,14 @@
|
|||||||
namespace LibInput
|
namespace LibInput
|
||||||
{
|
{
|
||||||
|
|
||||||
struct StringViewLower
|
struct StringViewLowerComp
|
||||||
{
|
{
|
||||||
BAN::StringView value;
|
constexpr bool operator()(const BAN::StringView& a, const BAN::StringView& b) const
|
||||||
|
|
||||||
StringViewLower(BAN::StringView sv)
|
|
||||||
: value(sv)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
bool operator==(const StringViewLower& other) const
|
|
||||||
{
|
{
|
||||||
if (value.size() != other.value.size())
|
if (a.size() != b.size())
|
||||||
return false;
|
return false;
|
||||||
for (size_t i = 0; i < value.size(); i++)
|
for (size_t i = 0; i < a.size(); i++)
|
||||||
if (tolower(value[i]) != tolower(other.value[i]))
|
if (tolower(a[i]) != tolower(b[i]))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -40,16 +34,16 @@ namespace LibInput
|
|||||||
|
|
||||||
struct StringViewLowerHash
|
struct StringViewLowerHash
|
||||||
{
|
{
|
||||||
BAN::hash_t operator()(const StringViewLower& value) const
|
BAN::hash_t operator()(const BAN::StringView& str) const
|
||||||
{
|
{
|
||||||
constexpr BAN::hash_t FNV_offset_basis = 0x811c9dc5;
|
constexpr BAN::hash_t FNV_offset_basis = 0x811c9dc5;
|
||||||
constexpr BAN::hash_t FNV_prime = 0x01000193;
|
constexpr BAN::hash_t FNV_prime = 0x01000193;
|
||||||
|
|
||||||
BAN::hash_t hash = FNV_offset_basis;
|
BAN::hash_t hash = FNV_offset_basis;
|
||||||
for (size_t i = 0; i < value.value.size(); i++)
|
for (size_t i = 0; i < str.size(); i++)
|
||||||
{
|
{
|
||||||
hash *= FNV_prime;
|
hash *= FNV_prime;
|
||||||
hash ^= (uint8_t)tolower(value.value[i]);
|
hash ^= (uint8_t)tolower(str[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash;
|
return hash;
|
||||||
@@ -113,7 +107,7 @@ namespace LibInput
|
|||||||
return keycode;
|
return keycode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BAN::HashMap<StringViewLower, Key, StringViewLowerHash> s_name_to_key;
|
static BAN::HashMap<BAN::StringView, Key, StringViewLowerHash, StringViewLowerComp> s_name_to_key;
|
||||||
static BAN::ErrorOr<void> initialize_name_to_key();
|
static BAN::ErrorOr<void> initialize_name_to_key();
|
||||||
|
|
||||||
static BAN::Optional<Key> parse_key(BAN::StringView name)
|
static BAN::Optional<Key> parse_key(BAN::StringView name)
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
set(SOURCES
|
|
||||||
dummy.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(libmath-static STATIC ${SOURCES})
|
|
||||||
add_library(libmath-shared SHARED ${SOURCES})
|
|
||||||
|
|
||||||
target_link_options(libmath-static PRIVATE -nolibc)
|
|
||||||
target_link_options(libmath-shared PRIVATE -nolibc)
|
|
||||||
|
|
||||||
banan_link_library(libmath-static libc)
|
|
||||||
banan_link_library(libmath-shared libc)
|
|
||||||
|
|
||||||
set_target_properties(libmath-static PROPERTIES OUTPUT_NAME libm)
|
|
||||||
set_target_properties(libmath-shared PROPERTIES OUTPUT_NAME libm)
|
|
||||||
|
|
||||||
install(TARGETS libmath-static OPTIONAL)
|
|
||||||
install(TARGETS libmath-shared OPTIONAL)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
set(SOURCES
|
|
||||||
dummy.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(libpthread-static STATIC ${SOURCES})
|
|
||||||
add_library(libpthread-shared SHARED ${SOURCES})
|
|
||||||
|
|
||||||
target_link_options(libpthread-static PRIVATE -nolibc)
|
|
||||||
target_link_options(libpthread-shared PRIVATE -nolibc)
|
|
||||||
|
|
||||||
banan_link_library(libpthread-static libc)
|
|
||||||
banan_link_library(libpthread-shared libc)
|
|
||||||
|
|
||||||
set_target_properties(libpthread-static PROPERTIES OUTPUT_NAME libpthread)
|
|
||||||
set_target_properties(libpthread-shared PROPERTIES OUTPUT_NAME libpthread)
|
|
||||||
|
|
||||||
install(TARGETS libpthread-static OPTIONAL)
|
|
||||||
install(TARGETS libpthread-shared OPTIONAL)
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <BAN/Endianness.h>
|
#include <BAN/Endianness.h>
|
||||||
#include <BAN/HashMap.h>
|
#include <BAN/HashMap.h>
|
||||||
#include <BAN/IPv4.h>
|
#include <BAN/IPv4.h>
|
||||||
|
#include <BAN/LinkedList.h>
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
|||||||
Reference in New Issue
Block a user