Merge pull request 'update main' (#1) from Bananymous/banan-os:main into main

Reviewed-on: Sinipelto/banan-os#1
This commit is contained in:
Sinipelto 2023-11-20 13:20:51 +02:00
commit a6a5c00763
263 changed files with 14912 additions and 157609 deletions

5
.gitignore vendored
View File

@ -2,7 +2,4 @@
.idea/
build/
base/
*.tar.*
toolchain/*/
!base-sysroot.tar.gz
script/fakeroot-context

4
.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "kernel/lai"]
path = kernel/lai
url = https://github.com/managarm/lai.git
ignore = untracked

View File

@ -1,102 +1,109 @@
#include <BAN/Errors.h>
#include <BAN/Math.h>
#include <BAN/Move.h>
#include <BAN/New.h>
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <string.h>
#include <BAN/New.h>
namespace BAN
{
String::String()
{
MUST(copy_impl(""sv));
}
String::String(const String& other)
{
MUST(copy_impl(other.sv()));
*this = other;
}
String::String(String&& other)
{
move_impl(move(other));
*this = move(other);
}
String::String(StringView other)
{
MUST(copy_impl(other));
*this = other;
}
String::~String()
{
BAN::deallocator(m_data);
clear();
}
String& String::operator=(const String& other)
{
MUST(copy_impl(other.sv()));
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size() + 1);
m_size = other.size();
return *this;
}
String& String::operator=(String&& other)
{
BAN::deallocator(m_data);
move_impl(move(other));
clear();
if (other.has_sso())
memcpy(data(), other.data(), other.size() + 1);
else
{
m_storage.general_storage = other.m_storage.general_storage;
m_has_sso = false;
}
m_size = other.m_size;
other.m_size = 0;
other.m_storage.sso_storage = SSOStorage();
other.m_has_sso = true;
return *this;
}
String& String::operator=(StringView other)
{
MUST(copy_impl(other));
clear();
MUST(ensure_capacity(other.size()));
memcpy(data(), other.data(), other.size());
m_size = other.size();
data()[m_size] = '\0';
return *this;
}
ErrorOr<void> String::push_back(char ch)
ErrorOr<void> String::push_back(char c)
{
TRY(ensure_capacity(m_size + 2));
m_data[m_size] = ch;
TRY(ensure_capacity(m_size + 1));
data()[m_size] = c;
m_size++;
m_data[m_size] = '\0';
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(char ch, size_type index)
ErrorOr<void> String::insert(char c, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + 1 + 1));
memmove(m_data + index + 1, m_data + index, m_size - index);
m_data[index] = ch;
m_size += 1;
m_data[m_size] = '\0';
TRY(ensure_capacity(m_size + 1));
memmove(data() + index + 1, data() + index, m_size - index);
data()[index] = c;
m_size++;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::insert(StringView other, size_type index)
ErrorOr<void> String::insert(StringView str, size_type index)
{
ASSERT(index <= m_size);
TRY(ensure_capacity(m_size + other.size() + 1));
memmove(m_data + index + other.size(), m_data + index, m_size - index);
memcpy(m_data + index, other.data(), other.size());
m_size += other.size();
m_data[m_size] = '\0';
TRY(ensure_capacity(m_size + str.size()));
memmove(data() + index + str.size(), data() + index, m_size - index);
memcpy(data() + index, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::append(StringView other)
ErrorOr<void> String::append(StringView str)
{
TRY(ensure_capacity(m_size + other.size() + 1));
memcpy(m_data + m_size, other.data(), other.size());
m_size += other.size();
m_data[m_size] = '\0';
return {};
}
ErrorOr<void> String::append(const String& string)
{
TRY(append(string.sv()));
TRY(ensure_capacity(m_size + str.size()));
memcpy(data() + m_size, str.data(), str.size());
m_size += str.size();
data()[m_size] = '\0';
return {};
}
@ -104,159 +111,159 @@ namespace BAN
{
ASSERT(m_size > 0);
m_size--;
m_data[m_size] = '\0';
data()[m_size] = '\0';
}
void String::remove(size_type index)
{
erase(index, 1);
}
void String::erase(size_type index, size_type count)
{
ASSERT(index + count <= m_size);
memmove(m_data + index, m_data + index + count, m_size - index - count);
m_size -= count;
m_data[m_size] = '\0';
ASSERT(index < m_size);
memcpy(data() + index, data() + index + 1, m_size - index);
m_size--;
data()[m_size] = '\0';
}
void String::clear()
{
if (!has_sso())
{
deallocator(m_storage.general_storage.data);
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
}
m_size = 0;
m_data[0] = '\0';
data()[m_size] = '\0';
}
char String::operator[](size_type index) const
bool String::operator==(StringView str) const
{
ASSERT(index < m_size);
return m_data[index];
}
char& String::operator[](size_type index)
{
ASSERT(index < m_size);
return m_data[index];
}
bool String::operator==(const String& other) const
{
if (m_size != other.m_size)
if (size() != str.size())
return false;
return memcmp(m_data, other.m_data, m_size) == 0;
}
bool String::operator==(StringView other) const
{
if (m_size != other.size())
return false;
return memcmp(m_data, other.data(), m_size) == 0;
}
bool String::operator==(const char* other) const
{
for (size_type i = 0; i <= m_size; i++)
if (m_data[i] != other[i])
for (size_type i = 0; i < m_size; i++)
if (data()[i] != str.data()[i])
return false;
return true;
}
ErrorOr<void> String::resize(size_type size, char ch)
bool String::operator==(const char* cstr) const
{
if (size < m_size)
for (size_type i = 0; i < m_size; i++)
if (data()[i] != cstr[i])
return false;
if (cstr[size()] != '\0')
return false;
return true;
}
ErrorOr<void> String::resize(size_type new_size, char init_c)
{
if (m_size == new_size)
return {};
// expanding
if (m_size < new_size)
{
m_data[size] = '\0';
m_size = size;
TRY(ensure_capacity(new_size));
memset(data() + m_size, init_c, new_size - m_size);
m_size = new_size;
data()[m_size] = '\0';
return {};
}
else if (size > m_size)
{
TRY(ensure_capacity(size + 1));
for (size_type i = m_size; i < size; i++)
m_data[i] = ch;
m_data[size] = '\0';
m_size = size;
}
m_size = size;
m_size = new_size;
data()[m_size] = '\0';
return {};
}
ErrorOr<void> String::reserve(size_type size)
ErrorOr<void> String::reserve(size_type new_size)
{
TRY(ensure_capacity(size));
TRY(ensure_capacity(new_size));
return {};
}
ErrorOr<void> String::shrink_to_fit()
{
size_type temp = m_capacity;
m_capacity = 0;
auto error_or = ensure_capacity(m_size);
if (error_or.is_error())
if (has_sso())
return {};
if (fits_in_sso())
{
m_capacity = temp;
return error_or;
char* data = m_storage.general_storage.data;
m_storage.sso_storage = SSOStorage();
m_has_sso = true;
memcpy(this->data(), data, m_size + 1);
deallocator(data);
return {};
}
GeneralStorage& storage = m_storage.general_storage;
if (storage.capacity == m_size)
return {};
char* new_data = (char*)allocator(m_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
memcpy(new_data, storage.data, m_size);
deallocator(storage.data);
storage.capacity = m_size;
storage.data = new_data;
return {};
}
StringView String::sv() const
{
return StringView(*this);
}
bool String::empty() const
{
return m_size == 0;
}
String::size_type String::size() const
{
return m_size;
}
String::size_type String::capacity() const
{
return m_capacity;
if (has_sso())
return sso_capacity;
return m_storage.general_storage.capacity;
}
char* String::data()
{
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
const char* String::data() const
{
return m_data;
if (has_sso())
return m_storage.sso_storage.data;
return m_storage.general_storage.data;
}
ErrorOr<void> String::ensure_capacity(size_type size)
ErrorOr<void> String::ensure_capacity(size_type new_size)
{
if (m_capacity >= size)
if (m_size >= new_size)
return {};
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
void* new_data = BAN::allocator(new_cap);
if (has_sso() && fits_in_sso(new_size))
return {};
char* new_data = (char*)allocator(new_size + 1);
if (new_data == nullptr)
return Error::from_errno(ENOMEM);
if (m_data)
memcpy(new_data, m_data, m_size + 1);
BAN::deallocator(m_data);
m_data = (char*)new_data;
m_capacity = new_cap;
memcpy(new_data, data(), m_size + 1);
if (has_sso())
{
m_storage.general_storage = GeneralStorage();
m_has_sso = false;
}
else
deallocator(m_storage.general_storage.data);
auto& storage = m_storage.general_storage;
storage.capacity = new_size;
storage.data = new_data;
return {};
}
ErrorOr<void> String::copy_impl(StringView other)
bool String::has_sso() const
{
TRY(ensure_capacity(other.size() + 1));
memcpy(m_data, other.data(), other.size());
m_size = other.size();
m_data[m_size] = '\0';
return {};
}
void String::move_impl(String&& other)
{
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
return m_has_sso;
}
}

View File

@ -10,17 +10,15 @@ set(BAN_SOURCES
)
add_custom_target(ban-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_library(ban ${BAN_SOURCES})
add_dependencies(ban headers libc-install)
add_custom_target(ban-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_BINARY_DIR}/libban.a ${BANAN_LIB}/
DEPENDS ban
BYPRODUCTS ${BANAN_LIB}/libban.a
USES_TERMINAL
)

View File

@ -1,11 +1,33 @@
#pragma once
#include <BAN/Traits.h>
#if defined(__is_kernel)
#include <kernel/Panic.h>
#define ASSERT(cond) do { if (!(cond)) Kernel::panic("ASSERT("#cond") failed"); } while (false)
#define ASSERT(cond) \
do { \
if (!(cond)) \
Kernel::panic("ASSERT(" #cond ") failed"); \
} while (false)
#define __ASSERT_BIN_OP(lhs, rhs, name, op) \
do { \
auto&& _lhs = lhs; \
auto&& _rhs = rhs; \
if (!(_lhs op _rhs)) \
Kernel::panic(name "(" #lhs ", " #rhs ") ({} " #op " {}) failed", _lhs, _rhs); \
} while (false)
#define ASSERT_LT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LT", <)
#define ASSERT_LTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_LTE", <=)
#define ASSERT_GT(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GT", >)
#define ASSERT_GTE(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_GTE", >=)
#define ASSERT_EQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_EQ", ==)
#define ASSERT_NEQ(lhs, rhs) __ASSERT_BIN_OP(lhs, rhs, "ASSERT_NEQ", !=)
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
#else
#include <assert.h>
#define ASSERT(cond) assert((cond) && "ASSERT("#cond") failed")
#define ASSERT_NOT_REACHED() do { assert(false && "ASSERT_NOT_REACHED() failed"); __builtin_unreachable(); } while (false)
#endif
#endif

140
BAN/include/BAN/ByteSpan.h Normal file
View File

@ -0,0 +1,140 @@
#pragma once
#include <BAN/Span.h>
namespace BAN
{
template<bool CONST>
class ByteSpanGeneral
{
public:
using value_type = maybe_const_t<CONST, uint8_t>;
using size_type = size_t;
public:
ByteSpanGeneral() = default;
ByteSpanGeneral(value_type* data, size_type size)
: m_data(data)
, m_size(size)
{ }
ByteSpanGeneral(ByteSpanGeneral& other)
: m_data(other.data())
, m_size(other.size())
{ }
template<bool C2>
ByteSpanGeneral(const ByteSpanGeneral<C2>& other) requires(CONST)
: m_data(other.data())
, m_size(other.size())
{ }
ByteSpanGeneral(Span<uint8_t> other)
: m_data(other.data())
, m_size(other.size())
{ }
ByteSpanGeneral(const Span<const uint8_t>& other) requires(CONST)
: m_data(other.data())
, m_size(other.size())
{ }
ByteSpanGeneral& operator=(ByteSpanGeneral other)
{
m_data = other.data();
m_size = other.size();
return *this;
}
template<bool C2>
ByteSpanGeneral& operator=(const ByteSpanGeneral<C2>& other) requires(CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(Span<uint8_t> other)
{
m_data = other.data();
m_size = other.size();
return *this;
}
ByteSpanGeneral& operator=(const Span<const uint8_t>& other) requires(CONST)
{
m_data = other.data();
m_size = other.size();
return *this;
}
template<typename S>
requires(CONST || !is_const_v<S>)
static ByteSpanGeneral from(S& value)
{
return ByteSpanGeneral(reinterpret_cast<value_type*>(&value), sizeof(S));
}
template<typename S>
requires(!CONST && !is_const_v<S>)
S& as()
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
requires(is_const_v<S>)
S& as() const
{
ASSERT(m_data);
ASSERT(m_size >= sizeof(S));
return *reinterpret_cast<S*>(m_data);
}
template<typename S>
requires(!CONST && !is_const_v<S>)
Span<S> as_span()
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
template<typename S>
const Span<S> as_span() const
{
ASSERT(m_data);
return Span<S>(reinterpret_cast<S*>(m_data), m_size / sizeof(S));
}
ByteSpanGeneral slice(size_type offset, size_type length = size_type(-1))
{
ASSERT(m_data);
ASSERT(m_size >= offset);
if (length == size_type(-1))
length = m_size - offset;
ASSERT(m_size >= offset + length);
return ByteSpanGeneral(m_data + offset, length);
}
value_type& operator[](size_type offset)
{
ASSERT(offset < m_size);
return m_data[offset];
}
const value_type& operator[](size_type offset) const
{
ASSERT(offset < m_size);
return m_data[offset];
}
value_type* data() { return m_data; }
const value_type* data() const { return m_data; }
size_type size() const { return m_size; }
private:
value_type* m_data { nullptr };
size_type m_size { 0 };
};
using ByteSpan = ByteSpanGeneral<false>;
using ConstByteSpan = ByteSpanGeneral<true>;
}

View File

@ -7,20 +7,62 @@
namespace BAN
{
template<integral T>
constexpr T swap_endianness(T value)
{
if constexpr(sizeof(T) == 1)
return value;
if constexpr(sizeof(T) == 2)
return (((value >> 8) & 0xFF) << 0)
| (((value >> 0) & 0xFF) << 8);
if constexpr(sizeof(T) == 4)
return (((value >> 24) & 0xFF) << 0)
| (((value >> 16) & 0xFF) << 8)
| (((value >> 8) & 0xFF) << 16)
| (((value >> 0) & 0xFF) << 24);
if constexpr(sizeof(T) == 8)
return (((value >> 56) & 0xFF) << 0)
| (((value >> 48) & 0xFF) << 8)
| (((value >> 40) & 0xFF) << 16)
| (((value >> 32) & 0xFF) << 24)
| (((value >> 24) & 0xFF) << 32)
| (((value >> 16) & 0xFF) << 40)
| (((value >> 8) & 0xFF) << 48)
| (((value >> 0) & 0xFF) << 56);
T result { 0 };
for (size_t i = 0; i < sizeof(T); i++)
result |= ((value >> (i * 8)) & 0xFF) << ((sizeof(T) - i - 1) * 8);
return result;
}
template<integral T>
constexpr T host_to_little_endian(T value)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return value;
#else
return swap_endianness(value);
#endif
}
template<integral T>
constexpr T host_to_big_endian(T value)
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return value;
#else
return swap_endianness(value);
#endif
}
template<integral T>
struct LittleEndian
{
constexpr operator T() const
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return raw;
#else
T result { 0 };
for (size_t i = 0; i < sizeof(T); i++)
result = (result << 8) | ((raw >> (sizeof(T) - i - 1) * 8) & 0xFF);
return result;
#endif
return host_to_little_endian(raw);
}
private:
T raw;
};
@ -29,14 +71,7 @@ namespace BAN
{
constexpr operator T() const
{
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
return raw;
#else
T result { 0 };
for (size_t i = 0; i < sizeof(T); i++)
result = (result << 8) | ((raw >> (i * 8)) & 0xFF);
return result;
#endif
return host_to_big_endian(raw);
}
private:
T raw;

View File

@ -1,5 +1,6 @@
#pragma once
#include <BAN/Traits.h>
#include <stddef.h>
namespace BAN
@ -14,5 +15,6 @@ namespace BAN
class StringView;
template<typename> class Vector;
template<typename> class LinkedList;
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
}

View File

@ -195,7 +195,7 @@ namespace BAN
template<typename T>
void LinkedList<T>::pop_back()
{
return remove(m_last);
remove(iterator(m_last, false));
}
template<typename T>

View File

@ -102,22 +102,4 @@ namespace BAN::Math
return result;
}
template<integral T>
inline constexpr T little_endian_to_host(const uint8_t* bytes)
{
T result = 0;
for (size_t i = 0; i < sizeof(T); i++)
result |= (T)bytes[i] << (i * 8);
return result;
}
template<integral T>
inline constexpr T big_endian_to_host(const uint8_t* bytes)
{
T result = 0;
for (size_t i = 0; i < sizeof(T); i++)
result |= (T)bytes[i] << (8 * (sizeof(T) - i - 1));
return result;
}
}

View File

@ -13,6 +13,8 @@ namespace BAN
{
public:
Optional();
Optional(Optional&&);
Optional(const Optional&);
Optional(const T&);
Optional(T&&);
template<typename... Args>
@ -20,8 +22,8 @@ namespace BAN
~Optional();
Optional& operator=(const Optional&);
Optional& operator=(Optional&&);
Optional& operator=(const Optional&);
template<typename... Args>
Optional& emplace(Args&&...);
@ -34,9 +36,9 @@ namespace BAN
bool has_value() const;
T&& release_value();
const T& value() const;
T release_value();
T& value();
const T& value() const;
void clear();
@ -50,6 +52,22 @@ namespace BAN
: m_has_value(false)
{}
template<typename T>
Optional<T>::Optional(Optional<T>&& other)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(move(other.release_value()));
}
template<typename T>
Optional<T>::Optional(const Optional<T>& other)
: m_has_value(other.has_value())
{
if (other.has_value())
new (m_storage) T(other.value());
}
template<typename T>
Optional<T>::Optional(const T& value)
: m_has_value(true)
@ -61,7 +79,7 @@ namespace BAN
Optional<T>::Optional(T&& value)
: m_has_value(true)
{
new (m_storage) T(BAN::move(value));
new (m_storage) T(move(value));
}
template<typename T>
@ -69,7 +87,7 @@ namespace BAN
Optional<T>::Optional(Args&&... args)
: m_has_value(true)
{
new (m_storage) T(BAN::forward<Args>(args)...);
new (m_storage) T(forward<Args>(args)...);
}
template<typename T>
@ -79,26 +97,22 @@ namespace BAN
}
template<typename T>
Optional<T>& Optional<T>::operator=(const Optional& other)
Optional<T>& Optional<T>::operator=(Optional&& other)
{
clear();
m_has_value = other.has_value();
if (other.has_value())
{
m_has_value = true;
new (m_storage) T(other.value());
}
new (m_storage) T(move(other.release_value()));
return *this;
}
template<typename T>
Optional<T>& Optional<T>::operator=(Optional&& other)
Optional<T>& Optional<T>::operator=(const Optional& other)
{
clear();
if (other.has_value())
{
m_has_value = true;
new (m_storage) T(BAN::move(other.release_value()));
}
m_has_value = other.has_value();
if (other.has_value)
new (m_storage) T(other.value());
return *this;
}
@ -108,7 +122,7 @@ namespace BAN
{
clear();
m_has_value = true;
new (m_storage) T(BAN::forward<Args>(args)...);
new (m_storage) T(forward<Args>(args)...);
return *this;
}
@ -147,18 +161,13 @@ namespace BAN
}
template<typename T>
T&& Optional<T>::release_value()
T Optional<T>::release_value()
{
ASSERT(has_value());
T released_value = move(value());
value().~T();
m_has_value = false;
return BAN::move((T&)m_storage);
}
template<typename T>
const T& Optional<T>::value() const
{
ASSERT(has_value());
return (const T&)m_storage;
return move(released_value);
}
template<typename T>
@ -168,6 +177,13 @@ namespace BAN
return (T&)m_storage;
}
template<typename T>
const T& Optional<T>::value() const
{
ASSERT(has_value());
return (const T&)m_storage;
}
template<typename T>
void Optional<T>::clear()
{

View File

@ -43,6 +43,8 @@ namespace BAN
Span slice(size_type, size_type = ~size_type(0));
Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
private:
T* m_data = nullptr;
size_type m_size = 0;
@ -125,8 +127,8 @@ namespace BAN
ASSERT(start <= m_size);
if (length == ~size_type(0))
length = m_size - start;
ASSERT(start + length <= m_size);
return Span(m_data + start, m_size - start - length);
ASSERT(m_size - start >= length);
return Span(m_data + start, length);
}
}

View File

@ -1,8 +1,8 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/ForwardList.h>
#include <BAN/Formatter.h>
#include <BAN/ForwardList.h>
#include <BAN/Hash.h>
#include <BAN/Iterators.h>
@ -15,6 +15,7 @@ namespace BAN
using size_type = size_t;
using iterator = IteratorSimple<char, String>;
using const_iterator = ConstIteratorSimple<char, String>;
static constexpr size_type sso_capacity = 15;
public:
String();
@ -34,29 +35,26 @@ namespace BAN
ErrorOr<void> insert(char, size_type);
ErrorOr<void> insert(StringView, size_type);
ErrorOr<void> append(StringView);
ErrorOr<void> append(const String&);
void pop_back();
void remove(size_type);
void erase(size_type, size_type);
void clear();
const_iterator begin() const { return const_iterator(m_data); }
iterator begin() { return iterator(m_data); }
const_iterator end() const { return const_iterator(m_data + m_size); }
iterator end() { return iterator(m_data + m_size); }
const_iterator begin() const { return const_iterator(data()); }
iterator begin() { return iterator(data()); }
const_iterator end() const { return const_iterator(data() + size()); }
iterator end() { return iterator(data() + size()); }
char front() const { ASSERT(!empty()); return m_data[0]; }
char& front() { ASSERT(!empty()); return m_data[0]; }
char front() const { ASSERT(m_size > 0); return data()[0]; }
char& front() { ASSERT(m_size > 0); return data()[0]; }
char back() const { ASSERT(!empty()); return m_data[m_size - 1]; }
char& back() { ASSERT(!empty()); return m_data[m_size - 1]; }
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
char operator[](size_type) const;
char& operator[](size_type);
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
bool operator==(const String&) const;
bool operator==(StringView) const;
bool operator==(const char*) const;
@ -64,24 +62,41 @@ namespace BAN
ErrorOr<void> reserve(size_type);
ErrorOr<void> shrink_to_fit();
StringView sv() const;
StringView sv() const { return StringView(data(), size()); }
bool empty() const;
size_type size() const;
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
size_type capacity() const;
char* data();
const char* data() const;
private:
ErrorOr<void> ensure_capacity(size_type);
ErrorOr<void> copy_impl(StringView);
void move_impl(String&&);
bool has_sso() const;
bool fits_in_sso() const { return fits_in_sso(m_size); }
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
private:
char* m_data = nullptr;
size_type m_capacity = 0;
size_type m_size = 0;
struct SSOStorage
{
char data[sso_capacity + 1] {};
};
struct GeneralStorage
{
size_type capacity { 0 };
char* data { nullptr };
};
private:
union {
SSOStorage sso_storage;
GeneralStorage general_storage;
} m_storage { .sso_storage = SSOStorage() };
size_type m_size : sizeof(size_type) * 8 - 1 { 0 };
size_type m_has_sso : 1 { true };
};
template<typename... Args>

107
BAN/include/BAN/WeakPtr.h Normal file
View File

@ -0,0 +1,107 @@
#pragma once
#include <BAN/RefPtr.h>
namespace BAN
{
template<typename T>
class Weakable;
template<typename T>
class WeakPtr;
template<typename T>
class WeakLink : public RefCounted<WeakLink<T>>
{
public:
RefPtr<T> lock() { ASSERT(m_ptr); return raw_ptr(); }
T* raw_ptr() { return m_ptr; }
bool valid() const { return m_ptr; }
void invalidate() { m_ptr = nullptr; }
private:
WeakLink(T* ptr) : m_ptr(ptr) {}
private:
T* m_ptr;
friend class RefPtr<WeakLink<T>>;
};
template<typename T>
class Weakable
{
public:
~Weakable()
{
if (m_link)
m_link->invalidate();
}
ErrorOr<WeakPtr<T>> get_weak_ptr() const
{
if (!m_link)
m_link = TRY(RefPtr<WeakLink<T>>::create((T*)this));
return WeakPtr<T>(m_link);
}
private:
mutable RefPtr<WeakLink<T>> m_link;
};
template<typename T>
class WeakPtr
{
public:
WeakPtr() = default;
WeakPtr(WeakPtr&& other) { *this = move(other); }
WeakPtr(const WeakPtr& other) { *this = other; }
WeakPtr(const RefPtr<T>& other) { *this = other; }
WeakPtr& operator=(WeakPtr&& other)
{
clear();
m_link = move(other.m_link);
return *this;
}
WeakPtr& operator=(const WeakPtr& other)
{
clear();
m_link = other.m_link;
return *this;
}
WeakPtr& operator=(const RefPtr<T>& other)
{
clear();
if (other)
m_link = MUST(other->get_weak_ptr()).move_link();
return *this;
}
RefPtr<T> lock()
{
if (m_link->valid())
return m_link->lock();
return nullptr;
}
void clear() { m_link.clear(); }
bool valid() const { return m_link && m_link->valid(); }
private:
WeakPtr(const RefPtr<WeakLink<T>>& link)
: m_link(link)
{ }
RefPtr<WeakLink<T>>&& move_link() { return move(m_link); }
private:
RefPtr<WeakLink<T>> m_link;
friend class Weakable<T>;
};
}

View File

@ -1,42 +1,30 @@
cmake_minimum_required(VERSION 3.26)
if(DEFINED ENV{BANAN_ARCH})
set(BANAN_ARCH $ENV{BANAN_ARCH})
else()
set(BANAN_ARCH x86_64)
endif()
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
endif ()
set(TOOLCHAIN_PREFIX ${CMAKE_SOURCE_DIR}/toolchain/local)
add_compile_options(-mno-sse -mno-sse2)
add_compile_definitions(__enable_sse=0)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}/bin/${BANAN_ARCH}-banan_os-g++)
set(CMAKE_CXX_COMPILER_WORKS True)
if(NOT EXISTS ${CMAKE_CXX_COMPILER})
set(CMAKE_CXX_COMPILER g++)
endif()
project(banan-os CXX)
project(banan-os CXX C ASM)
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
set(BANAN_SYSROOT ${CMAKE_BINARY_DIR}/sysroot)
set(BANAN_INCLUDE ${BANAN_SYSROOT}/usr/include)
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img)
add_subdirectory(kernel)
add_subdirectory(bootloader)
add_subdirectory(BAN)
add_subdirectory(libc)
add_subdirectory(LibELF)
add_subdirectory(userspace)
add_custom_target(sysroot
COMMAND mkdir -p ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && sudo tar xf ${BANAN_BASE_SYSROOT}
USES_TERMINAL
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
)
add_custom_target(headers
@ -46,51 +34,11 @@ add_custom_target(headers
DEPENDS libelf-headers
)
add_custom_target(toolchain
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" PREFIX="${TOOLCHAIN_PREFIX}" ARCH="${BANAN_ARCH}" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
DEPENDS headers
USES_TERMINAL
)
add_custom_target(libstdc++
COMMAND ${CMAKE_COMMAND} -E env LIBSTDCPP="1" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
DEPENDS libc-install
USES_TERMINAL
)
add_custom_target(image
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image.sh
add_custom_target(install-sysroot
COMMAND cd ${BANAN_SYSROOT} && tar cf ${BANAN_SYSROOT_TAR} *
DEPENDS kernel-install
DEPENDS ban-install
DEPENDS libc-install
DEPENDS userspace-install
DEPENDS libelf-install
USES_TERMINAL
)
add_custom_target(image-full
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/image-full.sh
DEPENDS kernel-install
DEPENDS ban-install
DEPENDS libc-install
DEPENDS userspace-install
DEPENDS libelf-install
USES_TERMINAL
)
add_custom_target(check-fs
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/check-fs.sh
USES_TERMINAL
)
add_custom_target(qemu
COMMAND ${CMAKE_COMMAND} -E env BANAN_ARCH="${BANAN_ARCH}" DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/qemu.sh
DEPENDS image
USES_TERMINAL
)
add_custom_target(bochs
COMMAND ${CMAKE_COMMAND} -E env DISK_IMAGE_PATH="${DISK_IMAGE_PATH}" ${CMAKE_SOURCE_DIR}/bochs.sh
DEPENDS image
USES_TERMINAL
)

View File

@ -3,9 +3,8 @@ cmake_minimum_required(VERSION 3.26)
project(LibELF CXX)
add_custom_target(libelf-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_custom_target(libelf-install

View File

@ -0,0 +1,331 @@
#include <BAN/ScopeGuard.h>
#include <kernel/CriticalScope.h>
#include <kernel/Memory/Heap.h>
#include <kernel/LockGuard.h>
#include <LibELF/LoadableELF.h>
#include <LibELF/Values.h>
namespace LibELF
{
using namespace Kernel;
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::load_from_inode(PageTable& page_table, BAN::RefPtr<Inode> inode)
{
auto* elf_ptr = new LoadableELF(page_table, inode);
if (elf_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
TRY(elf->initialize());
return BAN::move(elf);
}
LoadableELF::LoadableELF(PageTable& page_table, BAN::RefPtr<Inode> inode)
: m_inode(inode)
, m_page_table(page_table)
{
}
LoadableELF::~LoadableELF()
{
if (!m_loaded)
return;
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
continue;
case PT_LOAD:
{
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
for (size_t i = 0; i < pages; i++)
{
paddr_t paddr = m_page_table.physical_address_of(start + i * PAGE_SIZE);
if (paddr != 0)
Heap::get().release_page(paddr);
}
m_page_table.unmap_range(start, pages * PAGE_SIZE);
break;
}
default:
ASSERT_NOT_REACHED();
}
}
}
BAN::ErrorOr<void> LoadableELF::initialize()
{
if ((size_t)m_inode->size() < sizeof(ElfNativeFileHeader))
{
dprintln("Too small file");
return BAN::Error::from_errno(ENOEXEC);
}
size_t nread = TRY(m_inode->read(0, BAN::ByteSpan::from(m_file_header)));
ASSERT(nread == sizeof(m_file_header));
if (m_file_header.e_ident[EI_MAG0] != ELFMAG0 ||
m_file_header.e_ident[EI_MAG1] != ELFMAG1 ||
m_file_header.e_ident[EI_MAG2] != ELFMAG2 ||
m_file_header.e_ident[EI_MAG3] != ELFMAG3)
{
dprintln("Invalid magic in header");
return BAN::Error::from_errno(ENOEXEC);
}
if (m_file_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
dprintln("Only little-endian is supported");
return BAN::Error::from_errno(ENOEXEC);
}
if (m_file_header.e_ident[EI_VERSION] != EV_CURRENT)
{
dprintln("Invalid version");
return BAN::Error::from_errno(ENOEXEC);
}
#if ARCH(i386)
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS32)
#elif ARCH(x86_64)
if (m_file_header.e_ident[EI_CLASS] != ELFCLASS64)
#endif
{
dprintln("Not in native format");
return BAN::Error::from_errno(EINVAL);
}
if (m_file_header.e_type != ET_EXEC)
{
dprintln("Only executable files are supported");
return BAN::Error::from_errno(EINVAL);
}
if (m_file_header.e_version != EV_CURRENT)
{
dprintln("Unsupported version");
return BAN::Error::from_errno(EINVAL);
}
ASSERT(m_file_header.e_phentsize <= sizeof(ElfNativeProgramHeader));
TRY(m_program_headers.resize(m_file_header.e_phnum));
for (size_t i = 0; i < m_file_header.e_phnum; i++)
{
TRY(m_inode->read(m_file_header.e_phoff + m_file_header.e_phentsize * i, BAN::ByteSpan::from(m_program_headers[i])));
const auto& pheader = m_program_headers[i];
if (pheader.p_type != PT_NULL && pheader.p_type != PT_LOAD)
{
dprintln("Unsupported program header type {}", pheader.p_type);
return BAN::Error::from_errno(ENOTSUP);
}
if (pheader.p_memsz < pheader.p_filesz)
{
dprintln("Invalid program header");
return BAN::Error::from_errno(EINVAL);
}
m_virtual_page_count += BAN::Math::div_round_up<size_t>((pheader.p_vaddr % PAGE_SIZE) + pheader.p_memsz, PAGE_SIZE);
}
return {};
}
vaddr_t LoadableELF::entry_point() const
{
return m_file_header.e_entry;
}
bool LoadableELF::contains(vaddr_t address) const
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
continue;
case PT_LOAD:
if (program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz)
return true;
break;
default:
ASSERT_NOT_REACHED();
}
}
return false;
}
bool LoadableELF::is_address_space_free() const
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
if (!m_page_table.is_range_free(page_vaddr, pages * PAGE_SIZE))
return false;
break;
}
default:
ASSERT_NOT_REACHED();
}
}
return true;
}
void LoadableELF::reserve_address_space()
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
vaddr_t page_vaddr = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
ASSERT(m_page_table.reserve_range(page_vaddr, pages * PAGE_SIZE));
break;
}
default:
ASSERT_NOT_REACHED();
}
}
m_loaded = true;
}
BAN::ErrorOr<void> LoadableELF::load_page_to_memory(vaddr_t address)
{
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
if (!(program_header.p_vaddr <= address && address < program_header.p_vaddr + program_header.p_memsz))
continue;
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
if (program_header.p_flags & LibELF::PF_X)
flags |= PageTable::Flags::Execute;
vaddr_t vaddr = address & PAGE_ADDR_MASK;
paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
// Temporarily map page as RW so kernel can write to it
m_page_table.map_page_at(paddr, vaddr, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
m_physical_page_count++;
memset((void*)vaddr, 0x00, PAGE_SIZE);
if (vaddr / PAGE_SIZE < BAN::Math::div_round_up<size_t>(program_header.p_vaddr + program_header.p_filesz, PAGE_SIZE))
{
size_t vaddr_offset = 0;
if (vaddr < program_header.p_vaddr)
vaddr_offset = program_header.p_vaddr - vaddr;
size_t file_offset = 0;
if (vaddr > program_header.p_vaddr)
file_offset = vaddr - program_header.p_vaddr;
size_t bytes = BAN::Math::min<size_t>(PAGE_SIZE - vaddr_offset, program_header.p_filesz - file_offset);
TRY(m_inode->read(program_header.p_offset + file_offset, { (uint8_t*)vaddr + vaddr_offset, bytes }));
}
// Map page with the correct flags
m_page_table.map_page_at(paddr, vaddr, flags);
return {};
}
default:
ASSERT_NOT_REACHED();
}
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> LoadableELF::clone(Kernel::PageTable& new_page_table)
{
auto* elf_ptr = new LoadableELF(new_page_table, m_inode);
if (elf_ptr == nullptr)
return BAN::Error::from_errno(ENOMEM);
auto elf = BAN::UniqPtr<LoadableELF>::adopt(elf_ptr);
memcpy(&elf->m_file_header, &m_file_header, sizeof(ElfNativeFileHeader));
TRY(elf->m_program_headers.resize(m_program_headers.size()));
memcpy(elf->m_program_headers.data(), m_program_headers.data(), m_program_headers.size() * sizeof(ElfNativeProgramHeader));
elf->reserve_address_space();
ASSERT(&PageTable::current() == &m_page_table);
LockGuard _(m_page_table);
ASSERT(m_page_table.is_page_free(0));
for (const auto& program_header : m_program_headers)
{
switch (program_header.p_type)
{
case PT_NULL:
break;
case PT_LOAD:
{
if (!(program_header.p_flags & LibELF::PF_W))
continue;
PageTable::flags_t flags = PageTable::Flags::UserSupervisor | PageTable::Flags::Present;
if (program_header.p_flags & LibELF::PF_W)
flags |= PageTable::Flags::ReadWrite;
if (program_header.p_flags & LibELF::PF_X)
flags |= PageTable::Flags::Execute;
vaddr_t start = program_header.p_vaddr & PAGE_ADDR_MASK;
size_t pages = range_page_count(program_header.p_vaddr, program_header.p_memsz);
for (size_t i = 0; i < pages; i++)
{
if (m_page_table.physical_address_of(start + i * PAGE_SIZE) == 0)
continue;
paddr_t paddr = Heap::get().take_free_page();
if (paddr == 0)
return BAN::Error::from_errno(ENOMEM);
{
CriticalScope _;
PageTable::map_fast_page(paddr);
memcpy(PageTable::fast_page_as_ptr(), (void*)(start + i * PAGE_SIZE), PAGE_SIZE);
PageTable::unmap_fast_page();
}
new_page_table.map_page_at(paddr, start + i * PAGE_SIZE, flags);
elf->m_physical_page_count++;
}
break;
}
default:
ASSERT_NOT_REACHED();
}
}
return elf;
}
}

View File

@ -0,0 +1,54 @@
#pragma once
#ifndef __is_kernel
#error "This is kernel only header"
#endif
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <kernel/FS/Inode.h>
#include <kernel/Memory/PageTable.h>
#include <LibELF/Types.h>
namespace LibELF
{
class LoadableELF
{
BAN_NON_COPYABLE(LoadableELF);
BAN_NON_MOVABLE(LoadableELF);
public:
static BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> load_from_inode(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
~LoadableELF();
Kernel::vaddr_t entry_point() const;
bool contains(Kernel::vaddr_t address) const;
bool is_address_space_free() const;
void reserve_address_space();
BAN::ErrorOr<void> load_page_to_memory(Kernel::vaddr_t address);
BAN::ErrorOr<BAN::UniqPtr<LoadableELF>> clone(Kernel::PageTable&);
size_t virtual_page_count() const { return m_virtual_page_count; }
size_t physical_page_count() const { return m_physical_page_count; }
private:
LoadableELF(Kernel::PageTable&, BAN::RefPtr<Kernel::Inode>);
BAN::ErrorOr<void> initialize();
private:
BAN::RefPtr<Kernel::Inode> m_inode;
Kernel::PageTable& m_page_table;
ElfNativeFileHeader m_file_header;
BAN::Vector<ElfNativeProgramHeader> m_program_headers;
size_t m_virtual_page_count = 0;
size_t m_physical_page_count = 0;
bool m_loaded { false };
};
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <kernel/Arch.h>
#include <stdint.h>
namespace LibELF
@ -153,4 +155,32 @@ namespace LibELF
Elf64Xword p_align;
};
#if ARCH(i386)
using ElfNativeAddr = Elf32Addr;
using ElfNativeOff = Elf32Off;
using ElfNativeHalf = Elf32Half;
using ElfNativeWord = Elf32Word;
using ElfNativeSword = Elf32Sword;
using ElfNativeFileHeader = Elf32FileHeader;
using ElfNativeSectionHeader = Elf32SectionHeader;
using ElfNativeSymbol = Elf32Symbol;
using ElfNativeRelocation = Elf32Relocation;
using ElfNativeRelocationA = Elf32RelocationA;
using ElfNativeProgramHeader = Elf32ProgramHeader;
#elif ARCH(x86_64)
using ElfNativeAddr = Elf64Addr;
using ElfNativeOff = Elf64Off;
using ElfNativeHalf = Elf64Half;
using ElfNativeWord = Elf64Word;
using ElfNativeSword = Elf64Sword;
using ElfNativeXword = Elf64Xword;
using ElfNativeSxword = Elf64Sxword;
using ElfNativeFileHeader = Elf64FileHeader;
using ElfNativeSectionHeader = Elf64SectionHeader;
using ElfNativeSymbol = Elf64Symbol;
using ElfNativeRelocation = Elf64Relocation;
using ElfNativeRelocationA = Elf64RelocationA;
using ElfNativeProgramHeader = Elf64ProgramHeader;
#endif
}

View File

@ -2,7 +2,7 @@
# banan-os
This is my hobby operating system written in C++. Currently supports only x86_64 architecture. We have a read-only ext2 filesystem, read-write ramfs, IDE disk drivers in ATA PIO mode, userspace processes, executable loading from ELF format, linear VBE graphics and multithreaded processing on single core.
This is my hobby operating system written in C++. Currently supports only x86\_64 architecture. We have a ext2 filesystem, basic ramfs, IDE disk drivers in ATA PIO mode, ATA AHCI drivers, userspace processes, executable loading from ELF format, linear VBE graphics and multithreaded processing on single core.
![screenshot from qemu running banan-os](assets/banan-os.png)
@ -14,40 +14,35 @@ Each major component and library has its own subdirectory (kernel, userspace, li
There does not exist a complete list of needed packages for building. From the top of my head I can say that *cmake*, *ninja*, *make*, *grub*, *rsync* and emulator (*qemu* or *bochs*) are needed.
Create the build directory and cofigure cmake
```sh
mkdir build
cd build
cmake -G Ninja ..
```
To build the toolchain for this os. You can run the following command.
> ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc.
```sh
ninja toolchain
cmake -G Ninja --fresh .. # We need to reconfigure cmake to use the new compiler
ninja libstdc++
./script/build.sh toolchain
```
To build the os itself you can run either of the following commands. You will need root access since the sysroot has "proper" permissions.
To build the os itself you can run one of the following commands. You will need root access for disk image creation/modification.
```sh
ninja qemu
ninja bochs
./script/build.sh qemu
./script/build.sh qemu-nographic
./script/build.sh qemu-debug
./script/build.sh bochs
```
You can also build the kernel or disk image without running it:
```sh
ninja kernel
ninja image
./script/build.sh kernel
./script/build.sh image
```
If you have corrupted your disk image or want to create new one, you can either manually delete *banan-os.img* and cmake will automatically create you a new one or you can run the following command.
If you have corrupted your disk image or want to create new one, you can either manually delete *build/banan-os.img* and build system will automatically create you a new one or you can run the following command.
```sh
ninja image-full
./script/build.sh image-full
```
> ***NOTE*** ```ninja clean``` has to be ran with root permissions, since it deletes the root filesystem.
If you feel like ```./script/build.sh``` is too verbose, there exists a symlink _bos_ in this projects root directory. All build commands can be used with ```./bos args...``` instead.
I have also created shell completion script for zsh. You can either copy the file in _script/shell-completion/zsh/\_bos_ to _/usr/share/zsh/site-functions/_ or add the _script/shell-completion/zsh_ to your fpath in _.zshrc_.
### Contributing
Currently I don't accept contributions to this repository unless explicitly told otherwise. This is a learning project for me and I want to do everything myself. Feel free to fork/clone this repo and tinker with it yourself.
Currently I don't accept contributions to this repository unless explicitly told otherwise. This is a learning project for me and I want to do everything myself. Feel free to fork/clone this repo and tinker with it yourself.

Binary file not shown.

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.26)
add_subdirectory(bios)

View File

@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.26)
project(bootloader ASM)
set(BOOTLOADER_SOURCES
boot.S
command_line.S
disk.S
elf.S
ext2.S
framebuffer.S
memory_map.S
utils.S
)
add_executable(bootloader ${BOOTLOADER_SOURCES})
target_link_options(bootloader PRIVATE LINKER:-T,${CMAKE_CURRENT_SOURCE_DIR}/linker.ld)
target_link_options(bootloader PRIVATE -nostdlib)

170
bootloader/bios/boot.S Normal file
View File

@ -0,0 +1,170 @@
.code16
#########################################
#
# STAGE 1 BOOTLOADER
#
# its sole purpose is to load stage2 from
# bios boot partition
#
#########################################
.section .stage1
.global stage1_main
stage1_main:
# setup segments
movw $0, %ax
movw %ax, %ds
movw %ax, %es
# setup stack
movw %ax, %ss
movl $0x7C00, %esp
# save boot disk number
call read_stage2_into_memory
jmp stage2_main
.global print_and_halt
print_and_halt:
call puts
halt:
hlt
jmp halt
#########################################
#
# STAGE 2 BOOTLOADER
#
#########################################
.section .stage2
stage2_main:
# clear screen and enter 80x25 text mode
movb $0x03, %al
movb $0x00, %ah
int $0x10
# print hello message
movw $hello_msg, %si
call puts; call print_newline
call enter_unreal_mode
movw $unreal_enter_msg, %si
call puts; call print_newline
call get_memory_map
call read_user_command_line
call vesa_find_video_mode
call print_newline
movw $start_kernel_load_msg, %si
call puts; call print_newline
call print_memory_map
call find_root_disk
call find_root_partition
call print_root_partition_info
call print_newline
call has_ext2_filesystem
testb %al, %al
jz print_and_halt
call ext2_find_kernel
movl $ext2_inode_read_bytes, %esi
call elf_read_kernel_to_memory
call vesa_set_target_mode
cli
# setup protected mode
movl %cr0, %ebx
orb $1, %bl
movl %ebx, %cr0
# jump to kernel in protected mode
ljmpl $0x18, $protected_mode
.code32
protected_mode:
movw $0x10, %bx
movw %bx, %ds
movw %bx, %es
movw %bx, %fs
movw %bx, %gs
movw %bx, %ss
movl %eax, %ecx
movl $0xD3C60CFF, %eax
movl $banan_boot_info, %ebx
xorl %edx, %edx
xorl %esi, %esi
xorl %edi, %edi
jmp *%ecx
.code16
enter_unreal_mode:
cli
pushw %ds
lgdt gdtr
movl %cr0, %eax
orb $1, %al
movl %eax, %cr0
ljmpl $0x8, $.enter_unreal_mode_pmode
.enter_unreal_mode_pmode:
movw $0x10, %bx
movw %bx, %ds
andb 0xFE, %al
movl %eax, %cr0
ljmpl $0x0, $.enter_unreal_mode_unreal
.enter_unreal_mode_unreal:
popw %ds
sti
ret
hello_msg:
.asciz "This is banan-os bootloader"
unreal_enter_msg:
.asciz "Entered unreal mode"
start_kernel_load_msg:
.asciz "Starting to load kernel"
gdt:
.quad 0x0000000000000000
.quad 0x00009A000000FFFF
.quad 0x00CF92000000FFFF
.quad 0x00CF9A000000FFFF
gdtr:
.short . - gdt - 1
.quad gdt
banan_boot_info:
boot_command_line:
.long command_line
boot_framebuffer:
.long framebuffer
boot_memory_map:
.long memory_map

View File

@ -0,0 +1,83 @@
.code16
.section .stage2
# fills command line buffer
# NO REGISTERS SAVED
.global read_user_command_line
read_user_command_line:
# print initial command line
movw $command_line_enter_msg, %si
call puts
movw $command_line_buffer, %si
call puts
# prepare registers for input
movw $command_line_enter_msg, %si
movw $command_line_buffer, %di
.read_user_command_line_goto_end:
cmpb $0, (%di)
jz .read_user_command_line_loop
incw %di
jmp .read_user_command_line_goto_end
.read_user_command_line_loop:
call getc
cmpb $'\b', %al
je .read_user_command_line_backspace
# Not sure if some BIOSes return '\n' as enter, but check it just in case
cmpb $'\r', %al
je .read_user_command_line_done
cmpb $'\n', %al
je .read_user_command_line_done
pushw %ax
call isprint
testb %al, %al
jz .read_user_command_line_loop
popw %ax
# put byte to buffer
movb %al, (%di)
incw %di
# print byte
call putc
jmp .read_user_command_line_loop
.read_user_command_line_backspace:
# don't do anything if at the beginning
cmpw $command_line_buffer, %di
je .read_user_command_line_loop
# decrement buffer pointer
decw %di
# erase byte in display
call print_backspace
jmp .read_user_command_line_loop
.read_user_command_line_done:
# null terminate command line
movb $0, (%di)
call print_newline
ret
command_line_enter_msg:
.asciz "cmdline: "
.global command_line
command_line:
# 100 character command line
command_line_buffer:
.ascii "root=/dev/sda2"
.skip 100 - 28

521
bootloader/bios/disk.S Normal file
View File

@ -0,0 +1,521 @@
# FIXME: don't assume 512 byte sectors
.set SECTOR_SIZE_SHIFT, 9
.set SECTOR_SIZE, 1 << SECTOR_SIZE_SHIFT
.code16
.section .stage1
.global stage2_start
.global stage2_end
# check that drive has int13 ext
# dl: drive number
# returns only if drive does have the extension
drive_has_int13_ext:
pusha
movb $0x41, %ah
movw $0x55AA, %bx
int $0x13
jc .drive_has_int13_ext_no_int13_ext
popa
ret
.drive_has_int13_ext_no_int13_ext:
mov $no_int13_ext_msg, %si
jmp print_and_halt
# read sectors from disk
# bx:eax: lba start
# cx: lba count (has to less than 0x80)
# dl: drive number
# ds:di: physical address
# returns only on success
.global read_from_disk
read_from_disk:
pusha
call drive_has_int13_ext
# prepare disk read packet
mov $disk_address_packet, %si
movb $0x10, 0x00(%si) # packet size
movb $0x00, 0x01(%si) # always 0
movw %cx, 0x02(%si) # lba count
movw %di, 0x04(%si) # offset
movw %ds, 0x06(%si) # segment
movl %eax, 0x08(%si) # 32 bit lower lba
movw %bx, 0x0C(%si) # 16 bit upper lba
movw $0, 0x0E(%si) # zero
# issue read command
mov $0x42, %ah
int $0x13
jc .read_from_disk_failed
popa
ret
.read_from_disk_failed:
mov $read_from_disk_msg, %si
jmp print_and_halt
# Reads GPT header into gpt_header buffer
# dl: drive number
# return:
# ax: 1 if has GPT header, 0 otherwise
.global read_gpt_header
read_gpt_header:
pushw %bx
pushw %cx
pushw %di
xorw %bx, %bx
movl $1, %eax
movw $1, %cx
movw $gpt_header, %di
call read_from_disk
xorw %bx, %bx
movw $1, %ax
# check if header starts with 'EFI PART'
cmpl $0x20494645, (gpt_header + 0)
cmovnew %bx, %ax
cmpl $0x54524150, (gpt_header + 4)
cmovnew %bx, %ax
popw %di
popw %cx
popw %bx
ret
# Find bios boot partition from boot drive
# returns:
# bx:eax: first lba
# cx: sector count
find_stage2_partition:
# read boot disk GPT header
movb (boot_disk_number), %dl
call read_gpt_header
testb %al, %al
jz .find_stage2_partition_not_gpt
# eax := entry_count
movl (gpt_header + 80), %eax
test %eax, %eax
jz .find_stage2_partition_not_found
# edx:eax := eax * entry_size
mull (gpt_header + 84)
test %edx, %edx
jnz .find_stage2_partition_too_big_entries
# FIXME: read one entry array section at a time
# sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE
movl %eax, %ecx
shrl $SECTOR_SIZE_SHIFT, %ecx
# start lba
movl (gpt_header + 72), %eax
movw (gpt_header + 76), %bx
movw $gpt_entry_data, %di
movw $bios_boot_guid, %si
movb (boot_disk_number), %dl
call read_from_disk
# NOTE: 'only' 0xFFFF partitions supported,
# although read will fail with more than 0x80
movw (gpt_header + 80), %cx
.find_stage2_partition_loop_gpt_entries:
pushw %cx
movw $16, %cx
call memcmp
popw %cx
testb %al, %al
jnz .find_stage2_partition_found
# add entry size to entry pointer
addw (gpt_header + 84), %di
loop .find_stage2_partition_loop_gpt_entries
# fall through to not found case
.find_stage2_partition_not_found:
movw $no_bios_boot_partition_msg, %si
jmp print_and_halt
.find_stage2_partition_not_gpt:
movw $not_gpt_partition_msg, %si
jmp print_and_halt
.find_stage2_partition_too_big_entries:
movw $too_gpt_big_entries_msg, %si
jmp print_and_halt
.find_stage2_partition_found:
# first lba
movl 32(%di), %eax
movw 36(%di), %bx
# count := last lba - first lba + 1
movl 40(%di), %ecx
subl %eax, %ecx
incl %ecx
ret
# reads stage2 into memory
# dl: boot drive number
# returns only on success
.global read_stage2_into_memory
read_stage2_into_memory:
movb %dl, (boot_disk_number)
# push stage2 sector count
movl $stage2_end, %eax
subl $stage2_start, %eax
addl $(SECTOR_SIZE - 1), %eax
movl $SECTOR_SIZE, %ecx
xorl %edx, %edx
divl %ecx
pushl %eax
call find_stage2_partition
movb (boot_disk_number), %dl
popl %ecx # FIXME: validate that partition has enough sectors
movw $stage2_start, %di
call read_from_disk
ret
# 21686148-6449-6E6F-744E-656564454649
.align 4
bios_boot_guid:
.long 0x21686148 # little endian
.word 0x6449 # little endian
.word 0x6E6F # little endian
.word 0x4E74 # big endian
.quad 0x494645646565 # big endian
boot_disk_number:
.skip 1
read_from_disk_msg:
.asciz "read error"
no_int13_ext_msg:
.asciz "no INT13 ext"
no_bios_boot_partition_msg:
.asciz "no bios boot"
too_gpt_big_entries_msg:
.asciz "too big GPT array"
not_gpt_partition_msg:
.asciz "not GPT"
.section .stage2
# check if drive exists
# dl: drive number
# return:
# al: 1 if disk is usable, 0 otherwise
drive_exists:
pusha
movb $0x48, %ah
movw $disk_drive_parameters, %si
movw $0x1A, (disk_drive_parameters) # set buffer size
int $0x13
jc .drive_exists_nope
popa
movb $1, %al
ret
.drive_exists_nope:
popa
movb $0, %al
ret
# find root disk and populate root_disk_drive_number field
# NO REGISTERS SAVED
.global find_root_disk
find_root_disk:
movb $0x80, %dl
.find_root_disk_loop:
call drive_exists
testb %al, %al
jz .find_root_disk_not_found
# read GPT header
xorw %bx, %bx
movl $1, %eax
movw $1, %cx
movw $gpt_header, %di
call read_from_disk
# confirm header (starts with 'EFI PART')
cmpl $0x20494645, (gpt_header + 0)
jne .find_root_disk_next_disk
cmpl $0x54524150, (gpt_header + 4)
jne .find_root_disk_next_disk
# compare disk GUID
movw $root_disk_guid, %si
movw $(gpt_header + 56), %di
movw $16, %cx
call memcmp
testb %al, %al
jz .find_root_disk_next_disk
movw $root_disk_found_msg, %si
call puts; call print_newline
movb %dl, (root_disk_drive_number)
ret
.find_root_disk_next_disk:
incb %dl
jmp .find_root_disk_loop
.find_root_disk_not_found:
movw $root_disk_not_found_msg, %si
jmp print_and_halt
# finds root partition from root disk
# fills root_partition_entry data structure
# NOTE: assumes GPT header is in `gpt_header`
# NO REGISTERS SAVED
# return:
# dl: drive number
# ecx: sector count (capped at 0xFFFFFFFF)
# bx:eax: first sector
.global find_root_partition
find_root_partition:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
# esp + 0: 8 byte entry array lba
movl (gpt_header + 72), %eax
movl %eax, 0(%esp)
movl (gpt_header + 76), %eax
movl %eax, 4(%esp)
# FIXME: check that bits 48-63 are zero
# esp + 8: 4 byte entries per sector
xorl %edx, %edx
movl $SECTOR_SIZE, %eax
divl (gpt_header + 84)
movl %eax, 8(%esp)
# esp + 12: 4 byte entries remaining
movl (gpt_header + 80), %eax
testl %eax, %eax
jz .find_root_partition_not_found
movl %eax, 12(%esp)
.find_root_partition_read_entry_section:
movl 0(%esp), %eax
movl 4(%esp), %ebx
movw $1, %cx
movb (root_disk_drive_number), %dl
movw $sector_buffer, %di
call read_from_disk
# ecx: min(entries per section, entries remaining)
movl 8(%esp), %ecx
cmpl 12(%esp), %ecx
jae .find_root_partition_got_entry_count
movl 12(%esp), %ecx
.find_root_partition_got_entry_count:
# update entries remaining
subl %ecx, 12(%esp)
# si: entry pointer
movw $sector_buffer, %si
.find_root_partition_loop_entries:
# temporarily save cx in dx
movw %cx, %dx
# check that entry is used
movw $16, %cx
movw $zero_guid, %di
call memcmp
test %al, %al
jnz .find_root_partition_next_entry
# compare entry guid to root guid
movw $16, %cx
addw $16, %si
movw $root_partition_guid, %di
call memcmp
subw $16, %si
testb %al, %al
jnz .find_root_partition_found
.find_root_partition_next_entry:
# restore cx
movw %dx, %cx
# entry pointer += entry size
addw (gpt_header + 84), %si
loop .find_root_partition_loop_entries
# entry not found in this sector
# increment 8 byte entry array lba
incl 0(%esp)
jnc .find_root_partition_no_overflow
incl 4(%esp)
.find_root_partition_no_overflow:
# loop to read next section if entries remaining
cmpl $0, 12(%esp)
jnz .find_root_partition_read_entry_section
.find_root_partition_not_found:
movw $root_partition_not_found_msg, %si
jmp print_and_halt
.find_root_partition_found:
# copy entry to buffer
movw $root_partition_entry, %di
movw $128, %cx
rep movsb
movw $root_partition_found_msg, %si
call puts; call print_newline
# ebx:eax := last lba
movl (root_partition_entry + 44), %ebx
movl (root_partition_entry + 40), %eax
# ebx:eax -= first lba - 1
subl (root_partition_entry + 36), %ebx
movl (root_partition_entry + 32), %ecx;
decl %ecx
subl %ecx, %eax
jnc .find_root_partition_count_sub_no_carry
decl %ebx
.find_root_partition_count_sub_no_carry:
# ecx: min(partition count, 0xFFFFFFFF)
movl $0xFFFFFFFF, %edx
movl %eax, %ecx
testl %ebx, %ebx
cmovnzl %edx, %ecx
# ebx:eax := first lba
# FIXME: confirm ebx bits 16:31 are zero
movl (root_partition_entry + 36), %ebx
movl (root_partition_entry + 32), %eax
movb (root_disk_drive_number), %dl
leavel
ret
# print information about root partition
.global print_root_partition_info
print_root_partition_info:
pushw %ax
pushw %bx
pushw %cx
pushw %si
movw $root_partition_info_start_msg, %si
call puts;
movw $16, %bx
movw $2, %cx
movw (root_partition_entry + 38), %ax; call print_number
movw (root_partition_entry + 36), %ax; call print_number
movw (root_partition_entry + 34), %ax; call print_number
movw (root_partition_entry + 32), %ax; call print_number
movb $'-', %al; call putc
movb $'>', %al; call putc
movw (root_partition_entry + 46), %ax; call print_number
movw (root_partition_entry + 44), %ax; call print_number
movw (root_partition_entry + 42), %ax; call print_number
movw (root_partition_entry + 40), %ax; call print_number
call print_newline
popw %si
popw %cx
popw %bx
popw %ax
ret
# These will be patched during bootloader installation
root_disk_guid:
.ascii "root disk guid "
root_partition_guid:
.ascii "root part guid "
zero_guid:
.skip 16, 0
root_disk_found_msg:
.asciz "Root disk found!"
root_disk_not_found_msg:
.asciz "Root disk not found"
root_partition_found_msg:
.asciz "Root partition found!"
root_partition_not_found_msg:
.asciz "Root partition not found"
root_partition_info_start_msg:
.asciz "Root partition: "
.section .bss
.align SECTOR_SIZE
gpt_header:
.skip SECTOR_SIZE
gpt_entry_data:
.skip SECTOR_SIZE
sector_buffer:
.skip SECTOR_SIZE
disk_address_packet:
.skip 16
disk_drive_parameters:
.skip 0x1A
.skip 2 # padding
root_disk_drive_number:
.skip 1
.skip 3 # padding
root_partition_entry:
.skip 128

222
bootloader/bios/elf.S Normal file
View File

@ -0,0 +1,222 @@
.set SECTOR_SIZE, 512
# file header field offsets
.set e_type, 16
.set e_machine, 18
.set e_version, 20
.set e_entry, 24
.set e_phoff, 32
.set e_shoff, 40
.set e_flags, 48
.set e_ehsize, 52
.set e_phentsize, 54
.set e_phnum, 56
.set e_shentsize, 58
.set e_shnum, 60
.set e_shstrndx, 62
# e_ident offsets
.set EI_CLASS, 4
.set EI_DATA, 5
.set EI_VERSION, 6
# e_ident constants
.set ELFMAGIC, 0x464C457F
.set ELFCLASS64, 2
.set ELFDATA2LSB, 1
.set EV_CURRENT, 1
# e_type constants
.set ET_EXEC, 2
# program header field offsets
.set p_type, 0
.set p_flags, 4
.set p_offset, 8
.set p_vaddr, 16
.set p_paddr, 24
.set p_filesz, 32
.set p_memsz, 40
.set p_align, 48
# p_type constants
.set PT_NULL, 0
.set PT_LOAD, 1
.code16
.section .stage2
# Validate file header stored in elf_file_header
# returns only on success
elf_validate_file_header:
cmpl $ELFMAGIC, (elf_file_header)
jne .elf_validate_file_header_invalid_magic
cmpb $ELFCLASS64, (elf_file_header + EI_CLASS)
jne .elf_validate_file_header_only_64bit_supported
cmpb $ELFDATA2LSB, (elf_file_header + EI_DATA)
jne .elf_validate_file_header_only_little_endian_supported
cmpb $EV_CURRENT, (elf_file_header + EI_VERSION)
jne .elf_validate_file_header_not_current_version
cmpl $EV_CURRENT, (elf_file_header + e_version)
jne .elf_validate_file_header_not_current_version
cmpw $ET_EXEC, (elf_file_header + e_type)
jne .elf_validate_file_header_not_executable
ret
.elf_validate_file_header_invalid_magic:
movw $elf_validate_file_header_invalid_magic_msg, %si
jmp print_and_halt
.elf_validate_file_header_only_64bit_supported:
movw $elf_validate_file_header_only_64bit_supported_msg, %si
jmp print_and_halt
.elf_validate_file_header_only_little_endian_supported:
movw $elf_validate_file_header_only_little_endian_supported_msg, %si
jmp print_and_halt
.elf_validate_file_header_not_current_version:
movw $elf_validate_file_header_not_current_version_msg, %si
jmp print_and_halt
.elf_validate_file_header_not_executable:
movw $elf_validate_file_header_not_executable_msg, %si
jmp print_and_halt
# read callback format
# eax: first byte
# ecx: byte count
# edi: buffer
# returns only on success
# reads kernel to memory
# esi: callback for reading from kernel image
# return:
# eax: kernel entry address
.global elf_read_kernel_to_memory
elf_read_kernel_to_memory:
pushal
pushl %ebp
movl %esp, %ebp
subl $2, %esp
# read file header
movl $0, %eax
movl $64, %ecx
movl $elf_file_header, %edi
call *%esi
call elf_validate_file_header
cmpl $0, (elf_file_header + e_phoff + 4)
jnz .elf_read_kernel_to_memory_unsupported_offset
# current program header
movw $0, -2(%ebp)
.elf_read_kernel_to_memory_loop_program_headers:
movw -2(%ebp), %cx
cmpw (elf_file_header + e_phnum), %cx
jae .elf_read_kernel_to_memory_done
# eax := program_header_index * e_phentsize + e_phoff
xorl %eax, %eax
movw %cx, %ax
xorl %ebx, %ebx
movw (elf_file_header + e_phentsize), %bx
mull %ebx
addl (elf_file_header + e_phoff), %eax
jc .elf_read_kernel_to_memory_unsupported_offset
# setup program header size and address
movl $56, %ecx
movl $elf_program_header, %edi
# read the program header
call *%esi
# test if program header is empty
cmpl $PT_NULL, (elf_program_header + p_type)
je .elf_read_kernel_to_memory_null_program_header
# confirm that the program header is loadable
cmpl $PT_LOAD, (elf_program_header + p_type)
jne .elf_read_kernel_to_memory_not_loadable_header
# memset p_filesz -> p_memsz to 0
movl (elf_program_header + p_filesz), %ebx
movl (elf_program_header + p_vaddr), %edi
andl $0x7FFFFFFF, %edi
addl %ebx, %edi
movl (elf_program_header + p_memsz), %ecx
subl %ebx, %ecx
jz .elf_read_kernel_to_memory_memset_done
.elf_read_kernel_to_memory_memset:
movb $0, (%edi)
incl %edi
decl %ecx
jnz .elf_read_kernel_to_memory_memset
.elf_read_kernel_to_memory_memset_done:
# read file specified in program header to memory
movl (elf_program_header + p_offset), %eax
movl (elf_program_header + p_vaddr), %edi
andl $0x7FFFFFFF, %edi
movl (elf_program_header + p_filesz), %ecx
#call print_hex32; call print_newline
call *%esi
.elf_read_kernel_to_memory_null_program_header:
incw -2(%ebp)
jmp .elf_read_kernel_to_memory_loop_program_headers
.elf_read_kernel_to_memory_done:
leavel
popal
# set kernel entry address
movl (elf_file_header + e_entry), %eax
andl $0x7FFFFF, %eax
ret
.elf_read_kernel_to_memory_unsupported_offset:
movw $elf_read_kernel_to_memory_unsupported_offset_msg, %si
jmp print_and_halt
.elf_read_kernel_to_memory_not_loadable_header:
movw $elf_read_kernel_to_memory_not_loadable_header_msg, %si
jmp print_and_halt
elf_validate_file_header_invalid_magic_msg:
.asciz "ELF: file has invalid ELF magic"
elf_validate_file_header_only_64bit_supported_msg:
.asciz "ELF: file is not targettint 64 bit"
elf_validate_file_header_only_little_endian_supported_msg:
.asciz "ELF: file is not in little endian format"
elf_validate_file_header_not_current_version_msg:
.asciz "ELF: file is not in current ELF version"
elf_validate_file_header_not_executable_msg:
.asciz "ELF: file is not an executable"
elf_read_kernel_to_memory_unsupported_offset_msg:
.asciz "ELF: unsupported offset (only 32 bit offsets supported)"
elf_read_kernel_to_memory_not_loadable_header_msg:
.asciz "ELF: kernel contains non-loadable program header"
.section .bss
elf_file_header:
.skip 64
elf_program_header:
.skip 56

705
bootloader/bios/ext2.S Normal file
View File

@ -0,0 +1,705 @@
# FIXME: don't assume 512 byte sectors
.set SECTOR_SHIFT, 9
.set SECTOR_SIZE, 1 << SECTOR_SHIFT
# FIXME: don't assume 1024 byte blocks
.set EXT2_BLOCK_SHIFT, 10
.set EXT2_BLOCK_SIZE, 1 << EXT2_BLOCK_SHIFT
.set EXT2_SUPERBLOCK_SIZE, 264
.set EXT2_BGD_SHIFT, 5
.set EXT2_BGD_SIZE, 1 << EXT2_BGD_SHIFT
.set EXT2_INODE_SIZE_MAX, 256
.set EXT2_ROOT_INO, 2
.set EXT2_GOOD_OLD_REV, 0
# inode types
.set EXT2_S_IMASK, 0xF000
.set EXT2_S_IFDIR, 0x4000
.set EXT2_S_IFREG, 0x8000
# superblock offsets
.set s_log_block_size, 24
.set s_inodes_per_group, 40
.set s_magic, 56
.set s_rev_level, 76
.set s_inode_size, 88
# block group descriptor offsets
.set bg_inode_table, 8
# inode offsets
.set i_mode, 0
.set i_size, 4
.set i_block, 40
.code16
.section .stage2
# checks whether partition contains ext2 filesystem.
# fills ext2_superblock_buffer
# dl: drive number
# ecx: sector count
# bx:eax: first sector
# return:
# al: 1 if is ext2, 0 otherwise
# si: error message on error
.global has_ext2_filesystem
has_ext2_filesystem:
pushl %ecx
pushw %bx
pushw %di
# fill ext2_partition_first_sector
movw $0, (ext2_partition_first_sector + 6)
movw %bx, (ext2_partition_first_sector + 4)
movl %eax, (ext2_partition_first_sector + 0)
# fill ext2_drive_number
movb %dl, (ext2_drive_number)
cmpl $3, %ecx
jb .has_ext2_filesystem_does_not_fit
# one sector
movw $1, %cx
# from byte offset 1024
addl $(1024 / SECTOR_SIZE), %eax
jnc .has_ext2_filesystem_no_overflow
incw %bx
.has_ext2_filesystem_no_overflow:
# into sector buffer
movw $ext2_block_buffer, %di
call read_from_disk
# copy superblock to its buffer
movw $ext2_block_buffer, %si
movw $ext2_superblock_buffer, %di
movw $EXT2_SUPERBLOCK_SIZE, %cx
rep movsb
# verify magic
cmpw $0xEF53, (ext2_superblock_buffer + s_magic)
jne .has_ext2_filesystem_invalid_magic
# verify block size
# verify shift fits in one byte
movl (ext2_superblock_buffer + s_log_block_size), %ecx
testl $0xFFFFFF00, %ecx
jnz .has_ext2_filesystem_unsupported_block_size
# verify 1024 << s_log_block_size == EXT2_BLOCK_SIZE
movl $1024, %eax
shll %cl, %eax
cmpl $EXT2_BLOCK_SIZE, %eax
jne .has_ext2_filesystem_unsupported_block_size
# fill inode size
movl $128, %eax
cmpl $EXT2_GOOD_OLD_REV, (ext2_superblock_buffer + s_rev_level)
cmovnel (ext2_superblock_buffer + s_inode_size), %eax
movl %eax, (ext2_inode_size)
movb $1, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_does_not_fit:
movw $root_partition_does_not_fit_ext2_filesystem_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_invalid_magic:
movw $root_partition_has_invalid_ext2_magic_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_unsupported_block_size:
movw $root_partition_has_unsupported_ext2_block_size_msg, %si
movb $0, %al
jmp .has_ext2_filesystem_done
.has_ext2_filesystem_done:
popw %di
popw %bx
popl %ecx
ret
# reads block in to ext2_block_buffer
# eax: block number
ext2_read_block:
pushl %eax
pushl %ebx
pushw %cx
pushl %edx
pushw %di
# NOTE: this assumes 1024 block size
# eax := (block * block_size) / sector_size := (eax << EXT2_BLOCK_SHIFT) >> SECTOR_SHIFT
xorl %edx, %edx
shll $EXT2_BLOCK_SHIFT, %eax
shrl $SECTOR_SHIFT, %eax
# ebx:eax := eax + (ext2_partition_first_sector)
movl (ext2_partition_first_sector + 4), %ebx
addl (ext2_partition_first_sector + 0), %eax
jnc .ext2_read_block_no_carry
incl %ebx
.ext2_read_block_no_carry:
# sectors per block
movw $(EXT2_BLOCK_SIZE / SECTOR_SIZE), %cx
movw $ext2_block_buffer, %di
movb (ext2_drive_number), %dl
call read_from_disk
popw %di
popl %edx
popw %cx
popl %ebx
popl %eax
ret
# reads block group descrtiptor into ext2_block_group_descriptor
# eax: block group
ext2_read_block_group_descriptor:
pushal
# eax := bgd_byte_offset := 2048 + EXT2_BGD_SIZE * eax := (eax << EXT2_BGD_SHIFT) + 2048
shll $EXT2_BGD_SHIFT, %eax
addl $2048, %eax
# eax: bgd_block := bgd_byte_offset / EXT2_BLOCK_SIZE
# ebx: bgd_offset := bgd_byte_offset % EXT2_BLOCK_SIZE
xorl %edx, %edx
movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
movl %edx, %ebx
call ext2_read_block
# esi := &ext2_block_buffer + bgd_offset := ebx + &ext2_block_buffer
# edi := &ext2_block_group_descriptor_buffer
movl %ebx, %esi
addl $ext2_block_buffer, %esi
movl $ext2_block_group_descriptor_buffer, %edi
movw $EXT2_BGD_SIZE, %cx
rep movsb
popal
ret
# reads inode into ext2_inode_buffer
# eax: ino
ext2_read_inode:
pushal
# eax := block_group = (ino - 1) / s_inodes_per_group
# ebx := inode_index = (ino - 1) % s_inodes_per_group
xorl %edx, %edx
decl %eax
movl (ext2_superblock_buffer + s_inodes_per_group), %ebx
divl %ebx
movl %edx, %ebx
call ext2_read_block_group_descriptor
# eax := inode_table_block := (inode_index * inode_size) / EXT2_BLOCK_SIZE
# ebx := inode_table_offset := (inode_index * inode_size) % EXT2_BLOCK_SIZE
xorl %edx, %edx
movl %ebx, %eax
movl (ext2_inode_size), %ebx
mull %ebx
movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
movl %edx, %ebx
# eax := file system block := eax + bg_inode_table
addl (ext2_block_group_descriptor_buffer + bg_inode_table), %eax
movb (ext2_drive_number), %dl
call ext2_read_block
# copy inode memory
# esi := inode_table_offset + ext2_block_buffer := edx + ext2_block_buffer
movl %ebx, %esi
addl $ext2_block_buffer, %esi
# edi := ext2_inode_buffer
movl $ext2_inode_buffer, %edi
# ecx := inode_size
movl (ext2_inode_size), %ecx
rep movsb
popal
ret
# gets block index from n'th data block in inode stored in ext2_inode_buffer
# eax: data block index
# return:
# eax: block index
ext2_data_block_index:
pushl %ebx
pushl %ecx
pushl %edx
pushl %esi
# calculate max data blocks
movl (ext2_inode_buffer + i_size), %ecx
addl (ext2_inode_size), %ecx
decl %ecx
shll $EXT2_BLOCK_SHIFT, %ecx
# verify data block is within bounds
cmpl %ecx, %eax
jae .ext2_data_block_index_out_of_bounds
# check if this is direct block access
cmpl $12, %eax
jb .ext2_data_block_index_direct
subl $12, %eax
# check if this is singly indirect block access
cmpl $(EXT2_BLOCK_SIZE / 4), %eax
jb .ext2_data_block_index_singly_indirect
subl $(EXT2_BLOCK_SIZE / 4), %eax
# check if this is doubly indirect block access
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_doubly_indirect
subl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
# check if this is triply indirect block access
cmpl $((EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4) * (EXT2_BLOCK_SIZE / 4)), %eax
jb .ext2_data_block_index_triply_indirect
# otherwise this is invalid access
jmp .ext2_data_block_index_invalid
.ext2_data_block_index_direct:
movl $(ext2_inode_buffer + i_block), %esi
movl (%esi, %eax, 4), %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_singly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 12 * 4), %eax
movw $1, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_doubly_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 13 * 4), %eax
movw $2, %cx
jmp .ext2_data_block_index_indirect
.ext2_data_block_index_triply_indirect:
movl %eax, %ebx
movl (ext2_inode_buffer + i_block + 14 * 4), %eax
movw $3, %cx
jmp .ext2_data_block_index_indirect
# eax := current block
# ebx := index
# cx := depth
.ext2_data_block_index_indirect:
call ext2_read_block
# store depth and index
pushw %cx
pushl %ebx
cmpw $1, %cx
jbe .ext2_data_block_index_no_shift
# cl := shift
movb $(EXT2_BLOCK_SHIFT - 2), %al
decb %cl
mulb %cl
movb %al, %cl
# ebx := ebx >> cl
shrl %cl, %ebx
.ext2_data_block_index_no_shift:
# edx := index of next block
movl %ebx, %eax
xorl %edx, %edx
movl $(EXT2_BLOCK_SIZE / 4), %ebx
divl %ebx
# eax := next block
movl $ext2_block_buffer, %esi
movl (%esi, %edx, 4), %eax
# restore depth and index
popl %ebx
popw %cx
loop .ext2_data_block_index_indirect
jmp .ext2_data_block_index_done
.ext2_data_block_index_out_of_bounds:
movw $ext2_data_block_index_out_of_bounds_msg, %si
call puts; call print_newline
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_invalid:
movw $ext2_data_block_index_invalid_msg, %si
call puts; call print_newline
movl $0, %eax
jmp .ext2_data_block_index_done
.ext2_data_block_index_done:
popl %esi
popl %edx
popl %ecx
popl %ebx
ret
# read bytes from inode (implements read callback)
# eax: first byte
# ecx: byte count
# edi: buffer
# returns only on success
.global ext2_inode_read_bytes
ext2_inode_read_bytes:
pushal
pushl %ebp
movl %esp, %ebp
subl $8, %esp
# save read info
movl %eax, 0(%esp)
movl %ecx, 4(%esp)
# check if eax % EXT2_BLOCK_SIZE != 0,
# then we need to read a partial block starting from an offset
xorl %edx, %edx
movl $EXT2_BLOCK_SIZE, %ebx
divl %ebx
testl %edx, %edx
jz .ext2_inode_read_bytes_no_partial_start
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# ecx := byte count (min(block_size - edx, remaining_bytes))
movl $EXT2_BLOCK_SIZE, %ecx
subl %edx, %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
# esi := start sector data (block_buffer + index * SECTOR_SIZE)
movl $ext2_block_buffer, %esi
addl %edx, %esi
# very dumb memcpy with 32 bit addresses
movl $0, %ebx
.ext2_inode_read_bytes_memcpy_partial:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy_partial
addl %ebx, %edi
# check if all sectors are read
cmpl $0, 4(%esp)
je .ext2_inode_read_bytes_done
.ext2_inode_read_bytes_no_partial_start:
# eax := data block index (byte_start / block_size)
movl 0(%esp), %eax
shrl $(EXT2_BLOCK_SHIFT), %eax
# get data block index and read block
call ext2_data_block_index
call ext2_read_block
# calculate bytes to copy (min(block_size, remaining_bytes))
movl $EXT2_BLOCK_SIZE, %ecx
cmpl %ecx, 4(%esp)
cmovbl 4(%esp), %ecx
# update remaining read info
addl %ecx, 0(%esp)
subl %ecx, 4(%esp)
# very dumb memcpy with 32 bit addresses
movl $ext2_block_buffer, %esi
movl $0, %ebx
.ext2_inode_read_bytes_memcpy:
movb (%esi, %ebx), %al
movb %al, (%edi, %ebx)
incl %ebx
decl %ecx
jnz .ext2_inode_read_bytes_memcpy
addl %ebx, %edi
# read next block if more sectors remaining
cmpl $0, 4(%esp)
jnz .ext2_inode_read_bytes_no_partial_start
.ext2_inode_read_bytes_done:
leavel
popal
ret
# find inode in inside directory inode stored in ext2_inode_buffer
# store the found inode in ext2_inode_buffer
# si: name string
# cx: name length
# return:
# eax: ino if inode was found, 0 otherwise
ext2_directory_find_inode:
pushl %ebx
pushw %cx
pushw %dx
pushw %si
pushw %di
pushl %ebp
movl %esp, %ebp
subl $8, %esp
# 0(%esp) := name length
movw %cx, 0(%esp)
# 2(%esp) := name string
movw %si, 2(%esp)
# verify that the name is <= 0xFF bytes
cmpw $0xFF, %cx
ja .ext2_directory_find_inode_not_found
# ebx := max data blocks: ceil(i_size / EXT2_BLOCK_SIZE)
movl (ext2_inode_buffer + i_size), %ebx
addl $EXT2_BLOCK_SHIFT, %ebx
decl %ebx
shrl $EXT2_BLOCK_SHIFT, %ebx
jz .ext2_directory_find_inode_not_found
# 4(%esp) := current block
movl $0, 4(%esp)
.ext2_directory_find_inode_block_read_loop:
# get next block index
movl 4(%esp), %eax
call ext2_data_block_index
test %eax, %eax
jz .ext2_directory_find_inode_next_block
# read current block
call ext2_read_block
# dx := current entry pointer
movw $ext2_block_buffer, %si
.ext2_directory_find_inode_loop_entries:
# temporarily store entry pointer in dx
movw %si, %dx
# check if name length matches
# cx := name length
movw 0(%esp), %cx
cmpb 6(%si), %cl
jne .ext2_directory_find_inode_next_entry
# si := entry name
addw $8, %si
# di := asked name
movw 2(%esp), %di
# check if name matches
call memcmp
test %al, %al
# NOTE: dx contains entry pointer
jnz .ext2_directory_find_inode_found
.ext2_directory_find_inode_next_entry:
# restore si
movw %dx, %si
# go to next entry if this block contains one
addw 4(%si), %si
cmpw $(ext2_block_buffer + EXT2_BLOCK_SIZE), %si
jb .ext2_directory_find_inode_loop_entries
.ext2_directory_find_inode_next_block:
incl 4(%esp)
cmpl %ebx, 4(%esp)
jb .ext2_directory_find_inode_block_read_loop
.ext2_directory_find_inode_not_found:
movb $0, %al
jmp .ext2_directory_find_inode_done
.ext2_directory_find_inode_found:
# extract ino and read it to ext2_inode_buffer
movw %dx, %si
movl 0(%si), %eax
call ext2_read_inode
.ext2_directory_find_inode_done:
leavel
popw %di
popw %si
popw %dx
popw %cx
popl %ebx
ret
# search for kernel file from filesystem
# returns only on success
.global ext2_find_kernel
ext2_find_kernel:
pushl %eax
pushw %cx
pushw %di
pushw %si
movl $EXT2_ROOT_INO, %eax
call ext2_read_inode
movw $kernel_path, %di
.ext2_find_kernel_loop:
movw (%di), %si
# check if this list is done
testw %si, %si
jz .ext2_find_kernel_loop_done
# check that current part is directory
movw (ext2_inode_buffer + i_mode), %ax
andw $EXT2_S_IMASK, %ax
cmpw $EXT2_S_IFDIR, %ax
jne .ext2_find_kernel_part_not_dir
# prepare registers for directory finding
movw 0(%si), %cx
addw $2, %si
# print search path
pushw %si
movw $ext2_looking_for_msg, %si
call puts
popw %si
call puts; call print_newline
# search current directory for this file
call ext2_directory_find_inode
testl %eax, %eax
jz .ext2_find_kernel_part_not_found
# loop to next part
addw $2, %di
jmp .ext2_find_kernel_loop
.ext2_find_kernel_loop_done:
# check that kernel is a regular file
movw (ext2_inode_buffer + i_mode), %ax
andw $EXT2_S_IMASK, %ax
cmpw $EXT2_S_IFREG, %ax
jne .ext2_find_kernel_not_reg
movw $ext2_kernel_found_msg, %si
call puts; call print_newline
popw %si
popw %di
popw %cx
popl %eax
ret
.ext2_find_kernel_part_not_dir:
movw $ext2_part_not_dir_msg, %si
jmp print_and_halt
.ext2_find_kernel_part_not_found:
movw $ext2_part_not_found_msg, %si
jmp print_and_halt
.ext2_find_kernel_not_reg:
movw $ext2_kernel_not_reg_msg, %si
jmp print_and_halt
kernel_path:
.short kernel_path1
.short kernel_path2
.short 0
kernel_path1:
.short 4
.asciz "boot"
kernel_path2:
.short 15
.asciz "banan-os.kernel"
root_partition_does_not_fit_ext2_filesystem_msg:
.asciz "Root partition is too small to contain ext2 filesystem"
root_partition_has_invalid_ext2_magic_msg:
.asciz "Root partition doesn't contain ext2 magic number"
root_partition_has_unsupported_ext2_block_size_msg:
.asciz "Root partition has unsupported ext2 block size (only 1024 supported)"
ext2_part_not_dir_msg:
.asciz "inode in root path is not directory"
ext2_part_not_found_msg:
.asciz " not found"
ext2_kernel_not_reg_msg:
.asciz "kernel is not a regular file"
ext2_kernel_found_msg:
.asciz "kernel found!"
ext2_data_block_index_out_of_bounds_msg:
.asciz "data block index out of bounds"
ext2_data_block_index_invalid_msg:
.asciz "data block index is invalid"
ext2_looking_for_msg:
.asciz "looking for "
.section .bss
ext2_block_buffer:
.skip EXT2_BLOCK_SIZE
ext2_partition_first_sector:
.skip 8
ext2_drive_number:
.skip 1
.skip 3 # padding
# NOTE: fits in 2 bytes
ext2_inode_size:
.skip 4
ext2_superblock_buffer:
.skip EXT2_SUPERBLOCK_SIZE
ext2_block_group_descriptor_buffer:
.skip EXT2_BGD_SIZE
ext2_inode_buffer:
.skip EXT2_INODE_SIZE_MAX

View File

@ -0,0 +1,156 @@
.set TARGET_WIDTH, 800
.set TARGET_HEIGHT, 600
.set TARGET_BPP, 32
.code16
.section .stage2
# Find suitable video mode
# return:
# ax: video mode number if found, 0 otherwise
.global vesa_find_video_mode
vesa_find_video_mode:
pushw %ax
pushw %cx
pushw %di
pushl %esi
# clear target mode and frame buffer
movw $0, (vesa_target_mode)
movl $0, (framebuffer + 0)
movl $0, (framebuffer + 4)
movl $0, (framebuffer + 8)
movl $0, (framebuffer + 12)
movw $0, (framebuffer + 16)
# get vesa information
movw $0x4F00, %ax
movw $vesa_info_buffer, %di
int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# confirm that response starts with 'VESA'
cmpl $0x41534556, (vesa_info_buffer)
jne .vesa_error
# confirm that version is atleast 2.0
cmpw $0x0200, (vesa_info_buffer + 0x04)
jb .vesa_unsupported_version
movl $(vesa_info_buffer + 0x0E), %esi
movl (%esi), %esi
.vesa_find_video_mode_loop_modes:
cmpw $0xFFFF, (%esi)
je .vesa_find_video_mode_loop_modes_done
# get info of next mode
movw $0x4F01, %ax
movw (%esi), %cx
movw $vesa_mode_info_buffer, %di
int $0x10
cmpb $0x4F, %al; jne .vesa_unsupported
cmpb $0x00, %ah; jne .vesa_error
# check whether in graphics mode
testb $0x10, (vesa_mode_info_buffer + 0)
jz .vesa_find_video_mode_next_mode
# compare mode's dimensions
cmpw $TARGET_WIDTH, (vesa_mode_info_buffer + 0x12)
jne .vesa_find_video_mode_next_mode
cmpw $TARGET_HEIGHT, (vesa_mode_info_buffer + 0x14)
jne .vesa_find_video_mode_next_mode
cmpb $TARGET_BPP, (vesa_mode_info_buffer + 0x19)
jne .vesa_find_video_mode_next_mode
movl (vesa_mode_info_buffer + 0x28), %esi
movl %esi, (framebuffer + 0)
movw (vesa_mode_info_buffer + 0x10), %ax
movw %ax, (framebuffer + 4)
movl $TARGET_WIDTH, (framebuffer + 8)
movl $TARGET_HEIGHT, (framebuffer + 12)
movb $TARGET_BPP, (framebuffer + 16)
movb $1, (framebuffer + 17)
movw %cx, (vesa_target_mode)
jmp .vesa_find_video_mode_loop_modes_done
.vesa_find_video_mode_next_mode:
addl $2, %esi
jmp .vesa_find_video_mode_loop_modes
.vesa_find_video_mode_loop_modes_done:
popl %esi
popw %di
popw %cx
popw %ax
ret
.vesa_unsupported:
movw $vesa_unsupported_msg, %si
jmp print_and_halt
.vesa_unsupported_version:
movw $vesa_unsupported_version_msg, %si
jmp print_and_halt
.vesa_error:
movw $vesa_error_msg, %si
jmp print_and_halt
# set mode found from vesa_find_video_mode. if no mode
# was found, set it to 80x25 text mode to clear the screen.
.global vesa_set_target_mode
vesa_set_target_mode:
pushw %ax
pushw %bx
movw (vesa_target_mode), %bx
testw %bx, %bx
jz .vesa_set_target_mode_generic
movw $0x4F02, %ax
orw $0x4000, %bx
int $0x10
jmp .set_video_done
.vesa_set_target_mode_generic:
movb $0x03, %al
movb $0x00, %ah
int $0x10
.set_video_done:
popw %bx
popw %ax
ret
vesa_error_msg:
.asciz "VESA error"
vesa_unsupported_msg:
.asciz "VESA unsupported"
vesa_unsupported_version_msg:
.asciz "VESA unsupported version"
vesa_success_msg:
.asciz "VESA success"
.section .bss
vesa_info_buffer:
.skip 512
vesa_mode_info_buffer:
.skip 256
vesa_target_mode:
.skip 2
.global framebuffer
framebuffer:
.skip 4 # address
.skip 4 # pitch
.skip 4 # width
.skip 4 # height
.skip 1 # bpp
.skip 1 # type

15
bootloader/bios/linker.ld Normal file
View File

@ -0,0 +1,15 @@
ENTRY(stage1_main)
SECTIONS
{
. = 0x7C00;
.stage1 : { *(.stage1) }
. = ALIGN(512);
stage2_start = .;
.stage2 : { *(.stage2) }
stage2_end = .;
. = ALIGN(512);
.bss : { *(.bss) }
}

View File

@ -0,0 +1,131 @@
.code16
.section .stage2
# fills memory map data structure
# doesn't return on error
# NO REGISTERS SAVED
.global get_memory_map
get_memory_map:
movl $0, (memory_map_entry_count)
movl $0x0000E820, %eax
movl $0x534D4150, %edx
xorl %ebx, %ebx
movl $20, %ecx
movw $memory_map_entries, %di
clc
int $0x15
# If first call returs with CF set, the call failed
jc .get_memory_map_error
.get_memory_map_rest:
cmpl $0x534D4150, %eax
jne .get_memory_map_error
# FIXME: don't assume BIOS to always return 20 bytes
cmpl $20, %ecx
jne .get_memory_map_error
# increment entry count
incl (memory_map_entry_count)
# increment entry pointer
addw %cx, %di
# BIOS can indicate end of list by 0 in ebx
testl %ebx, %ebx
jz .get_memory_map_done
movl $0x0000E820, %eax
movl $0x534D4150, %edx
clc
int $0x15
# BIOS can indicate end of list by setting CF
jnc .get_memory_map_rest
.get_memory_map_done:
ret
.get_memory_map_error:
movw $memory_map_error_msg, %si
jmp print_and_halt
# print memory map from memory_map_entries
# NO REGISTERS SAVED
.global print_memory_map
print_memory_map:
movw $memory_map_msg, %si
call puts
call print_newline
movl (memory_map_entry_count), %edx
movw $memory_map_entries, %si
movw $16, %bx
movw $4, %cx
.loop_memory_map:
movb $' ', %al
call putc; call putc; call putc; call putc
movw 0x06(%si), %ax
call print_number
movw 0x04(%si), %ax
call print_number
movw 0x02(%si), %ax
call print_number
movw 0x00(%si), %ax
call print_number
movb $',', %al
call putc
movb $' ', %al
call putc
movw 0x0E(%si), %ax
call print_number
movw 0x0C(%si), %ax
call print_number
movw 0x0A(%si), %ax
call print_number
movw 0x08(%si), %ax
call print_number
movb $',', %al
call putc
movb $' ', %al
call putc
movw 0x12(%si), %ax
call print_number
movw 0x10(%si), %ax
call print_number
call print_newline
addw $20, %si
decl %edx
jnz .loop_memory_map
ret
memory_map_msg:
.asciz "memmap:"
memory_map_error_msg:
.asciz "Failed to get memory map"
.section .bss
.global memory_map
memory_map:
memory_map_entry_count:
.skip 4
# 100 entries should be enough...
memory_map_entries:
.skip 20 * 100

280
bootloader/bios/utils.S Normal file
View File

@ -0,0 +1,280 @@
.set SCREEN_WIDTH, 80
.set SCREEN_HEIGHT, 25
.code16
.section .stage1
# prints character to screen
# al: ascii character to print
.global putc
putc:
pushw %ax
pushw %bx
movb $0x0E, %ah
xorb %bh, %bh
int $0x10
popw %bx
popw %ax
ret
# prints null terminated string to screen
# ds:si: string address
.global puts
puts:
pushw %si
pushw %bx
pushw %ax
movb $0x0E, %ah
xorb %bh, %bh
.puts_loop:
lodsb
test %al, %al
jz .puts_done
int $0x10
jmp .puts_loop
.puts_done:
popw %ax
popw %bx
popw %si
ret
# compares memory between addresses
# si: ptr1
# di: ptr2
# cx: bytes count
# return:
# al: 1 if equal, 0 otherwise
.global memcmp
memcmp:
# NOTE: using pusha + popa to save space
pusha
cld
repe cmpsb
popa
setzb %al
ret
.section .stage2
# read a character from keyboard
# return:
# al: ascii
# ah: bios scan code
.global getc
getc:
movb $0x00, %ah
int $0x16
ret
# prints newline to screen
.global print_newline
print_newline:
pushw %ax
movb $'\r', %al
call putc
movb $'\n', %al
call putc
pop %ax
ret
# prints backspace to screen, can go back a line
.global print_backspace
print_backspace:
pushw %ax
pushw %bx
pushw %cx
pushw %dx
# get cursor position
movb $0x03, %ah
movb $0x00, %bh
int $0x10
# don't do anyting if on first row
testb %dh, %dh
jz .print_backspace_done
# go one line up if on first column
test %dl, %dl
jz .print_backspace_go_line_up
# otherwise decrease column
decb %dl
jmp .print_backspace_do_print
.print_backspace_go_line_up:
# decrease row and set column to the last one
decb %dh
movb $(SCREEN_WIDTH - 1), %dl
.print_backspace_do_print:
# set cursor position
movb $0x02, %ah
int $0x10
# print 'empty' character (space)
mov $' ', %al
call putc
# set cursor position
movb $0x02, %ah
int $0x10
.print_backspace_done:
popw %dx
popw %cx
popw %bx
popw %ax
ret
# print number to screen
# ax: number to print
# bx: number base
# cx: min width (zero pads if shorter)
.global print_number
print_number:
pusha
pushl %ebp
movl %esp, %ebp
# save min width
subl $4, %esp
movw %cx, (%esp)
movw $print_number_buffer, %si
xorw %cx, %cx
.print_number_fill_loop:
# fill buffer with all remainders ax % bx
xorw %dx, %dx
divw %bx
movb %dl, (%si)
incw %si
incw %cx
testw %ax, %ax
jnz .print_number_fill_loop
# check if zero pad is required
cmpw (%esp), %cx
jae .print_number_print_loop
# dx: saved number count
# cx: zero pad count
movw %cx, %dx
movw (%esp), %cx
subw %dx, %cx
movb $'0', %al
.print_number_pad_zeroes:
call putc
loop .print_number_pad_zeroes
# restore number count
movw %dx, %cx
.print_number_print_loop:
decw %si
movb (%si), %al
cmpb $10, %al
jae .print_number_hex
addb $'0', %al
jmp .print_number_do_print
.print_number_hex:
addb $('a' - 10), %al
.print_number_do_print:
call putc
loop .print_number_print_loop
leavel
popa
ret
# prints 8 bit hexadecimal number to screen
# al: number to print
.global print_hex8
print_hex8:
pushw %ax
pushw %bx
pushw %cx
movw $16, %bx
movw $2, %cx
andw $0xFF, %ax
call print_number
popw %cx
popw %bx
popw %ax
ret
# prints 16 bit hexadecimal number to screen
# ax: number to print
.global print_hex16
print_hex16:
pushw %bx
pushw %cx
movw $16, %bx
movw $4, %cx
call print_number
popw %cx
popw %bx
ret
# prints 32 bit hexadecimal number to screen
# eax: number to print
.global print_hex32
print_hex32:
pushl %eax
pushw %dx
movw %ax, %dx
shrl $16, %eax;
call print_hex16
movw %dx, %ax
call print_hex16
popw %dx
popl %eax
ret
# prints 64 bit hexadecimal number to screen
# edx:eax: number to print
.global print_hex64
print_hex64:
xchgl %eax, %edx
call print_hex32
xchgl %eax, %edx
call print_hex32
ret
# test if character is printable ascii
# al: character to test
# return:
# al: 1 if is printable, 0 otherwise
.global isprint
isprint:
subb $0x20, %al
cmpb $(0x7E - 0x20), %al
ja .isprint_not_printable
movb $1, %al
ret
.isprint_not_printable:
movb $0, %al
ret
.section .bss
# enough for base 2 printing
print_number_buffer:
.skip 16

1
bootloader/installer/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.26)
project(x86_64-banan_os-bootloader-installer CXX)
set(SOURCES
crc32.cpp
ELF.cpp
GPT.cpp
GUID.cpp
main.cpp
)
add_executable(x86_64-banan_os-bootloader-installer ${SOURCES})
target_compile_options(x86_64-banan_os-bootloader-installer PRIVATE -O2 -std=c++20)
target_compile_definitions(x86_64-banan_os-bootloader-installer PRIVATE __arch=x86_64)
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../LibELF/include)
target_include_directories(x86_64-banan_os-bootloader-installer PRIVATE ${CMAKE_SOURCE_DIR}/../../kernel/include)

View File

@ -0,0 +1,142 @@
#include "ELF.h"
#include <LibELF/Values.h>
#include <cassert>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
using namespace LibELF;
ELFFile::ELFFile(std::string_view path)
: m_path(path)
{
m_fd = open(m_path.c_str(), O_RDONLY);
if (m_fd == -1)
{
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
if (fstat(m_fd, &m_stat) == -1)
{
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ, MAP_PRIVATE, m_fd, 0);
if (mmap_addr == MAP_FAILED)
{
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
if (!validate_elf_header())
return;
m_success = true;
}
ELFFile::~ELFFile()
{
if (m_mmap)
munmap(m_mmap, m_stat.st_size);
m_mmap = nullptr;
if (m_fd != -1)
close(m_fd);
m_fd = -1;
}
const ElfNativeFileHeader& ELFFile::elf_header() const
{
return *reinterpret_cast<LibELF::ElfNativeFileHeader*>(m_mmap);
}
bool ELFFile::validate_elf_header() const
{
if (m_stat.st_size < sizeof(ElfNativeFileHeader))
{
std::cerr << m_path << " is too small to be a ELF executable" << std::endl;
return false;
}
const auto& elf_header = this->elf_header();
if (
elf_header.e_ident[EI_MAG0] != ELFMAG0 ||
elf_header.e_ident[EI_MAG1] != ELFMAG1 ||
elf_header.e_ident[EI_MAG2] != ELFMAG2 ||
elf_header.e_ident[EI_MAG3] != ELFMAG3
)
{
std::cerr << m_path << " doesn't have an ELF magic number" << std::endl;
return false;
}
#if ARCH(x86_64)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS64)
#elif ARCH(i386)
if (elf_header.e_ident[EI_CLASS] != ELFCLASS32)
#endif
{
std::cerr << m_path << " architecture doesn't match" << std::endl;
return false;
}
if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB)
{
std::cerr << m_path << " is not in little endian format" << std::endl;
return false;
}
if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
{
std::cerr << m_path << " has unsupported version" << std::endl;
return false;
}
if (elf_header.e_type != ET_EXEC)
{
std::cerr << m_path << " is not an executable ELF file" << std::endl;
return false;
}
return true;
}
const ElfNativeSectionHeader& ELFFile::section_header(std::size_t index) const
{
const auto& elf_header = this->elf_header();
assert(index < elf_header.e_shnum);
const uint8_t* section_array_start = m_mmap + elf_header.e_shoff;
return *reinterpret_cast<const ElfNativeSectionHeader*>(section_array_start + index * elf_header.e_shentsize);
}
std::string_view ELFFile::section_name(const ElfNativeSectionHeader& section_header) const
{
const auto& elf_header = this->elf_header();
assert(elf_header.e_shstrndx != SHN_UNDEF);
const auto& section_string_table = this->section_header(elf_header.e_shstrndx);
const char* string_table_start = reinterpret_cast<const char*>(m_mmap + section_string_table.sh_offset);
return string_table_start + section_header.sh_name;
}
std::optional<std::span<const uint8_t>> ELFFile::find_section(std::string_view name) const
{
const auto& elf_header = this->elf_header();
for (std::size_t i = 0; i < elf_header.e_shnum; i++)
{
const auto& section_header = this->section_header(i);
auto section_name = this->section_name(section_header);
if (section_name != name)
continue;
return std::span<const uint8_t>(m_mmap + section_header.sh_offset, section_header.sh_size);
}
return {};
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <LibELF/Types.h>
#include <cstdint>
#include <optional>
#include <span>
#include <string_view>
#include <string>
#include <sys/stat.h>
class ELFFile
{
public:
ELFFile(std::string_view path);
~ELFFile();
const LibELF::ElfNativeFileHeader& elf_header() const;
std::optional<std::span<const uint8_t>> find_section(std::string_view name) const;
bool success() const { return m_success; }
std::string_view path() const { return m_path; }
private:
const LibELF::ElfNativeSectionHeader& section_header(std::size_t index) const;
std::string_view section_name(const LibELF::ElfNativeSectionHeader&) const;
bool validate_elf_header() const;
private:
const std::string m_path;
bool m_success { false };
int m_fd { -1 };
struct stat m_stat { };
uint8_t* m_mmap { nullptr };
};

View File

@ -0,0 +1,246 @@
#include "crc32.h"
#include "GPT.h"
#include <cassert>
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
// FIXME: don't assume 512 byte sectors
#define SECTOR_SIZE 512
GPTFile::GPTFile(std::string_view path)
: m_path(path)
{
m_fd = open(m_path.c_str(), O_RDWR);
if (m_fd == -1)
{
std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
if (fstat(m_fd, &m_stat) == -1)
{
std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);
if (mmap_addr == MAP_FAILED)
{
std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl;
return;
}
m_mmap = reinterpret_cast<uint8_t*>(mmap_addr);
if (!validate_gpt_header())
return;
m_success = true;
}
GPTFile::~GPTFile()
{
if (m_mmap)
munmap(m_mmap, m_stat.st_size);
m_mmap = nullptr;
if (m_fd != -1)
close(m_fd);
m_fd = -1;
}
MBR& GPTFile::mbr()
{
return *reinterpret_cast<MBR*>(m_mmap);
}
const GPTHeader& GPTFile::gpt_header() const
{
return *reinterpret_cast<GPTHeader*>(m_mmap + SECTOR_SIZE);
}
bool GPTFile::install_stage1(std::span<const uint8_t> stage1)
{
auto& mbr = this->mbr();
if (stage1.size() > sizeof(mbr.boot_code))
{
std::cerr << m_path << ": can't fit " << stage1.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl;
return false;
}
// copy boot code
memcpy(mbr.boot_code, stage1.data(), stage1.size());
// setup mbr
mbr.unique_mbr_disk_signature = 0xdeadbeef;
mbr.unknown = 0;
mbr.signature = 0xAA55;
// setup mbr partition records
mbr.partition_records[0].boot_indicator = 0x00;
mbr.partition_records[0].starting_chs[0] = 0x00;
mbr.partition_records[0].starting_chs[1] = 0x02;
mbr.partition_records[0].starting_chs[2] = 0x00;
mbr.partition_records[0].os_type = 0xEE;
mbr.partition_records[0].ending_chs[0] = 0xFF;
mbr.partition_records[0].ending_chs[1] = 0xFF;
mbr.partition_records[0].ending_chs[2] = 0xFF;
mbr.partition_records[0].starting_lba = 1;
mbr.partition_records[0].size_in_lba = 0xFFFFFFFF;
memset(&mbr.partition_records[1], 0x00, sizeof(MBRPartitionRecord));
memset(&mbr.partition_records[2], 0x00, sizeof(MBRPartitionRecord));
memset(&mbr.partition_records[3], 0x00, sizeof(MBRPartitionRecord));
return true;
}
bool GPTFile::install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid)
{
if (stage2.size() < 16)
{
std::cerr << m_path << ": contains invalid .stage2 section, too small for patches" << std::endl;
return false;
}
// find GUID patch offsets
std::size_t disk_guid_offset(-1);
std::size_t part_guid_offset(-1);
for (std::size_t i = 0; i < stage2.size() - 16; i++)
{
if (memcmp(stage2.data() + i, "root disk guid ", 16) == 0)
{
if (disk_guid_offset != std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable disk guids" << std::endl;
return false;
}
disk_guid_offset = i;
}
if (memcmp(stage2.data() + i, "root part guid ", 16) == 0)
{
if (part_guid_offset != std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .stage2 section, multiple patchable partition guids" << std::endl;
return false;
}
part_guid_offset = i;
}
}
if (disk_guid_offset == std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .stage2 section, no patchable disk guid" << std::endl;
return false;
}
if (part_guid_offset == std::size_t(-1))
{
std::cerr << m_path << ": contains invalid .stage2 section, no patchable partition guid" << std::endl;
return false;
}
auto partition = find_partition_with_type(bios_boot_guid);
if (!partition.has_value())
{
std::cerr << m_path << ": could not find partition with type " << bios_boot_guid << std::endl;
return false;
}
const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE;
if (stage2.size() > partition_size)
{
std::cerr << m_path << ": can't fit " << stage2.size() << " bytes of data to partition of size " << partition_size << std::endl;
return false;
}
uint8_t* partition_start = m_mmap + partition->starting_lba * SECTOR_SIZE;
memcpy(partition_start, stage2.data(), stage2.size());
// patch GUIDs
*reinterpret_cast<GUID*>(partition_start + disk_guid_offset) = gpt_header().disk_guid;
*reinterpret_cast<GUID*>(partition_start + part_guid_offset) = root_partition_guid;
return true;
}
bool GPTFile::install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid)
{
if (!find_partition_with_guid(root_partition_guid).has_value())
{
std::cerr << m_path << ": no partition with GUID " << root_partition_guid << std::endl;
return false;
}
if (!install_stage1(stage1))
return false;
if (!install_stage2(stage2, root_partition_guid))
return false;
return true;
}
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_guid(const GUID& guid) const
{
const auto& gpt_header = this->gpt_header();
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
{
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
if (partition_entry.partition_guid != guid)
continue;
return partition_entry;
}
return {};
}
std::optional<GPTPartitionEntry> GPTFile::find_partition_with_type(const GUID& type_guid) const
{
const auto& gpt_header = this->gpt_header();
const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE;
for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++)
{
const auto& partition_entry = *reinterpret_cast<const GPTPartitionEntry*>(partition_entry_array_start + i * gpt_header.size_of_partition_entry);
if (partition_entry.type_guid != type_guid)
continue;
return partition_entry;
}
return {};
}
bool GPTFile::validate_gpt_header() const
{
if (SECTOR_SIZE + m_stat.st_size < sizeof(GPTHeader))
{
std::cerr << m_path << " is too small to have GPT header" << std::endl;
return false;
}
auto gpt_header = this->gpt_header();
if (std::memcmp(gpt_header.signature, "EFI PART", 8) != 0)
{
std::cerr << m_path << " doesn't contain GPT partition header signature" << std::endl;
return false;
}
const uint32_t header_crc32 = gpt_header.header_crc32;
gpt_header.header_crc32 = 0;
if (header_crc32 != crc32_checksum(reinterpret_cast<uint8_t*>(&gpt_header), gpt_header.header_size))
{
std::cerr << m_path << " has non-matching header crc32" << std::endl;
return false;
}
const std::size_t partition_array_size = gpt_header.number_of_partition_entries * gpt_header.size_of_partition_entry;
if (gpt_header.partition_entry_array_crc32 != crc32_checksum(m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE, partition_array_size))
{
std::cerr << m_path << " has non-matching partition entry crc32" << std::endl;
return false;
}
return true;
}

View File

@ -0,0 +1,91 @@
#pragma once
#include "GUID.h"
#include <cstdint>
#include <optional>
#include <span>
#include <string_view>
#include <string>
#include <sys/stat.h>
struct MBRPartitionRecord
{
uint8_t boot_indicator;
uint8_t starting_chs[3];
uint8_t os_type;
uint8_t ending_chs[3];
uint32_t starting_lba;
uint32_t size_in_lba;
} __attribute__((packed));
struct MBR
{
uint8_t boot_code[440];
uint32_t unique_mbr_disk_signature;
uint16_t unknown;
MBRPartitionRecord partition_records[4];
uint16_t signature;
} __attribute__((packed));
static_assert(sizeof(MBR) == 512);
struct GPTPartitionEntry
{
GUID type_guid;
GUID partition_guid;
uint64_t starting_lba;
uint64_t ending_lba;
uint64_t attributes;
uint16_t name[36];
};
static_assert(sizeof(GPTPartitionEntry) == 128);
struct GPTHeader
{
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t reserved;
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
GUID disk_guid;
uint64_t partition_entry_lba;
uint32_t number_of_partition_entries;
uint32_t size_of_partition_entry;
uint32_t partition_entry_array_crc32;
} __attribute__((packed));
static_assert(sizeof(GPTHeader) == 92);
class GPTFile
{
public:
GPTFile(std::string_view path);
~GPTFile();
bool install_bootloader(std::span<const uint8_t> stage1, std::span<const uint8_t> stage2, const GUID& root_partition_guid);
const GPTHeader& gpt_header() const;
bool success() const { return m_success; }
std::string_view path() const { return m_path; }
private:
MBR& mbr();
bool validate_gpt_header() const;
std::optional<GPTPartitionEntry> find_partition_with_guid(const GUID& guid) const;
std::optional<GPTPartitionEntry> find_partition_with_type(const GUID& type_guid) const;
bool install_stage1(std::span<const uint8_t> stage1);
bool install_stage2(std::span<const uint8_t> stage2, const GUID& root_partition_guid);
private:
const std::string m_path;
bool m_success { false };
int m_fd { -1 };
struct stat m_stat { };
uint8_t* m_mmap { nullptr };
};

View File

@ -0,0 +1,74 @@
#include "GUID.h"
#include <iomanip>
#include <cstring>
std::optional<uint64_t> parse_hex(std::string_view hex_string)
{
uint64_t result = 0;
for (char c : hex_string)
{
if (!isxdigit(c))
return {};
uint8_t nibble = 0;
if ('0' <= c && c <= '9')
nibble = c - '0';
else if ('a' <= c && c <= 'f')
nibble = c - 'a' + 10;
else
nibble = c - 'A' + 10;
result = (result << 4) | nibble;
}
return result;
}
std::optional<GUID> GUID::from_string(std::string_view guid_string)
{
if (guid_string.size() != 36)
return {};
if (guid_string[8] != '-' || guid_string[13] != '-' || guid_string[18] != '-' || guid_string[23] != '-')
return {};
auto comp1 = parse_hex(guid_string.substr(0, 8));
auto comp2 = parse_hex(guid_string.substr(9, 4));
auto comp3 = parse_hex(guid_string.substr(14, 4));
auto comp4 = parse_hex(guid_string.substr(19, 4));
auto comp5 = parse_hex(guid_string.substr(24, 12));
if (!comp1.has_value() || !comp2.has_value() || !comp3.has_value() || !comp4.has_value() || !comp5.has_value())
return {};
GUID result;
result.component1 = *comp1;
result.component2 = *comp2;
result.component3 = *comp3;
for (int i = 0; i < 2; i++)
result.component45[i + 0] = *comp4 >> ((2-1) * 8 - i * 8);
for (int i = 0; i < 6; i++)
result.component45[i + 2] = *comp5 >> ((6-1) * 8 - i * 8);
return result;
}
bool GUID::operator==(const GUID& other) const
{
return std::memcmp(this, &other, sizeof(GUID)) == 0;
}
std::ostream& operator<<(std::ostream& out, const GUID& guid)
{
auto flags = out.flags();
out << std::hex << std::setfill('0');
out << std::setw(8) << guid.component1 << '-';
out << std::setw(4) << guid.component2 << '-';
out << std::setw(4) << guid.component3 << '-';
out << std::setw(2);
for (int i = 0; i < 2; i++) out << +guid.component45[i];
out << '-';
for (int i = 2; i < 8; i++) out << +guid.component45[i];
out.flags(flags);
return out;
}

View File

@ -0,0 +1,37 @@
#pragma once
#include <cstdint>
#include <optional>
#include <ostream>
#include <string_view>
struct GUID
{
static std::optional<GUID> from_string(std::string_view);
uint32_t component1;
uint16_t component2;
uint16_t component3;
// last 2 components are combined so no packed needed
uint8_t component45[8];
bool operator==(const GUID& other) const;
};
std::ostream& operator<<(std::ostream& out, const GUID& guid);
// unused 00000000-0000-0000-0000-000000000000
static constexpr GUID unused_guid = {
0x00000000,
0x0000,
0x0000,
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
};
// bios boot 21686148-6449-6E6F-744E-656564454649
static constexpr GUID bios_boot_guid = {
0x21686148,
0x6449,
0x6E6F,
{ 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 }
};

3
bootloader/installer/build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader

View File

@ -0,0 +1,80 @@
#include "crc32.h"
static constexpr uint32_t crc32_table[256] =
{
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
};
uint32_t crc32_checksum(const uint8_t* data, std::size_t count)
{
uint32_t crc32 = 0xFFFFFFFF;
for (size_t i = 0; i < count; i++)
{
uint8_t index = (crc32 ^ data[i]) & 0xFF;
crc32 = (crc32 >> 8) ^ crc32_table[index];
}
return crc32 ^ 0xFFFFFFFF;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include <cstddef>
#include <cstdint>
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);

View File

@ -0,0 +1,44 @@
#include "ELF.h"
#include "GPT.h"
#include <iostream>
int main(int argc, char** argv)
{
using namespace std::string_view_literals;
if (argc != 4)
{
std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE ROOT_PARTITION_GUID\n", argv[0]);
return 1;
}
auto root_partition_guid = GUID::from_string(argv[3]);
if (!root_partition_guid.has_value())
{
std::cerr << "invalid guid '" << argv[3] << '\'' << std::endl;
return 1;
}
ELFFile bootloader(argv[1]);
if (!bootloader.success())
return 1;
auto stage1 = bootloader.find_section(".stage1"sv);
auto stage2 = bootloader.find_section(".stage2"sv);
if (!stage1.has_value() || !stage2.has_value())
{
std::cerr << bootloader.path() << " doesn't contain .stage1 and .stage2 sections" << std::endl;
return 1;
}
GPTFile disk_image(argv[2]);
if (!disk_image.success())
return 1;
if (!disk_image.install_bootloader(*stage1, *stage2, *root_partition_guid))
return 1;
std::cout << "bootloader installed" << std::endl;
return 0;
}

1
bos Symbolic link
View File

@ -0,0 +1 @@
script/build.sh

View File

@ -1,10 +0,0 @@
#!/bin/bash
set -e
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
sudo fsck.ext2 -fn ${LOOP_DEV}p2 || true
sudo losetup -d $LOOP_DEV

View File

@ -1,49 +0,0 @@
#!/bin/bash
set -e
DISK_SIZE=$[50 * 1024 * 1024]
MOUNT_DIR=/mnt
dd if=/dev/zero of=$DISK_IMAGE_PATH bs=512 count=$[$DISK_SIZE / 512] > /dev/null
sed -e 's/\s*\([-\+[:alnum:]]*\).*/\1/' << EOF | fdisk $DISK_IMAGE_PATH > /dev/null
g # gpt
n # new partition
1 # partition number 1
# default (from the beginning of the disk)
+1MiB # bios boot partiton size
n # new partition
3 # partition number 3
# default (right after bios boot partition)
+10Mib# partition size
n # new partition
2 # partition number 2
# default (right after bios boot partition)
# default (to the end of disk)
t # set type
1 # ... of partition 1
4 # bios boot partition
t # set type
2 # ... of partition 2
20 # Linux filesystem
t # set type
3 # ... of partition 3
20 # Linux filesystem
w # write changes
EOF
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
PARTITION1=${LOOP_DEV}p1
PARTITION2=${LOOP_DEV}p2
PARTITION3=${LOOP_DEV}p3
sudo mkfs.ext2 $PARTITION3 > /dev/null
sudo mkfs.ext2 -d $SYSROOT $PARTITION2 > /dev/null
sudo mount $PARTITION2 $MOUNT_DIR
sudo grub-install --no-floppy --target=i386-pc --modules="normal ext2 multiboot" --boot-directory=${MOUNT_DIR}/boot $LOOP_DEV > /dev/null
sudo umount $MOUNT_DIR
sudo losetup -d $LOOP_DEV

View File

@ -1,22 +0,0 @@
#!/bin/bash
set -e
if [ ! -f $DISK_IMAGE_PATH ]; then
$(dirname "$0")/image-full.sh
exit 0
fi
MOUNT_DIR=/mnt
LOOP_DEV=$(sudo losetup -f --show $DISK_IMAGE_PATH)
sudo partprobe $LOOP_DEV
ROOT_PARTITON=${LOOP_DEV}p2
sudo mount $ROOT_PARTITON $MOUNT_DIR
sudo rsync -a ${SYSROOT}/* ${MOUNT_DIR}/
sudo umount $MOUNT_DIR
sudo losetup -d $LOOP_DEV

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.26)
project(kernel CXX ASM)
project(kernel CXX C ASM)
if("${BANAN_ARCH}" STREQUAL "x86_64")
set(ELF_FORMAT elf64-x86-64)
@ -12,6 +12,7 @@ set(KERNEL_SOURCES
font/prefs.psf.o
kernel/ACPI.cpp
kernel/APIC.cpp
kernel/BootInfo.cpp
kernel/CPUID.cpp
kernel/Debug.cpp
kernel/Device/Device.cpp
@ -24,20 +25,25 @@ set(KERNEL_SOURCES
kernel/FS/Ext2/Inode.cpp
kernel/FS/Inode.cpp
kernel/FS/Pipe.cpp
kernel/FS/RamFS/FileSystem.cpp
kernel/FS/RamFS/Inode.cpp
kernel/FS/ProcFS/FileSystem.cpp
kernel/FS/ProcFS/Inode.cpp
kernel/FS/TmpFS/FileSystem.cpp
kernel/FS/TmpFS/Inode.cpp
kernel/FS/VirtualFileSystem.cpp
kernel/Input/PS2Controller.cpp
kernel/Input/PS2Keyboard.cpp
kernel/Input/PS2Keymap.cpp
kernel/InterruptController.cpp
kernel/kernel.cpp
kernel/Memory/FixedWidthAllocator.cpp
kernel/Memory/GeneralAllocator.cpp
kernel/Memory/DMARegion.cpp
kernel/Memory/FileBackedRegion.cpp
kernel/Memory/Heap.cpp
kernel/Memory/kmalloc.cpp
kernel/Memory/MemoryBackedRegion.cpp
kernel/Memory/MemoryRegion.cpp
kernel/Memory/PhysicalRange.cpp
kernel/Memory/VirtualRange.cpp
kernel/Networking/E1000.cpp
kernel/OpenFileDescriptorSet.cpp
kernel/Panic.cpp
kernel/PCI.cpp
@ -47,9 +53,11 @@ set(KERNEL_SOURCES
kernel/Semaphore.cpp
kernel/SpinLock.cpp
kernel/SSP.cpp
kernel/Storage/ATABus.cpp
kernel/Storage/ATAController.cpp
kernel/Storage/ATADevice.cpp
kernel/Storage/ATA/AHCI/Controller.cpp
kernel/Storage/ATA/AHCI/Device.cpp
kernel/Storage/ATA/ATABus.cpp
kernel/Storage/ATA/ATAController.cpp
kernel/Storage/ATA/ATADevice.cpp
kernel/Storage/DiskCache.cpp
kernel/Storage/StorageDevice.cpp
kernel/Syscall.cpp
@ -97,6 +105,14 @@ else()
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
endif()
file(GLOB_RECURSE LAI_SOURCES
lai/*.c
)
set(LAI_SOURCES
${LAI_SOURCES}
kernel/lai_host.cpp
)
set(BAN_SOURCES
../BAN/BAN/New.cpp
../BAN/BAN/String.cpp
@ -110,11 +126,12 @@ set(LIBC_SOURCES
)
set(LIBELF_SOURCES
../LibELF/LibELF/ELF.cpp
../LibELF/LibELF/LoadableELF.cpp
)
set(KERNEL_SOURCES
${KERNEL_SOURCES}
${LAI_SOURCES}
${BAN_SOURCES}
${LIBC_SOURCES}
${LIBELF_SOURCES}
@ -127,10 +144,12 @@ target_compile_definitions(kernel PUBLIC __is_kernel)
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
target_compile_options(kernel PUBLIC -O2 -g)
target_compile_options(kernel PUBLIC -Wno-literal-suffix)
target_compile_options(kernel PUBLIC -fno-rtti -fno-exceptions)
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Wextra -Werror=return-type -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
target_compile_options(kernel PUBLIC -fstack-protector -ffreestanding -Wall -Werror=return-type -Wstack-usage=1024 -fno-omit-frame-pointer -mgeneral-regs-only)
# This might not work with other toolchains
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>)
if(ENABLE_KERNEL_UBSAN)
target_compile_options(kernel PUBLIC -fsanitize=undefined)
@ -146,27 +165,15 @@ endif()
target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
add_custom_target(crt0
COMMAND ${CMAKE_CXX_COMPILER} -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crt0.S -o ${CMAKE_CURRENT_BINARY_DIR}/crt0.o
DEPENDS headers
)
add_custom_command(
TARGET crt0
POST_BUILD
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/crt0.o ${BANAN_LIB}/
)
add_custom_target(kernel-headers
COMMAND sudo rsync -a ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/include/ ${BANAN_INCLUDE}/
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different ${CMAKE_CURRENT_SOURCE_DIR}/lai/include/ ${BANAN_INCLUDE}/
DEPENDS sysroot
USES_TERMINAL
)
add_custom_target(kernel-install
COMMAND sudo cp ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/kernel ${BANAN_BOOT}/banan-os.kernel
DEPENDS kernel
USES_TERMINAL
)
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
@ -176,14 +183,14 @@ add_custom_command(
TARGET kernel PRE_LINK
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crti.S ${COMPILE_OPTIONS}
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
COMMAND cp ${CRTBEGIN} .
COMMAND cp ${CRTEND} .
COMMAND ${CMAKE_COMMAND} -E copy ${CRTBEGIN} .
COMMAND ${CMAKE_COMMAND} -E copy ${CRTEND} .
)
add_custom_command(
TARGET kernel POST_BUILD
COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
)
#add_custom_command(
# TARGET kernel POST_BUILD
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
#)
add_custom_command(
OUTPUT font/prefs.psf.o

View File

@ -1,4 +1,4 @@
#include <BAN/Assert.h>
#include <BAN/Array.h>
#include <kernel/GDT.h>
#include <string.h>
@ -56,44 +56,44 @@ namespace Kernel::GDT
static constexpr uint16_t s_tss_offset = 0x28;
static TaskStateSegment* s_tss = nullptr;
static SegmentDescriptor* s_gdt = nullptr;
static TaskStateSegment s_tss;
static BAN::Array<SegmentDescriptor, 7> s_gdt; // null, kernel code, kernel data, user code, user data, tss low, tss high
static GDTR s_gdtr;
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
{
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + offset);
desc.base1 = base;
desc.base2 = base >> 16;
desc.base3 = base >> 24;
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
desc.limit1 = limit;
desc.limit2 = limit >> 16;
SegmentDescriptor& desc = s_gdt[offset / sizeof(SegmentDescriptor)];
desc.base1 = (base >> 0) & 0xFFFF;
desc.base2 = (base >> 16) & 0xFF;
desc.base3 = (base >> 24) & 0xFF;
desc.access = access;
desc.limit1 = (limit >> 0) & 0xFFFF;
desc.limit2 = (limit >> 16) & 0x0F;
desc.flags = flags;
desc.access = access & 0xFF;
desc.flags = flags & 0x0F;
}
static void write_tss()
{
s_tss = new TaskStateSegment();
ASSERT(s_tss);
memset(&s_tss, 0x00, sizeof(TaskStateSegment));
s_tss.iopb = sizeof(TaskStateSegment);
memset(s_tss, 0x00, sizeof(TaskStateSegment));
s_tss->rsp0 = 0;
uintptr_t base = (uintptr_t)s_tss;
uint64_t base = (uint64_t)&s_tss;
write_entry(s_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
SegmentDescriptor& desc = *(SegmentDescriptor*)((uintptr_t)s_gdt + s_tss_offset + 0x08);
SegmentDescriptor& desc = s_gdt[s_tss_offset / sizeof(SegmentDescriptor) + 1];
desc.low = base >> 32;
desc.high = 0;
}
void set_tss_stack(uintptr_t rsp)
{
s_tss->rsp0 = rsp;
s_tss.rsp0 = rsp;
}
static void flush_gdt()
@ -108,12 +108,8 @@ namespace Kernel::GDT
void initialize()
{
constexpr uint32_t descriptor_count = 6 + 1; // tss takes 2
s_gdt = new SegmentDescriptor[descriptor_count];
ASSERT(s_gdt);
s_gdtr.address = (uint64_t)s_gdt;
s_gdtr.size = descriptor_count * sizeof(SegmentDescriptor) - 1;
s_gdtr.address = (uint64_t)&s_gdt;
s_gdtr.size = s_gdt.size() * sizeof(SegmentDescriptor) - 1;
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code

View File

@ -1,3 +1,4 @@
#include <BAN/Array.h>
#include <BAN/Errors.h>
#include <kernel/IDT.h>
#include <kernel/InterruptController.h>
@ -8,12 +9,10 @@
#include <kernel/Scheduler.h>
#include <kernel/Timer/PIT.h>
#include <unistd.h>
#define ISR_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
#define IRQ_LIST_X X(0) X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) X(18) X(19) X(20) X(21) X(22) X(23) X(24) X(25) X(26) X(27) X(28) X(29) X(30) X(31)
#define REGISTER_ISR_HANDLER(i) register_interrupt_handler(i, isr ## i)
#define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i)
namespace IDT
namespace Kernel::IDT
{
struct Registers
@ -63,7 +62,9 @@ namespace IDT
static IDTR s_idtr;
static GateDescriptor* s_idt = nullptr;
static void(*s_irq_handlers[0x10])() { nullptr };
#define X(num) 1 +
static BAN::Array<Interruptable*, IRQ_LIST_X 0> s_interruptables;
#undef X
enum ISR
{
@ -101,6 +102,29 @@ namespace IDT
UnkownException0x1F,
};
struct PageFaultError
{
union
{
uint32_t raw;
struct
{
uint32_t present : 1;
uint32_t write : 1;
uint32_t userspace : 1;
uint32_t reserved_write : 1;
uint32_t instruction : 1;
uint32_t protection_key : 1;
uint32_t shadow_stack : 1;
uint32_t reserved1 : 8;
uint32_t sgx_violation : 1;
uint32_t reserved2 : 16;
};
};
};
static_assert(sizeof(PageFaultError) == 4);
static const char* isr_exceptions[] =
{
"Division Error",
@ -137,15 +161,79 @@ namespace IDT
"Unkown Exception 0x1F",
};
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, Kernel::InterruptStack& interrupt_stack, const Registers* regs)
extern "C" void cpp_isr_handler(uint64_t isr, uint64_t error, InterruptStack& interrupt_stack, const Registers* regs)
{
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Kernel::Thread::current().save_sse();
Thread::current().save_sse();
#endif
pid_t tid = Kernel::Scheduler::current_tid();
pid_t pid = tid ? Kernel::Process::current().pid() : 0;
pid_t tid = Scheduler::current_tid();
pid_t pid = tid ? Process::current().pid() : 0;
if (tid)
{
Thread::current().set_return_rsp(interrupt_stack.rsp);
Thread::current().set_return_rip(interrupt_stack.rip);
if (isr == ISR::PageFault)
{
// Check if stack is OOB
auto& stack = Thread::current().stack();
auto& istack = Thread::current().interrupt_stack();
if (stack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= stack.vaddr() + stack.size())
; // using normal stack
else if (istack.vaddr() < interrupt_stack.rsp && interrupt_stack.rsp <= istack.vaddr() + istack.size())
; // using interrupt stack
else
{
derrorln("Stack pointer out of bounds!");
derrorln("rsp {H}, stack {H}->{H}, istack {H}->{H}",
interrupt_stack.rsp,
stack.vaddr(), stack.vaddr() + stack.size(),
istack.vaddr(), istack.vaddr() + istack.size()
);
Thread::current().handle_signal(SIGKILL);
goto done;
}
// Try demand paging on non present pages
PageFaultError page_fault_error;
page_fault_error.raw = error;
if (!page_fault_error.present)
{
asm volatile("sti");
auto result = Process::current().allocate_page_for_demand_paging(regs->cr2);
asm volatile("cli");
if (!result.is_error() && result.value())
goto done;
if (result.is_error())
{
dwarnln("Demand paging: {}", result.error());
Thread::current().handle_signal(SIGKILL);
goto done;
}
}
}
}
if (PageTable::current().get_page_flags(interrupt_stack.rip & PAGE_ADDR_MASK) & PageTable::Flags::Present)
{
auto* machine_code = (const uint8_t*)interrupt_stack.rip;
dwarnln("While executing: {2H}{2H}{2H}{2H}{2H}{2H}{2H}{2H}",
machine_code[0],
machine_code[1],
machine_code[2],
machine_code[3],
machine_code[4],
machine_code[5],
machine_code[6],
machine_code[7]
);
}
dwarnln(
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
@ -160,14 +248,11 @@ namespace IDT
regs->rip, regs->rflags,
regs->cr0, regs->cr2, regs->cr3, regs->cr4
);
if (isr == ISR::PageFault)
PageTable::current().debug_dump();
Debug::dump_stack_trace();
if (tid)
{
Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
}
if (tid && Kernel::Thread::current().is_userspace())
if (tid && Thread::current().is_userspace())
{
// TODO: Confirm and fix the exception to signal mappings
@ -195,33 +280,38 @@ namespace IDT
break;
}
Kernel::Thread::current().handle_signal(signal);
Thread::current().handle_signal(signal);
}
else
{
Kernel::panic("Unhandled exception");
panic("Unhandled exception");
}
ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
ASSERT(Thread::current().state() != Thread::State::Terminated);
done:
#if __enable_sse
if (from_userspace)
{
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
Kernel::Thread::current().load_sse();
ASSERT(Thread::current().state() == Thread::State::Executing);
Thread::current().load_sse();
}
#endif
return;
}
extern "C" void cpp_irq_handler(uint64_t irq, Kernel::InterruptStack& interrupt_stack)
extern "C" void cpp_irq_handler(uint64_t irq, InterruptStack& interrupt_stack)
{
#if __enable_sse
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
if (from_userspace)
Kernel::Thread::current().save_sse();
Thread::current().save_sse();
#endif
if (Kernel::Scheduler::current_tid())
if (Scheduler::current_tid())
{
Kernel::Thread::current().set_return_rsp(interrupt_stack.rsp);
Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
Thread::current().set_return_rsp(interrupt_stack.rsp);
Thread::current().set_return_rip(interrupt_stack.rip);
}
if (!InterruptController::get().is_in_service(irq))
@ -229,21 +319,23 @@ namespace IDT
else
{
InterruptController::get().eoi(irq);
if (s_irq_handlers[irq])
s_irq_handlers[irq]();
if (s_interruptables[irq])
s_interruptables[irq]->handle_irq();
else
dprintln("no handler for irq 0x{2H}\n", irq);
}
Kernel::Scheduler::get().reschedule_if_idling();
Scheduler::get().reschedule_if_idling();
ASSERT(Kernel::Thread::current().state() != Kernel::Thread::State::Terminated);
ASSERT(Thread::current().state() != Thread::State::Terminated);
#if __enable_sse
if (from_userspace)
{
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
Kernel::Thread::current().load_sse();
ASSERT(Thread::current().state() == Thread::State::Executing);
Thread::current().load_sse();
}
#endif
}
static void flush_idt()
@ -269,60 +361,20 @@ namespace IDT
s_idt[index].flags = 0xEE;
}
void register_irq_handler(uint8_t irq, void(*handler)())
void register_irq_handler(uint8_t irq, Interruptable* interruptable)
{
s_irq_handlers[irq] = handler;
if (irq > s_interruptables.size())
Kernel::panic("Trying to assign handler for irq {} while only {} are supported", irq, s_interruptables.size());
s_interruptables[irq] = interruptable;
}
extern "C" void isr0();
extern "C" void isr1();
extern "C" void isr2();
extern "C" void isr3();
extern "C" void isr4();
extern "C" void isr5();
extern "C" void isr6();
extern "C" void isr7();
extern "C" void isr8();
extern "C" void isr9();
extern "C" void isr10();
extern "C" void isr11();
extern "C" void isr12();
extern "C" void isr13();
extern "C" void isr14();
extern "C" void isr15();
extern "C" void isr16();
extern "C" void isr17();
extern "C" void isr18();
extern "C" void isr19();
extern "C" void isr20();
extern "C" void isr21();
extern "C" void isr22();
extern "C" void isr23();
extern "C" void isr24();
extern "C" void isr25();
extern "C" void isr26();
extern "C" void isr27();
extern "C" void isr28();
extern "C" void isr29();
extern "C" void isr30();
extern "C" void isr31();
#define X(num) extern "C" void isr ## num();
ISR_LIST_X
#undef X
extern "C" void irq0();
extern "C" void irq1();
extern "C" void irq2();
extern "C" void irq3();
extern "C" void irq4();
extern "C" void irq5();
extern "C" void irq6();
extern "C" void irq7();
extern "C" void irq8();
extern "C" void irq9();
extern "C" void irq10();
extern "C" void irq11();
extern "C" void irq12();
extern "C" void irq13();
extern "C" void irq14();
extern "C" void irq15();
#define X(num) extern "C" void irq ## num();
IRQ_LIST_X
#undef X
extern "C" void syscall_asm();
@ -335,59 +387,27 @@ namespace IDT
s_idtr.offset = (uint64_t)s_idt;
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
REGISTER_ISR_HANDLER(0);
REGISTER_ISR_HANDLER(1);
REGISTER_ISR_HANDLER(2);
REGISTER_ISR_HANDLER(3);
REGISTER_ISR_HANDLER(4);
REGISTER_ISR_HANDLER(5);
REGISTER_ISR_HANDLER(6);
REGISTER_ISR_HANDLER(7);
REGISTER_ISR_HANDLER(8);
REGISTER_ISR_HANDLER(9);
REGISTER_ISR_HANDLER(10);
REGISTER_ISR_HANDLER(11);
REGISTER_ISR_HANDLER(12);
REGISTER_ISR_HANDLER(13);
REGISTER_ISR_HANDLER(14);
REGISTER_ISR_HANDLER(15);
REGISTER_ISR_HANDLER(16);
REGISTER_ISR_HANDLER(17);
REGISTER_ISR_HANDLER(18);
REGISTER_ISR_HANDLER(19);
REGISTER_ISR_HANDLER(20);
REGISTER_ISR_HANDLER(21);
REGISTER_ISR_HANDLER(22);
REGISTER_ISR_HANDLER(23);
REGISTER_ISR_HANDLER(24);
REGISTER_ISR_HANDLER(25);
REGISTER_ISR_HANDLER(26);
REGISTER_ISR_HANDLER(27);
REGISTER_ISR_HANDLER(28);
REGISTER_ISR_HANDLER(29);
REGISTER_ISR_HANDLER(30);
REGISTER_ISR_HANDLER(31);
#define X(num) register_interrupt_handler(num, isr ## num);
ISR_LIST_X
#undef X
REGISTER_IRQ_HANDLER(0);
REGISTER_IRQ_HANDLER(1);
REGISTER_IRQ_HANDLER(2);
REGISTER_IRQ_HANDLER(3);
REGISTER_IRQ_HANDLER(4);
REGISTER_IRQ_HANDLER(5);
REGISTER_IRQ_HANDLER(6);
REGISTER_IRQ_HANDLER(7);
REGISTER_IRQ_HANDLER(8);
REGISTER_IRQ_HANDLER(9);
REGISTER_IRQ_HANDLER(10);
REGISTER_IRQ_HANDLER(11);
REGISTER_IRQ_HANDLER(12);
REGISTER_IRQ_HANDLER(13);
REGISTER_IRQ_HANDLER(14);
REGISTER_IRQ_HANDLER(15);
#define X(num) register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
IRQ_LIST_X
#undef X
register_syscall_handler(0x80, syscall_asm);
flush_idt();
}
}
[[noreturn]] void force_triple_fault()
{
// load 0 sized IDT and trigger an interrupt to force triple fault
asm volatile("cli");
s_idtr.size = 0;
flush_idt();
asm volatile("int $0x00");
ASSERT_NOT_REACHED();
}
}

View File

@ -1,6 +1,6 @@
#include <BAN/Errors.h>
#include <kernel/Arch.h>
#include <kernel/CPUID.h>
#include <kernel/InterruptController.h>
#include <kernel/LockGuard.h>
#include <kernel/Memory/kmalloc.h>
#include <kernel/Memory/PageTable.h>
@ -91,6 +91,13 @@ namespace Kernel
s_has_pge = true;
}
// enable write protect to kernel
asm volatile(
"movq %cr0, %rax;"
"orq $0x10000, %rax;"
"movq %rax, %cr0;"
);
ASSERT(s_kernel == nullptr);
s_kernel = new PageTable();
ASSERT(s_kernel);
@ -110,6 +117,13 @@ namespace Kernel
return *s_current;
}
bool PageTable::is_valid_pointer(uintptr_t pointer)
{
if (!is_canonical(pointer))
return false;
return true;
}
static uint64_t* allocate_zeroed_page_aligned_page()
{
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
@ -128,9 +142,25 @@ namespace Kernel
uint64_t* pml4 = (uint64_t*)P2V(m_highest_paging_struct);
pml4[511] = s_global_pml4e;
// Map (0 -> phys_kernel_end) to (KERNEL_OFFSET -> virt_kernel_end)
map_range_at(0, KERNEL_OFFSET, (uintptr_t)g_kernel_end - KERNEL_OFFSET, Flags::ReadWrite | Flags::Present);
prepare_fast_page();
// Map main bios area below 1 MiB
map_range_at(
0x000E0000,
P2V(0x000E0000),
0x00100000 - 0x000E0000,
PageTable::Flags::Present
);
// Map (phys_kernel_start -> phys_kernel_end) to (virt_kernel_start -> virt_kernel_end)
ASSERT((vaddr_t)g_kernel_start % PAGE_SIZE == 0);
map_range_at(
V2P(g_kernel_start),
(vaddr_t)g_kernel_start,
g_kernel_end - g_kernel_start,
Flags::ReadWrite | Flags::Present
);
// Map executable kernel memory as executable
map_range_at(
V2P(g_kernel_execute_start),
@ -148,6 +178,76 @@ namespace Kernel
);
}
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 = (uint64_t*)P2V(m_highest_paging_struct);
ASSERT(!(pml4[pml4e] & Flags::Present));
pml4[pml4e] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
uint64_t* pdpt = (uint64_t*)P2V(pml4[pml4e] & PAGE_ADDR_MASK);
ASSERT(!(pdpt[pdpte] & Flags::Present));
pdpt[pdpte] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
ASSERT(!(pd[pde] & Flags::Present));
pd[pde] = V2P(allocate_zeroed_page_aligned_page()) | Flags::ReadWrite | Flags::Present;
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = V2P(allocate_zeroed_page_aligned_page());
}
void PageTable::map_fast_page(paddr_t paddr)
{
ASSERT(s_kernel);
ASSERT_NEQ(paddr, 0);
ASSERT(!interrupts_enabled());
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 = (uint64_t*)P2V(s_kernel->m_highest_paging_struct);
uint64_t* pdpt = (uint64_t*)P2V(pml4[pml4e] & PAGE_ADDR_MASK);
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
ASSERT(!(pt[pte] & Flags::Present));
pt[pte] = paddr | Flags::ReadWrite | Flags::Present;
invalidate(fast_page());
}
void PageTable::unmap_fast_page()
{
ASSERT(s_kernel);
ASSERT(!interrupts_enabled());
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 = (uint64_t*)P2V(s_kernel->m_highest_paging_struct);
uint64_t* pdpt = (uint64_t*)P2V(pml4[pml4e] & PAGE_ADDR_MASK);
uint64_t* pd = (uint64_t*)P2V(pdpt[pdpte] & PAGE_ADDR_MASK);
uint64_t* pt = (uint64_t*)P2V(pd[pde] & PAGE_ADDR_MASK);
ASSERT(pt[pte] & Flags::Present);
pt[pte] = 0;
invalidate(fast_page());
}
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
{
LockGuard _(s_kernel->m_lock);
@ -209,13 +309,16 @@ namespace Kernel
void PageTable::invalidate(vaddr_t vaddr)
{
ASSERT(vaddr % PAGE_SIZE == 0);
if (this == s_current)
asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
asm volatile("invlpg (%0)" :: "r"(vaddr) : "memory");
}
void PageTable::unmap_page(vaddr_t vaddr)
{
if (vaddr && (vaddr >= KERNEL_OFFSET) != (this == s_kernel))
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
if (vaddr >= KERNEL_OFFSET)
ASSERT_GTE(vaddr, (vaddr_t)g_kernel_start);
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
Kernel::panic("unmapping {8H}, kernel: {}", vaddr, this == s_kernel);
ASSERT(is_canonical(vaddr));
@ -257,7 +360,11 @@ namespace Kernel
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags)
{
if (vaddr && (vaddr >= KERNEL_OFFSET) != (this == s_kernel))
ASSERT(vaddr);
ASSERT(vaddr != fast_page());
if (vaddr >= KERNEL_OFFSET && s_current)
ASSERT_GTE(vaddr, (vaddr_t)g_kernel_start);
if ((vaddr >= KERNEL_OFFSET) != (this == s_kernel))
Kernel::panic("mapping {8H} to {8H}, kernel: {}", paddr, vaddr, this == s_kernel);
ASSERT(is_canonical(vaddr));
@ -324,12 +431,11 @@ namespace Kernel
{
ASSERT(is_canonical(vaddr));
ASSERT(vaddr);
ASSERT(paddr % PAGE_SIZE == 0);
ASSERT(vaddr % PAGE_SIZE == 0);
size_t first_page = vaddr / PAGE_SIZE;
size_t last_page = (vaddr + size - 1) / PAGE_SIZE;
size_t page_count = last_page - first_page + 1;
size_t page_count = range_page_count(vaddr, size);
LockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++)
@ -406,6 +512,8 @@ namespace Kernel
vaddr_t PageTable::reserve_free_page(vaddr_t first_address, vaddr_t last_address)
{
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_end)
first_address = (vaddr_t)g_kernel_end;
if (size_t rem = first_address % PAGE_SIZE)
first_address += PAGE_SIZE - rem;
if (size_t rem = last_address % PAGE_SIZE)
@ -463,8 +571,9 @@ namespace Kernel
vaddr |= (uint64_t)pdpte << 30;
vaddr |= (uint64_t)pde << 21;
vaddr |= (uint64_t)pte << 12;
vaddr = canonicalize(vaddr);
ASSERT(reserve_page(vaddr));
return canonicalize(vaddr);
return vaddr;
}
}
}
@ -488,6 +597,8 @@ namespace Kernel
vaddr_t PageTable::reserve_free_contiguous_pages(size_t page_count, vaddr_t first_address, vaddr_t last_address)
{
if (first_address >= KERNEL_OFFSET && first_address < (vaddr_t)g_kernel_start)
first_address = (vaddr_t)g_kernel_start;
if (size_t rem = first_address % PAGE_SIZE)
first_address += PAGE_SIZE - rem;
if (size_t rem = last_address % PAGE_SIZE)

View File

@ -1,11 +1,3 @@
# Declare constants for the multiboot header
.set ALIGN, 1<<0 # align loaded modules on page boundaries
.set MEMINFO, 1<<1 # provide memory map
.set VIDEOINFO, 1<<2 # provide video info
.set MB_FLAGS, ALIGN | MEMINFO | VIDEOINFO # this is the Multiboot 'flag' field
.set MB_MAGIC, 0x1BADB002 # 'magic number' lets bootloader find the header
.set MB_CHECKSUM, -(MB_MAGIC + MB_FLAGS) #checksum of above, to prove we are multiboot
.set PG_PRESENT, 1<<0
.set PG_READ_WRITE, 1<<1
.set PG_PAGE_SIZE, 1<<7
@ -15,19 +7,37 @@
.code32
# Multiboot header
# multiboot2 header
.section .multiboot, "aw"
.align 4
.long MB_MAGIC
.long MB_FLAGS
.long MB_CHECKSUM
.skip 20
multiboot2_start:
.align 8
.long 0xE85250D6
.long 0
.long multiboot2_end - multiboot2_start
.long -(0xE85250D6 + (multiboot2_end - multiboot2_start))
# framebuffer tag
.align 8
.short 5
.short 0
.long 20
.long 800
.long 600
.long 32
# legacy start
.align 8
.short 3
.short 0
.long 12
.long V2P(_start)
.align 8
.short 0
.short 0
.long 8
multiboot2_end:
.section .bss, "aw", @nobits
# Create stack
.global g_boot_stack_bottom
@ -40,11 +50,9 @@
g_kernel_cmdline:
.skip 4096
.global g_multiboot_info
g_multiboot_info:
bootloader_magic:
.skip 8
.global g_multiboot_magic
g_multiboot_magic:
bootloader_info:
.skip 8
.section .data
@ -119,19 +127,6 @@ check_requirements:
.exit:
jmp system_halt
copy_kernel_commandline:
pushl %esi
pushl %edi
movl V2P(g_multiboot_info), %esi
addl $16, %esi
movl (%esi), %esi
movl $1024, %ecx
movl $V2P(g_kernel_cmdline), %edi
rep movsl
popl %edi
popl %esi
ret
enable_sse:
movl %cr0, %eax
andw $0xFFFB, %ax
@ -170,10 +165,9 @@ initialize_paging:
_start:
# Initialize stack and multiboot info
movl $V2P(g_boot_stack_top), %esp
movl %eax, V2P(g_multiboot_magic)
movl %ebx, V2P(g_multiboot_info)
movl %eax, V2P(bootloader_magic)
movl %ebx, V2P(bootloader_info)
call copy_kernel_commandline
call check_requirements
call enable_sse
@ -201,13 +195,14 @@ long_mode:
jmp *%rcx
higher_half:
addq $KERNEL_OFFSET, g_multiboot_info
# call global constuctors
call _init
# call to the kernel itself (clear ebp for stacktrace)
# call to the kernel itself (clear rbp for stacktrace)
xorq %rbp, %rbp
movl V2P(bootloader_magic), %edi
movl V2P(bootloader_info), %esi
call kernel_main
# call global destructors

View File

@ -158,6 +158,22 @@ irq 12
irq 13
irq 14
irq 15
irq 16
irq 17
irq 18
irq 19
irq 20
irq 21
irq 22
irq 23
irq 24
irq 25
irq 26
irq 27
irq 28
irq 29
irq 30
irq 31
// arguments in RAX, RBX, RCX, RDX, RSI, RDI
// System V ABI: RDI, RSI, RDX, RCX, R8, R9

View File

@ -108,26 +108,37 @@ namespace Kernel
static BAN::ErrorOr<void> initialize();
static ACPI& get();
const SDTHeader* get_header(const char[4]);
const SDTHeader* get_header(BAN::StringView signature, uint32_t index);
private:
ACPI() = default;
BAN::ErrorOr<void> initialize_impl();
const SDTHeader* get_header_from_index(size_t);
private:
paddr_t m_header_table_paddr = 0;
vaddr_t m_header_table_vaddr = 0;
uint32_t m_entry_size = 0;
uint32_t m_entry_count = 0;
struct MappedPage
{
Kernel::paddr_t paddr;
Kernel::vaddr_t vaddr;
SDTHeader* as_header() { return (SDTHeader*)vaddr; }
};
BAN::Vector<MappedPage> m_mapped_headers;
};
}
}
namespace BAN::Formatter
{
template<typename F>
void print_argument(F putc, const Kernel::ACPI::SDTHeader& header, const ValueFormat& format)
{
putc(header.signature[0]);
putc(header.signature[1]);
putc(header.signature[2]);
putc(header.signature[3]);
}
}

View File

@ -4,51 +4,56 @@
#include <kernel/InterruptController.h>
#include <kernel/Memory/Types.h>
class APIC final : public InterruptController
namespace Kernel
{
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
private:
uint32_t read_from_local_apic(ptrdiff_t);
void write_to_local_apic(ptrdiff_t, uint32_t);
private:
~APIC() { ASSERT_NOT_REACHED(); }
static APIC* create();
friend class InterruptController;
private:
struct Processor
class APIC final : public InterruptController
{
enum Flags : uint8_t
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
private:
uint32_t read_from_local_apic(ptrdiff_t);
void write_to_local_apic(ptrdiff_t, uint32_t);
private:
~APIC() { ASSERT_NOT_REACHED(); }
static APIC* create();
friend class InterruptController;
private:
struct Processor
{
Enabled = 1,
OnlineCapable = 2,
enum Flags : uint8_t
{
Enabled = 1,
OnlineCapable = 2,
};
uint8_t processor_id;
uint8_t apic_id;
uint8_t flags;
};
uint8_t processor_id;
uint8_t apic_id;
uint8_t flags;
struct IOAPIC
{
uint8_t id;
Kernel::paddr_t paddr;
Kernel::vaddr_t vaddr;
uint32_t gsi_base;
uint8_t max_redirs;
uint32_t read(uint8_t offset);
void write(uint8_t offset, uint32_t data);
};
private:
BAN::Vector<Processor> m_processors;
Kernel::paddr_t m_local_apic_paddr = 0;
Kernel::vaddr_t m_local_apic_vaddr = 0;
BAN::Vector<IOAPIC> m_io_apics;
uint8_t m_irq_overrides[0x100] {};
};
struct IOAPIC
{
uint8_t id;
Kernel::paddr_t paddr;
Kernel::vaddr_t vaddr;
uint32_t gsi_base;
uint8_t max_redirs;
uint32_t read(uint8_t offset);
void write(uint8_t offset, uint32_t data);
};
private:
BAN::Vector<Processor> m_processors;
Kernel::paddr_t m_local_apic_paddr = 0;
Kernel::vaddr_t m_local_apic_vaddr = 0;
BAN::Vector<IOAPIC> m_io_apics;
uint8_t m_irq_overrides[0x100] {};
};
}

View File

@ -21,4 +21,8 @@
#include <stdint.h>
#ifdef __cplusplus
extern "C" uintptr_t read_rip();
#else
extern uintptr_t read_rip();
#endif

View File

@ -1,3 +1,4 @@
#pragma once
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define NEVER_INLINE __attribute__((noinline))

View File

@ -0,0 +1,36 @@
#pragma once
#include <stdint.h>
#define BANAN_BOOTLOADER_MAGIC 0xD3C60CFF
#define BANAN_BOOTLOADER_FB_RGB 1
struct BananBootFramebufferInfo
{
uint32_t address;
uint32_t pitch;
uint32_t width;
uint32_t height;
uint8_t bpp;
uint8_t type;
};
struct BananBootloaderMemoryMapEntry
{
uint64_t address;
uint64_t length;
uint32_t type;
} __attribute__((packed));
struct BananBootloaderMemoryMapInfo
{
uint32_t entry_count;
BananBootloaderMemoryMapEntry entries[];
} __attribute__((packed));
struct BananBootloaderInfo
{
uint32_t command_line_addr;
uint32_t framebuffer_addr;
uint32_t memory_map_addr;
} __attribute__((packed));

View File

@ -0,0 +1,47 @@
#pragma once
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
namespace Kernel
{
enum class FramebufferType
{
NONE,
UNKNOWN,
RGB
};
struct FramebufferInfo
{
paddr_t address;
uint32_t pitch;
uint32_t width;
uint32_t height;
uint8_t bpp;
FramebufferType type = FramebufferType::NONE;
};
struct MemoryMapEntry
{
uint32_t type;
paddr_t address;
uint64_t length;
};
struct BootInfo
{
BAN::String command_line;
FramebufferInfo framebuffer;
BAN::Vector<MemoryMapEntry> memory_map_entries;
};
bool validate_boot_magic(uint32_t magic);
void parse_boot_info(uint32_t magic, uint32_t info);
BAN::StringView get_early_boot_command_line(uint32_t magic, uint32_t info);
extern BootInfo g_boot_info;
}

View File

@ -29,6 +29,24 @@
Debug::DebugLock::unlock(); \
} while(false)
#define dprintln_if(cond, ...) \
do { \
if constexpr(cond) \
dprintln(__VA_ARGS__); \
} while(false)
#define dwarnln_if(cond, ...) \
do { \
if constexpr(cond) \
dwarnln(__VA_ARGS__); \
} while(false)
#define derrorln_if(cond, ...) \
do { \
if constexpr(cond) \
derrorln(__VA_ARGS__); \
} while(false)
#define BOCHS_BREAK() asm volatile("xchgw %bx, %bx")
namespace Debug

View File

@ -1,11 +1,11 @@
#pragma once
#include <kernel/FS/RamFS/Inode.h>
#include <kernel/FS/TmpFS/Inode.h>
namespace Kernel
{
class Device : public RamInode
class Device : public TmpInode
{
public:
virtual ~Device() = default;
@ -13,9 +13,12 @@ namespace Kernel
virtual bool is_device() const override { return true; }
virtual bool is_partition() const { return false; }
virtual bool is_storage_device() const { return false; }
virtual dev_t rdev() const override = 0;
virtual BAN::StringView name() const = 0;
protected:
Device(mode_t, uid_t, gid_t);
};
@ -24,9 +27,9 @@ namespace Kernel
{
protected:
BlockDevice(mode_t mode, uid_t uid, gid_t gid)
: Device(Mode::IFBLK | mode, uid, gid)
: Device(mode, uid, gid)
{
ASSERT(Device::mode().ifblk());
m_inode_info.mode |= Inode::Mode::IFBLK;
}
};
@ -34,9 +37,9 @@ namespace Kernel
{
protected:
CharacterDevice(mode_t mode, uid_t uid, gid_t gid)
: Device(Mode::IFCHR | mode, uid, gid)
: Device(mode, uid, gid)
{
ASSERT(Device::mode().ifchr());
m_inode_info.mode |= Inode::Mode::IFCHR;
}
};

View File

@ -1,3 +1,5 @@
#pragma once
#include <kernel/Device/Device.h>
namespace Kernel
@ -10,14 +12,16 @@ namespace Kernel
virtual dev_t rdev() const override { return m_rdev; }
virtual BAN::StringView name() const override { return "null"sv; }
protected:
NullDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
: CharacterDevice(mode, uid, gid)
, m_rdev(rdev)
{ }
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override { return 0; }
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t size) override { return size; };
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override { return 0; }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan buffer) override { return buffer.size(); };
private:
const dev_t m_rdev;

View File

@ -10,14 +10,16 @@ namespace Kernel
virtual dev_t rdev() const override { return m_rdev; }
virtual BAN::StringView name() const override { return "zero"sv; }
protected:
ZeroDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
: CharacterDevice(mode, uid, gid)
, m_rdev(rdev)
{ }
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t size) override { return size; };
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan buffer) override { return buffer.size(); };
private:
const dev_t m_rdev;

View File

@ -1,12 +1,13 @@
#pragma once
#include <kernel/Device/Device.h>
#include <kernel/FS/RamFS/FileSystem.h>
#include <kernel/FS/TmpFS/FileSystem.h>
#include <kernel/Semaphore.h>
namespace Kernel
{
class DevFileSystem final : public RamFileSystem
class DevFileSystem final : public TmpFileSystem
{
public:
static void initialize();
@ -14,17 +15,26 @@ namespace Kernel
void initialize_device_updater();
void add_device(BAN::StringView path, BAN::RefPtr<RamInode>);
void add_device(BAN::RefPtr<Device>);
void add_inode(BAN::StringView path, BAN::RefPtr<TmpInode>);
void for_each_device(const BAN::Function<BAN::Iteration(Device*)>& callback);
dev_t get_next_dev();
dev_t get_next_dev() const;
int get_next_input_device() const;
void initiate_sync(bool should_block);
private:
DevFileSystem(size_t size)
: RamFileSystem(size)
DevFileSystem()
: TmpFileSystem(-1)
{ }
private:
SpinLock m_device_lock;
mutable SpinLock m_device_lock;
Semaphore m_sync_done;
Semaphore m_sync_semaphore;
volatile bool m_should_sync { false };
};
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/Storage/StorageDevice.h>
#include <kernel/FS/FileSystem.h>
#include <kernel/FS/Ext2/Inode.h>
@ -9,6 +10,40 @@ namespace Kernel
class Ext2FS final : public FileSystem
{
public:
class BlockBufferWrapper
{
BAN_NON_COPYABLE(BlockBufferWrapper);
BAN_NON_MOVABLE(BlockBufferWrapper);
public:
BlockBufferWrapper(BAN::Span<uint8_t> buffer, bool& used)
: m_buffer(buffer)
, m_used(used)
{
ASSERT(m_used);
}
~BlockBufferWrapper()
{
m_used = false;
}
size_t size() const { return m_buffer.size(); }
uint8_t* data() { return m_buffer.data(); }
const uint8_t* data() const { return m_buffer.data(); }
BAN::ByteSpan span() { return m_buffer; }
BAN::ConstByteSpan span() const { return m_buffer.as_const(); }
uint8_t& operator[](size_t index) { return m_buffer[index]; }
uint8_t operator[](size_t index) const { return m_buffer[index]; }
private:
BAN::Span<uint8_t> m_buffer;
bool& m_used;
};
public:
static BAN::ErrorOr<Ext2FS*> create(Partition&);
@ -23,14 +58,19 @@ namespace Kernel
BAN::ErrorOr<void> initialize_root_inode();
BAN::ErrorOr<uint32_t> create_inode(const Ext2::Inode&);
BAN::ErrorOr<void> delete_inode(uint32_t);
void delete_inode(uint32_t ino);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
void read_block(uint32_t, BAN::Span<uint8_t>);
void write_block(uint32_t, BAN::Span<const uint8_t>);
void read_block(uint32_t, BlockBufferWrapper&);
void write_block(uint32_t, const BlockBufferWrapper&);
void sync_superblock();
BlockBufferWrapper get_block_buffer();
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
void release_block(uint32_t block);
BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>>& inode_cache() { return m_inode_cache; }
const Ext2::Superblock& superblock() const { return m_superblock; }
@ -39,11 +79,30 @@ namespace Kernel
uint32_t block;
uint32_t offset;
};
BAN::ErrorOr<BlockLocation> locate_inode(uint32_t);
BlockLocation locate_inode(uint32_t);
BlockLocation locate_block_group_descriptior(uint32_t);
uint32_t block_size() const { return 1024 << superblock().log_block_size; }
class BlockBufferManager
{
public:
BlockBufferManager() = default;
BlockBufferWrapper get_buffer();
BAN::ErrorOr<void> initialize(size_t block_size);
private:
struct BlockBuffer
{
BAN::Vector<uint8_t> buffer;
bool used { false };
};
private:
BAN::Array<BlockBuffer, 10> m_buffers;
};
private:
RecursiveSpinLock m_lock;
@ -52,6 +111,10 @@ namespace Kernel
BAN::RefPtr<Inode> m_root_inode;
BAN::Vector<uint32_t> m_superblock_backups;
BAN::HashMap<ino_t, BAN::RefPtr<Ext2Inode>> m_inode_cache;
BlockBufferManager m_buffer_manager;
Ext2::Superblock m_superblock;
friend class Ext2Inode;

View File

@ -13,6 +13,8 @@ namespace Kernel
class Ext2Inode final : public Inode
{
public:
~Ext2Inode();
virtual ino_t ino() const override { return m_ino; };
virtual Mode mode() const override { return { m_inode.mode }; }
virtual nlink_t nlink() const override { return m_inode.links_count; }
@ -31,19 +33,34 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) override;
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
private:
BAN::ErrorOr<void> for_data_block_index(uint32_t, const BAN::Function<void(uint32_t&)>&, bool allocate);
// Returns maximum number of data blocks in use
// NOTE: the inode might have more blocks than what this suggests if it has been shrinked
uint32_t max_used_data_block_count() const { return size() / blksize(); }
BAN::ErrorOr<uint32_t> data_block_index(uint32_t);
BAN::ErrorOr<uint32_t> allocate_new_block();
BAN::ErrorOr<void> sync();
BAN::Optional<uint32_t> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth);
BAN::Optional<uint32_t> fs_block_of_data_block_index(uint32_t data_block_index);
BAN::ErrorOr<void> link_inode_to_directory(Ext2Inode&, BAN::StringView name);
BAN::ErrorOr<bool> is_directory_empty();
void cleanup_indirect_block(uint32_t block, uint32_t depth);
BAN::ErrorOr<void> cleanup_default_links();
void cleanup_from_fs();
BAN::ErrorOr<uint32_t> allocate_new_block_to_indirect_block(uint32_t& block, uint32_t index, uint32_t depth);
BAN::ErrorOr<uint32_t> allocate_new_block(uint32_t data_block_index);
void sync();
uint32_t block_group() const;
@ -53,7 +70,7 @@ namespace Kernel
, m_inode(inode)
, m_ino(ino)
{}
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(Ext2FS&, uint32_t);
static BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> create(Ext2FS&, uint32_t);
private:
Ext2FS& m_fs;

View File

@ -1,9 +1,11 @@
#pragma once
#include <BAN/ByteSpan.h>
#include <BAN/RefPtr.h>
#include <BAN/String.h>
#include <BAN/StringView.h>
#include <BAN/Vector.h>
#include <BAN/WeakPtr.h>
#include <kernel/API/DirectoryEntry.h>
#include <kernel/Credentials.h>
@ -17,6 +19,9 @@ namespace Kernel
using namespace API;
class FileBackedRegion;
class SharedFileData;
class Inode : public BAN::RefCounted<Inode>
{
public:
@ -85,14 +90,17 @@ namespace Kernel
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode(BAN::StringView);
BAN::ErrorOr<void> list_next_inodes(off_t, DirectoryEntryList*, size_t);
BAN::ErrorOr<void> create_file(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> create_directory(BAN::StringView, mode_t, uid_t, gid_t);
BAN::ErrorOr<void> unlink(BAN::StringView);
// Link API
BAN::ErrorOr<BAN::String> link_target();
// General API
BAN::ErrorOr<size_t> read(off_t, void*, size_t);
BAN::ErrorOr<size_t> write(off_t, const void*, size_t);
BAN::ErrorOr<size_t> read(off_t, BAN::ByteSpan buffer);
BAN::ErrorOr<size_t> write(off_t, BAN::ConstByteSpan buffer);
BAN::ErrorOr<void> truncate(size_t);
BAN::ErrorOr<void> chmod(mode_t);
bool has_data() const;
protected:
@ -100,18 +108,24 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); }
// Link API
virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); }
// General API
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual BAN::ErrorOr<void> chmod_impl(mode_t) { return BAN::Error::from_errno(ENOTSUP); }
virtual bool has_data_impl() const { dwarnln("nonblock not supported"); return true; }
private:
mutable RecursiveSpinLock m_lock;
BAN::WeakPtr<SharedFileData> m_shared_region;
friend class FileBackedRegion;
};
}

View File

@ -31,8 +31,8 @@ namespace Kernel
virtual dev_t rdev() const override { return 0; } // FIXME
protected:
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) override;
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
private:
Pipe(const Credentials&);

View File

@ -0,0 +1,23 @@
#pragma once
#include <kernel/FS/TmpFS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Process.h>
namespace Kernel
{
class ProcFileSystem final : public TmpFileSystem
{
public:
static void initialize();
static ProcFileSystem& get();
BAN::ErrorOr<void> on_process_create(Process&);
void on_process_delete(Process&);
private:
ProcFileSystem();
};
}

View File

@ -0,0 +1,56 @@
#pragma once
#include <kernel/FS/TmpFS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/Process.h>
namespace Kernel
{
class ProcPidInode final : public TmpDirectoryInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<ProcPidInode>> create_new(Process&, TmpFileSystem&, mode_t, uid_t, gid_t);
~ProcPidInode() = default;
virtual uid_t uid() const override { return m_process.credentials().ruid(); }
virtual gid_t gid() const override { return m_process.credentials().rgid(); }
void cleanup();
protected:
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override { return BAN::Error::from_errno(EPERM); }
private:
ProcPidInode(Process&, TmpFileSystem&, const TmpInodeInfo&);
private:
Process& m_process;
};
class ProcROInode final : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<ProcROInode>> create_new(Process&, size_t (Process::*callback)(off_t, BAN::ByteSpan) const, TmpFileSystem&, mode_t, uid_t, gid_t);
~ProcROInode() = default;
virtual uid_t uid() const override { return m_process.credentials().ruid(); }
virtual gid_t gid() const override { return m_process.credentials().rgid(); }
protected:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
// You may not write here and this is always non blocking
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override { return BAN::Error::from_errno(EINVAL); }
virtual BAN::ErrorOr<void> truncate_impl(size_t) override { return BAN::Error::from_errno(EINVAL); }
virtual bool has_data_impl() const override { return true; }
private:
ProcROInode(Process&, size_t (Process::*)(off_t, BAN::ByteSpan) const, TmpFileSystem&, const TmpInodeInfo&);
private:
Process& m_process;
size_t (Process::*m_callback)(off_t, BAN::ByteSpan) const;
};
}

View File

@ -1,46 +0,0 @@
#pragma once
#include <BAN/HashMap.h>
#include <kernel/FS/FileSystem.h>
#include <kernel/SpinLock.h>
namespace Kernel
{
class RamInode;
class RamDirectoryInode;
class RamFileSystem : public FileSystem
{
public:
static BAN::ErrorOr<RamFileSystem*> create(size_t size, mode_t, uid_t, gid_t);
virtual ~RamFileSystem() = default;
BAN::ErrorOr<void> set_root_inode(BAN::RefPtr<RamDirectoryInode>);
virtual BAN::RefPtr<Inode> root_inode() override { return m_inodes[m_root_inode]; }
BAN::ErrorOr<void> add_inode(BAN::RefPtr<RamInode>);
BAN::ErrorOr<BAN::RefPtr<RamInode>> get_inode(ino_t);
blksize_t blksize() const { return m_blksize; }
ino_t next_ino() { return m_next_ino++; }
void for_each_inode(void (*callback)(BAN::RefPtr<RamInode>));
protected:
RamFileSystem(size_t size)
: m_size(size)
{ }
private:
RecursiveSpinLock m_lock;
size_t m_size { 0 };
BAN::HashMap<ino_t, BAN::RefPtr<RamInode>> m_inodes;
ino_t m_root_inode { 0 };
const blksize_t m_blksize = PAGE_SIZE;
ino_t m_next_ino { 1 };
};
}

View File

@ -1,122 +0,0 @@
#pragma once
#include <kernel/FS/Inode.h>
#include <limits.h>
namespace Kernel
{
class RamFileSystem;
class RamInode : public Inode
{
public:
static BAN::ErrorOr<BAN::RefPtr<RamInode>> create(RamFileSystem&, mode_t, uid_t, gid_t);
virtual ~RamInode() = default;
virtual ino_t ino() const override { return m_inode_info.ino; }
virtual Mode mode() const override { return { m_inode_info.mode }; }
virtual nlink_t nlink() const override { return m_inode_info.nlink; }
virtual uid_t uid() const override { return m_inode_info.uid; }
virtual gid_t gid() const override { return m_inode_info.gid; }
virtual off_t size() const override { return m_inode_info.size; }
virtual timespec atime() const override { return m_inode_info.atime; }
virtual timespec mtime() const override { return m_inode_info.mtime; }
virtual timespec ctime() const override { return m_inode_info.ctime; }
virtual blksize_t blksize() const override { return m_inode_info.blksize; }
virtual blkcnt_t blocks() const override { return m_inode_info.blocks; }
virtual dev_t dev() const override { return m_inode_info.dev; }
virtual dev_t rdev() const override { return m_inode_info.rdev; }
void add_link() { m_inode_info.nlink++; }
protected:
RamInode(RamFileSystem& fs, mode_t, uid_t, gid_t);
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
protected:
struct FullInodeInfo
{
ino_t ino;
mode_t mode;
nlink_t nlink;
uid_t uid;
gid_t gid;
off_t size;
timespec atime;
timespec mtime;
timespec ctime;
blksize_t blksize;
blkcnt_t blocks;
dev_t dev;
dev_t rdev;
};
protected:
RamFileSystem& m_fs;
FullInodeInfo m_inode_info;
BAN::Vector<uint8_t> m_data;
friend class RamFileSystem;
};
class RamDirectoryInode final : public RamInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<RamDirectoryInode>> create(RamFileSystem&, ino_t parent, mode_t, uid_t, gid_t);
~RamDirectoryInode() = default;
BAN::ErrorOr<void> add_inode(BAN::StringView, BAN::RefPtr<RamInode>);
protected:
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override;
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override;
private:
RamDirectoryInode(RamFileSystem&, ino_t parent, mode_t, uid_t, gid_t);
private:
static constexpr size_t m_name_max = NAME_MAX;
struct Entry
{
char name[m_name_max + 1];
size_t name_len = 0;
ino_t ino;
};
private:
BAN::Vector<Entry> m_entries;
ino_t m_parent;
friend class RamFileSystem;
};
class RamSymlinkInode final : public RamInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<RamSymlinkInode>> create(RamFileSystem&, BAN::StringView target, mode_t, uid_t, gid_t);
~RamSymlinkInode() = default;
virtual off_t size() const override { return m_target.size(); }
BAN::ErrorOr<void> set_link_target(BAN::StringView);
protected:
virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
private:
RamSymlinkInode(RamFileSystem&, mode_t, uid_t, gid_t);
private:
BAN::String m_target;
friend class RamFileSystem;
};
}

View File

@ -0,0 +1,53 @@
#pragma once
#include <BAN/Array.h>
#include <kernel/Memory/Types.h>
#include <sys/types.h>
#include <time.h>
namespace Kernel
{
struct TmpInodeInfo
{
mode_t mode { 0 };
uid_t uid { 0 };
gid_t gid { 0 };
timespec atime { 0 };
timespec ctime { 0 };
timespec mtime { 0 };
nlink_t nlink { 0 };
size_t size { 0 };
blkcnt_t blocks { 0 };
// 2x direct blocks
// 1x singly indirect
// 1x doubly indirect
// 1x triply indirect
BAN::Array<paddr_t, 5> block;
static constexpr size_t direct_block_count = 2;
static constexpr size_t max_size =
direct_block_count * PAGE_SIZE +
(PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE +
(PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE +
(PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * (PAGE_SIZE / sizeof(paddr_t)) * PAGE_SIZE;
};
static_assert(sizeof(TmpInodeInfo) == 128);
struct TmpDirectoryEntry
{
ino_t ino;
uint8_t type;
size_t name_len;
size_t rec_len;
char name[];
BAN::StringView name_sv() const
{
ASSERT(type != DT_UNKNOWN);
return BAN::StringView(name, name_len);
}
};
}

View File

@ -0,0 +1,180 @@
#pragma once
#include <BAN/HashMap.h>
#include <BAN/Iteration.h>
#include <kernel/FS/FileSystem.h>
#include <kernel/FS/TmpFS/Inode.h>
#include <kernel/LockGuard.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/SpinLock.h>
namespace Kernel
{
namespace TmpFuncs
{
template<typename F>
concept for_each_indirect_paddr_allocating_callback = requires(F func, paddr_t paddr, bool was_allocated)
{
requires BAN::is_same_v<decltype(func(paddr, was_allocated)), BAN::Iteration>;
};
template<typename F>
concept with_block_buffer_callback = requires(F func, BAN::ByteSpan buffer)
{
requires BAN::is_same_v<decltype(func(buffer)), void>;
};
template<typename F>
concept for_each_inode_callback = requires(F func, BAN::RefPtr<TmpInode> inode)
{
requires BAN::is_same_v<decltype(func(inode)), BAN::Iteration>;
};
}
class TmpFileSystem : public FileSystem
{
public:
static constexpr size_t no_page_limit = SIZE_MAX;
public:
static BAN::ErrorOr<TmpFileSystem*> create(size_t max_pages, mode_t, uid_t, gid_t);
~TmpFileSystem();
virtual BAN::RefPtr<Inode> root_inode() override { return m_root_inode; }
BAN::ErrorOr<BAN::RefPtr<TmpInode>> open_inode(ino_t ino);
BAN::ErrorOr<void> add_to_cache(BAN::RefPtr<TmpInode>);
void remove_from_cache(BAN::RefPtr<TmpInode>);
// FIXME: read_block and write_block should not require external buffer
// probably some wrapper like PageTable::with_fast_page could work?
void read_inode(ino_t ino, TmpInodeInfo& out);
void write_inode(ino_t ino, const TmpInodeInfo&);
void delete_inode(ino_t ino);
BAN::ErrorOr<ino_t> allocate_inode(const TmpInodeInfo&);
template<TmpFuncs::with_block_buffer_callback F>
void with_block_buffer(size_t index, F callback);
void free_block(size_t index);
BAN::ErrorOr<size_t> allocate_block();
template<TmpFuncs::for_each_inode_callback F>
void for_each_inode(F callback);
private:
struct PageInfo
{
enum Flags : paddr_t
{
Present = 1 << 0,
Internal = 1 << 1,
};
// 12 bottom bits of paddr can be used as flags, since
// paddr will always be page aligned.
static constexpr size_t flag_bits = 12;
static constexpr paddr_t flags_mask = (1 << flag_bits) - 1;
static constexpr paddr_t paddr_mask = ~flags_mask;
static_assert((1 << flag_bits) <= PAGE_SIZE);
paddr_t paddr() const { return raw & paddr_mask; }
paddr_t flags() const { return raw & flags_mask; }
void set_paddr(paddr_t paddr) { raw = (raw & flags_mask) | (paddr & paddr_mask); }
void set_flags(paddr_t flags) { raw = (raw & paddr_mask) | (flags & flags_mask); }
paddr_t raw { 0 };
};
struct InodeLocation
{
paddr_t paddr;
size_t index;
};
protected:
TmpFileSystem(size_t max_pages);
BAN::ErrorOr<void> initialize(mode_t, uid_t, gid_t);
private:
InodeLocation find_inode(ino_t ino);
paddr_t find_block(size_t index);
template<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
BAN::ErrorOr<void> for_each_indirect_paddr_allocating(PageInfo page_info, F callback, size_t depth);
template<TmpFuncs::for_each_indirect_paddr_allocating_callback F>
BAN::ErrorOr<BAN::Iteration> for_each_indirect_paddr_allocating_internal(PageInfo page_info, F callback, size_t depth);
paddr_t find_indirect(PageInfo root, size_t index, size_t depth);
private:
RecursiveSpinLock m_lock;
BAN::HashMap<ino_t, BAN::RefPtr<TmpInode>> m_inode_cache;
BAN::RefPtr<TmpDirectoryInode> m_root_inode;
// We store pages with triple indirection.
// With 64-bit pointers we can store 512^3 pages of data (512 GiB)
// which should be enough for now.
// In future this should be dynamically calculated based on maximum
// number of pages for this file system.
PageInfo m_data_pages {};
static constexpr size_t first_data_page = 1;
static constexpr size_t max_data_pages =
(PAGE_SIZE / sizeof(PageInfo)) *
(PAGE_SIZE / sizeof(PageInfo)) *
(PAGE_SIZE / sizeof(PageInfo));
// We store inodes in pages with double indirection.
// With 64-bit pointers we can store 512^2 pages of inodes
// which should be enough for now.
// In future this should be dynamically calculated based on maximum
// number of pages for this file system.
PageInfo m_inode_pages;
static constexpr size_t first_inode = 1;
static constexpr size_t max_inodes =
(PAGE_SIZE / sizeof(PageInfo)) *
(PAGE_SIZE / sizeof(PageInfo)) *
(PAGE_SIZE / sizeof(TmpInodeInfo));
const size_t m_max_pages;
size_t m_used_pages { 0 };
};
template<TmpFuncs::with_block_buffer_callback F>
void TmpFileSystem::with_block_buffer(size_t index, F callback)
{
LockGuard _(m_lock);
paddr_t block_paddr = find_block(index);
PageTable::with_fast_page(block_paddr, [&] {
BAN::ByteSpan buffer(reinterpret_cast<uint8_t*>(PageTable::fast_page()), PAGE_SIZE);
callback(buffer);
});
}
template<TmpFuncs::for_each_inode_callback F>
void TmpFileSystem::for_each_inode(F callback)
{
LockGuard _(m_lock);
for (auto& [_, inode] : m_inode_cache)
{
switch (callback(inode))
{
case BAN::Iteration::Continue:
break;
case BAN::Iteration::Break:
return;
default:
ASSERT_NOT_REACHED();
}
}
}
}

View File

@ -0,0 +1,129 @@
#pragma once
#include <BAN/Iteration.h>
#include <BAN/Optional.h>
#include <kernel/FS/Inode.h>
#include <kernel/FS/TmpFS/Definitions.h>
namespace Kernel
{
namespace TmpFuncs
{
template<typename F>
concept for_each_valid_entry_callback = requires(F func, TmpDirectoryEntry& entry)
{
requires BAN::is_same_v<decltype(func(entry)), BAN::Iteration>;
};
}
class TmpFileSystem;
class TmpInode : public Inode
{
public:
virtual ino_t ino() const override final { return m_ino; }
virtual Mode mode() const override final { return Mode(m_inode_info.mode); }
virtual nlink_t nlink() const override final { return m_inode_info.nlink; }
virtual uid_t uid() const override { return m_inode_info.uid; }
virtual gid_t gid() const override { return m_inode_info.gid; }
virtual off_t size() const override final { return m_inode_info.size; }
virtual timespec atime() const override final { return m_inode_info.atime; }
virtual timespec mtime() const override final { return m_inode_info.mtime; }
virtual timespec ctime() const override final { return m_inode_info.ctime; }
virtual blksize_t blksize() const override final { return PAGE_SIZE; }
virtual blkcnt_t blocks() const override final { return m_inode_info.blocks; }
virtual dev_t dev() const override { return 0; } // TODO
virtual dev_t rdev() const override { return 0; } // TODO
public:
static BAN::ErrorOr<BAN::RefPtr<TmpInode>> create_from_existing(TmpFileSystem&, ino_t, const TmpInodeInfo&);
~TmpInode();
protected:
TmpInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
void sync();
void free_all_blocks();
virtual BAN::ErrorOr<void> prepare_unlink() { return {}; };
BAN::Optional<size_t> block_index(size_t data_block_index);
BAN::ErrorOr<size_t> block_index_with_allocation(size_t data_block_index);
protected:
TmpFileSystem& m_fs;
TmpInodeInfo m_inode_info;
const ino_t m_ino;
// has to be able to increase link count
friend class TmpDirectoryInode;
};
class TmpFileInode : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpFileInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t);
~TmpFileInode();
protected:
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
virtual bool has_data_impl() const override { return true; }
private:
TmpFileInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
friend class TmpInode;
};
class TmpSymlinkInode : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpSymlinkInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, BAN::StringView target);
~TmpSymlinkInode();
BAN::ErrorOr<void> set_link_target(BAN::StringView);
protected:
virtual BAN::ErrorOr<BAN::String> link_target_impl() override;
private:
TmpSymlinkInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
};
class TmpDirectoryInode : public TmpInode
{
public:
static BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> create_root(TmpFileSystem&, mode_t, uid_t, gid_t);
static BAN::ErrorOr<BAN::RefPtr<TmpDirectoryInode>> create_new(TmpFileSystem&, mode_t, uid_t, gid_t, TmpInode& parent);
~TmpDirectoryInode();
BAN::ErrorOr<void> link_inode(TmpInode&, BAN::StringView);
protected:
TmpDirectoryInode(TmpFileSystem&, ino_t, const TmpInodeInfo&);
virtual BAN::ErrorOr<void> prepare_unlink() override;
protected:
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_impl(BAN::StringView) override final;
virtual BAN::ErrorOr<void> list_next_inodes_impl(off_t, DirectoryEntryList*, size_t) override final;
virtual BAN::ErrorOr<void> create_file_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
virtual BAN::ErrorOr<void> create_directory_impl(BAN::StringView, mode_t, uid_t, gid_t) override final;
virtual BAN::ErrorOr<void> unlink_impl(BAN::StringView) override;
private:
template<TmpFuncs::for_each_valid_entry_callback F>
void for_each_valid_entry(F callback);
friend class TmpInode;
};
TmpInodeInfo create_inode_info(mode_t mode, uid_t uid, gid_t gid);
}

View File

@ -4,10 +4,10 @@
constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
namespace IDT
namespace Kernel::IDT
{
void initialize();
void register_irq_handler(uint8_t irq, void(*f)());
[[noreturn]] void force_triple_fault();
}

View File

@ -0,0 +1,97 @@
#pragma once
#include <stdint.h>
namespace Kernel::Input::PS2
{
enum IOPort : uint8_t
{
DATA = 0x60,
STATUS = 0x64,
COMMAND = 0x64,
};
enum Status : uint8_t
{
OUTPUT_FULL = (1 << 0),
INPUT_FULL = (1 << 1),
SYSTEM = (1 << 2),
DEVICE_OR_CONTROLLER = (1 << 3),
TIMEOUT_ERROR = (1 << 6),
PARITY_ERROR = (1 << 7),
};
enum Config : uint8_t
{
INTERRUPT_FIRST_PORT = (1 << 0),
INTERRUPT_SECOND_PORT = (1 << 1),
SYSTEM_FLAG = (1 << 2),
ZERO1 = (1 << 3),
CLOCK_FIRST_PORT = (1 << 4),
CLOCK_SECOND_PORT = (1 << 5),
TRANSLATION_FIRST_PORT = (1 << 6),
ZERO2 = (1 << 7),
};
enum Command : uint8_t
{
READ_CONFIG = 0x20,
WRITE_CONFIG = 0x60,
DISABLE_SECOND_PORT = 0xA7,
ENABLE_SECOND_PORT = 0xA8,
TEST_SECOND_PORT = 0xA9,
TEST_CONTROLLER = 0xAA,
TEST_FIRST_PORT = 0xAB,
DISABLE_FIRST_PORT = 0xAD,
ENABLE_FIRST_PORT = 0xAE,
WRITE_TO_SECOND_PORT = 0xD4,
};
enum Response : uint8_t
{
TEST_FIRST_PORT_PASS = 0x00,
TEST_SECOND_PORT_PASS = 0x00,
TEST_CONTROLLER_PASS = 0x55,
SELF_TEST_PASS = 0xAA,
ACK = 0xFA,
};
enum DeviceCommand : uint8_t
{
ENABLE_SCANNING = 0xF4,
DISABLE_SCANNING = 0xF5,
IDENTIFY = 0xF2,
RESET = 0xFF,
};
enum IRQ : uint8_t
{
DEVICE0 = 1,
DEVICE1 = 12,
};
enum KBResponse : uint8_t
{
KEY_ERROR_OR_BUFFER_OVERRUN1 = 0x00,
SELF_TEST_PASSED = 0xAA,
ECHO_RESPONSE = 0xEE,
RESEND = 0xFE,
KEY_ERROR_OR_BUFFER_OVERRUN2 = 0xFF,
};
enum KBScancode : uint8_t
{
SET_SCANCODE_SET1 = 1,
SET_SCANCODE_SET2 = 2,
SET_SCANCODE_SET3 = 3,
};
enum KBLeds : uint8_t
{
SCROLL_LOCK = (1 << 0),
NUM_LOCK = (1 << 1),
CAPS_LOCK = (1 << 2),
};
}

View File

@ -1,20 +1,23 @@
#pragma once
#include <kernel/Device/Device.h>
#include <kernel/InterruptController.h>
namespace Kernel::Input
{
class PS2Device : public CharacterDevice
class PS2Device : public CharacterDevice, public Interruptable
{
public:
PS2Device();
virtual ~PS2Device() {}
virtual void on_byte(uint8_t) = 0;
public:
PS2Device()
: CharacterDevice(Mode::IRUSR | Mode::IRGRP, 0, 0)
{ }
virtual void send_initialize() = 0;
virtual BAN::StringView name() const override { return m_name; }
private:
const BAN::String m_name;
};
class PS2Controller
@ -33,11 +36,8 @@ namespace Kernel::Input
BAN::ErrorOr<void> reset_device(uint8_t);
BAN::ErrorOr<void> set_scanning(uint8_t, bool);
static void device0_irq();
static void device1_irq();
private:
PS2Device* m_devices[2] { nullptr, nullptr };
};
}
}

View File

@ -28,13 +28,13 @@ namespace Kernel::Input
public:
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
virtual void send_initialize() override;
virtual void on_byte(uint8_t) override;
virtual void handle_irq() override;
virtual void update() override;
private:
PS2Keyboard(PS2Controller& controller);
BAN::ErrorOr<void> initialize();
void append_command_queue(uint8_t);
void append_command_queue(uint8_t, uint8_t);
@ -63,7 +63,7 @@ namespace Kernel::Input
virtual dev_t rdev() const override { return m_rdev; }
protected:
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override;
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) override;
private:
const dev_t m_rdev;

View File

@ -5,17 +5,46 @@
#define DISABLE_INTERRUPTS() asm volatile("cli")
#define ENABLE_INTERRUPTS() asm volatile("sti")
class InterruptController
namespace Kernel
{
public:
virtual ~InterruptController() {}
virtual void eoi(uint8_t) = 0;
virtual void enable_irq(uint8_t) = 0;
virtual bool is_in_service(uint8_t) = 0;
class Interruptable
{
public:
void set_irq(int irq);
void enable_interrupt();
void disable_interrupt();
static void initialize(bool force_pic);
static InterruptController& get();
};
virtual void handle_irq() = 0;
bool interrupts_enabled();
protected:
Interruptable() = default;
~Interruptable() {}
private:
int m_irq { -1 };
};
class InterruptController
{
public:
virtual ~InterruptController() {}
virtual void eoi(uint8_t) = 0;
virtual void enable_irq(uint8_t) = 0;
virtual bool is_in_service(uint8_t) = 0;
static void initialize(bool force_pic);
static InterruptController& get();
bool is_using_apic() const { return m_using_apic; }
void enter_acpi_mode();
private:
bool m_using_apic { false };
};
bool interrupts_enabled();
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <kernel/Memory/MemoryRegion.h>
namespace Kernel
{
class DMARegion
{
public:
static BAN::ErrorOr<BAN::UniqPtr<DMARegion>> create(size_t size);
~DMARegion();
size_t size() const { return m_size; }
vaddr_t vaddr() const { return m_vaddr; }
paddr_t paddr() const { return m_paddr; }
paddr_t vaddr_to_paddr(vaddr_t vaddr) const { return vaddr - m_vaddr + m_paddr; }
vaddr_t paddr_to_vaddr(paddr_t paddr) const { return paddr - m_paddr + m_vaddr; }
private:
DMARegion(size_t size, vaddr_t vaddr, paddr_t paddr);
private:
const size_t m_size;
const vaddr_t m_vaddr;
const paddr_t m_paddr;
};
}

View File

@ -0,0 +1,45 @@
#pragma once
#include <kernel/FS/Inode.h>
#include <kernel/Memory/MemoryRegion.h>
namespace Kernel
{
struct SharedFileData : public BAN::RefCounted<SharedFileData>, public BAN::Weakable<SharedFileData>
{
~SharedFileData();
// FIXME: this should probably be ordered tree like map
// for fast lookup and less memory usage
BAN::Vector<paddr_t> pages;
BAN::RefPtr<Inode> inode;
uint8_t page_buffer[PAGE_SIZE];
};
class FileBackedRegion final : public MemoryRegion
{
BAN_NON_COPYABLE(FileBackedRegion);
BAN_NON_MOVABLE(FileBackedRegion);
public:
static BAN::ErrorOr<BAN::UniqPtr<FileBackedRegion>> create(BAN::RefPtr<Inode>, PageTable&, off_t offset, size_t size, AddressRange address_range, Type, PageTable::flags_t);
~FileBackedRegion();
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
protected:
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr) override;
private:
FileBackedRegion(BAN::RefPtr<Inode>, PageTable&, off_t offset, ssize_t size, Type flags, PageTable::flags_t page_flags);
private:
BAN::RefPtr<Inode> m_inode;
const off_t m_offset;
// FIXME: is this even synchronized?
BAN::RefPtr<SharedFileData> m_shared_data;
};
}

View File

@ -1,64 +0,0 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/UniqPtr.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/PageTable.h>
namespace Kernel
{
class FixedWidthAllocator
{
BAN_NON_COPYABLE(FixedWidthAllocator);
BAN_NON_MOVABLE(FixedWidthAllocator);
public:
static BAN::ErrorOr<BAN::UniqPtr<FixedWidthAllocator>> create(PageTable&, uint32_t);
~FixedWidthAllocator();
BAN::ErrorOr<BAN::UniqPtr<FixedWidthAllocator>> clone(PageTable&);
vaddr_t allocate();
bool deallocate(vaddr_t);
uint32_t allocation_size() const { return m_allocation_size; }
uint32_t allocations() const { return m_allocations; }
uint32_t max_allocations() const;
private:
FixedWidthAllocator(PageTable&, uint32_t);
BAN::ErrorOr<void> initialize();
bool allocate_page_if_needed(vaddr_t, uint8_t flags);
struct node
{
node* prev { nullptr };
node* next { nullptr };
bool allocated { false };
};
vaddr_t address_of_node(const node*) const;
node* node_from_address(vaddr_t) const;
void allocate_page_for_node_if_needed(const node*);
void allocate_node(node*);
void deallocate_node(node*);
private:
static constexpr uint32_t m_min_allocation_size = 16;
PageTable& m_page_table;
const uint32_t m_allocation_size;
vaddr_t m_nodes_page { 0 };
vaddr_t m_allocated_pages { 0 };
node* m_free_list { nullptr };
node* m_used_list { nullptr };
uint32_t m_allocations { 0 };
};
}

View File

@ -1,46 +0,0 @@
#pragma once
#include <BAN/LinkedList.h>
#include <BAN/Optional.h>
#include <BAN/UniqPtr.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/PageTable.h>
namespace Kernel
{
class GeneralAllocator
{
BAN_NON_COPYABLE(GeneralAllocator);
BAN_NON_MOVABLE(GeneralAllocator);
public:
static BAN::ErrorOr<BAN::UniqPtr<GeneralAllocator>> create(PageTable&, vaddr_t first_vaddr);
~GeneralAllocator();
BAN::ErrorOr<BAN::UniqPtr<GeneralAllocator>> clone(PageTable&);
BAN::Optional<paddr_t> paddr_of(vaddr_t);
vaddr_t allocate(size_t);
bool deallocate(vaddr_t);
private:
GeneralAllocator(PageTable&, vaddr_t first_vaddr);
private:
struct Allocation
{
vaddr_t address { 0 };
BAN::Vector<paddr_t> pages;
bool contains(vaddr_t);
};
private:
PageTable& m_page_table;
BAN::LinkedList<Allocation> m_allocations;
const vaddr_t m_first_vaddr;
};
}

View File

@ -21,6 +21,9 @@ namespace Kernel
paddr_t take_free_page();
void release_page(paddr_t);
paddr_t take_free_contiguous_pages(size_t pages);
void release_contiguous_pages(paddr_t paddr, size_t pages);
size_t used_pages() const;
size_t free_pages() const;

View File

@ -0,0 +1,31 @@
#pragma once
#include <kernel/Memory/MemoryRegion.h>
namespace Kernel
{
class MemoryBackedRegion final : public MemoryRegion
{
BAN_NON_COPYABLE(MemoryBackedRegion);
BAN_NON_MOVABLE(MemoryBackedRegion);
public:
static BAN::ErrorOr<BAN::UniqPtr<MemoryBackedRegion>> create(PageTable&, size_t size, AddressRange, Type, PageTable::flags_t);
~MemoryBackedRegion();
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) override;
// Copy data from buffer into this region
// This can fail if no memory is mapped and no free memory was available
BAN::ErrorOr<void> copy_data_to_region(size_t offset_into_region, const uint8_t* buffer, size_t buffer_size);
protected:
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t vaddr) override;
private:
MemoryBackedRegion(PageTable&, size_t size, Type, PageTable::flags_t);
};
}

View File

@ -0,0 +1,65 @@
#pragma once
#include <BAN/UniqPtr.h>
#include <kernel/Memory/PageTable.h>
#include <kernel/Memory/Types.h>
#include <stddef.h>
namespace Kernel
{
struct AddressRange
{
vaddr_t start;
vaddr_t end;
};
class MemoryRegion
{
BAN_NON_COPYABLE(MemoryRegion);
BAN_NON_MOVABLE(MemoryRegion);
public:
enum class Type : uint8_t
{
PRIVATE,
SHARED
};
public:
virtual ~MemoryRegion();
bool contains(vaddr_t address) const;
bool contains_fully(vaddr_t address, size_t size) const;
bool overlaps(vaddr_t address, size_t size) const;
size_t size() const { return m_size; }
vaddr_t vaddr() const { return m_vaddr; }
size_t virtual_page_count() const { return BAN::Math::div_round_up<size_t>(m_size, PAGE_SIZE); }
size_t physical_page_count() const { return m_physical_page_count; }
// Returns error if no memory was available
// Returns true if page was succesfully allocated
// Returns false if page was already allocated
BAN::ErrorOr<bool> allocate_page_containing(vaddr_t address);
virtual BAN::ErrorOr<BAN::UniqPtr<MemoryRegion>> clone(PageTable& new_page_table) = 0;
protected:
MemoryRegion(PageTable&, size_t size, Type type, PageTable::flags_t flags);
BAN::ErrorOr<void> initialize(AddressRange);
virtual BAN::ErrorOr<bool> allocate_page_containing_impl(vaddr_t address) = 0;
protected:
PageTable& m_page_table;
const size_t m_size;
const Type m_type;
const PageTable::flags_t m_flags;
vaddr_t m_vaddr { 0 };
size_t m_physical_page_count { 0 };
};
}

View File

@ -1,12 +1,20 @@
#pragma once
#include <BAN/Errors.h>
#include <BAN/Traits.h>
#include <kernel/CriticalScope.h>
#include <kernel/Memory/Types.h>
#include <kernel/SpinLock.h>
namespace Kernel
{
template<typename F>
concept with_fast_page_callback = requires(F func)
{
requires BAN::is_same_v<decltype(func()), void>;
};
class PageTable
{
public:
@ -29,13 +37,50 @@ namespace Kernel
static PageTable& kernel();
static PageTable& current();
static void map_fast_page(paddr_t);
static void unmap_fast_page();
static constexpr vaddr_t fast_page() { return KERNEL_OFFSET; }
template<with_fast_page_callback F>
static void with_fast_page(paddr_t paddr, F callback)
{
CriticalScope _;
map_fast_page(paddr);
callback();
unmap_fast_page();
}
// FIXME: implement sized checks, return span, etc
static void* fast_page_as_ptr(size_t offset = 0)
{
ASSERT(offset <= PAGE_SIZE);
return reinterpret_cast<void*>(fast_page() + offset);
}
template<typename T>
static T& fast_page_as(size_t offset = 0)
{
ASSERT(offset + sizeof(T) <= PAGE_SIZE);
return *reinterpret_cast<T*>(fast_page() + offset);
}
// Retrieves index'th element from fast_page
template<typename T>
static T& fast_page_as_sized(size_t index)
{
ASSERT((index + 1) * sizeof(T) <= PAGE_SIZE);
return *reinterpret_cast<T*>(fast_page() + index * sizeof(T));
}
static bool is_valid_pointer(uintptr_t);
static BAN::ErrorOr<PageTable*> create_userspace();
~PageTable();
void unmap_page(vaddr_t);
void unmap_range(vaddr_t, size_t bytes);
void map_range_at(paddr_t, vaddr_t, size_t, flags_t);
void map_range_at(paddr_t, vaddr_t, size_t bytes, flags_t);
void map_page_at(paddr_t, vaddr_t, flags_t);
paddr_t physical_address_of(vaddr_t) const;
@ -62,7 +107,8 @@ namespace Kernel
uint64_t get_page_data(vaddr_t) const;
void initialize_kernel();
void map_kernel_memory();
void invalidate(vaddr_t);
void prepare_fast_page();
static void invalidate(vaddr_t);
private:
paddr_t m_highest_paging_struct { 0 };
@ -73,7 +119,7 @@ namespace Kernel
{
size_t first_page = start / PAGE_SIZE;
size_t last_page = BAN::Math::div_round_up<size_t>(start + bytes, PAGE_SIZE);
return last_page - first_page + 1;
return last_page - first_page;
}
}

View File

@ -3,7 +3,6 @@
#include <kernel/Memory/Types.h>
#include <stddef.h>
#include <stdint.h>
namespace Kernel
{
@ -12,42 +11,40 @@ namespace Kernel
{
public:
PhysicalRange(paddr_t, size_t);
paddr_t reserve_page();
void release_page(paddr_t);
paddr_t reserve_contiguous_pages(size_t pages);
void release_contiguous_pages(paddr_t paddr, size_t pages);
paddr_t start() const { return m_paddr; }
paddr_t end() const { return m_paddr + m_size; }
bool contains(paddr_t addr) const { return m_paddr <= addr && addr < m_paddr + m_size; }
size_t usable_memory() const { return m_reservable_pages * PAGE_SIZE; }
size_t usable_memory() const { return m_data_pages * PAGE_SIZE; }
size_t used_pages() const { return m_used_pages; }
size_t used_pages() const { return m_data_pages - m_free_pages; }
size_t free_pages() const { return m_free_pages; }
private:
struct node
{
node* next;
node* prev;
};
unsigned long long* ull_bitmap_ptr() { return (unsigned long long*)m_vaddr; }
const unsigned long long* ull_bitmap_ptr() const { return (const unsigned long long*)m_vaddr; }
paddr_t page_address(const node*) const;
node* node_address(paddr_t) const;
paddr_t paddr_for_bit(unsigned long long) const;
unsigned long long bit_for_paddr(paddr_t paddr) const;
unsigned long long contiguous_bits_set(unsigned long long start, unsigned long long count) const;
private:
paddr_t m_paddr { 0 };
const paddr_t m_paddr { 0 };
const size_t m_size { 0 };
vaddr_t m_vaddr { 0 };
size_t m_size { 0 };
uint64_t m_total_pages { 0 };
uint64_t m_reservable_pages { 0 };
uint64_t m_list_pages { 0 };
size_t m_used_pages { 0 };
const size_t m_bitmap_pages { 0 };
const size_t m_data_pages { 0 };
size_t m_free_pages { 0 };
node* m_free_list { nullptr };
node* m_used_list { nullptr };
};
}

View File

@ -15,9 +15,9 @@ namespace Kernel
public:
// Create virtual range to fixed virtual address
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr(PageTable&, vaddr_t, size_t, PageTable::flags_t flags);
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_to_vaddr(PageTable&, vaddr_t, size_t, PageTable::flags_t flags, bool preallocate_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);
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);
// Create virtual range in kernel memory with kmalloc
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_kmalloc(size_t);
~VirtualRange();
@ -28,19 +28,24 @@ namespace Kernel
size_t size() const { return m_size; }
PageTable::flags_t flags() const { return m_flags; }
void set_zero();
bool contains(vaddr_t address) const { return vaddr() <= address && address < vaddr() + size(); }
BAN::ErrorOr<void> allocate_page_for_demand_paging(vaddr_t address);
void copy_from(size_t offset, const uint8_t* buffer, size_t bytes);
private:
VirtualRange(PageTable&);
VirtualRange(PageTable&, bool preallocated, bool kmalloc);
void set_zero();
private:
PageTable& m_page_table;
bool m_kmalloc { false };
const bool m_preallocated;
const bool m_kmalloc;
vaddr_t m_vaddr { 0 };
size_t m_size { 0 };
PageTable::flags_t m_flags { 0 };
BAN::Vector<paddr_t> m_physical_pages;
};
}

View File

@ -0,0 +1,58 @@
#pragma once
#include <BAN/UniqPtr.h>
#include <kernel/Networking/NetworkDriver.h>
#include <kernel/PCI.h>
#define E1000_NUM_RX_DESC 32
#define E1000_NUM_TX_DESC 8
namespace Kernel
{
class E1000 final : public NetworkDriver
{
public:
static bool probe(PCI::Device&);
static BAN::ErrorOr<BAN::UniqPtr<E1000>> create(PCI::Device&);
~E1000();
virtual uint8_t* get_mac_address() override { return m_mac_address; }
virtual BAN::ErrorOr<void> send_packet(const void* data, uint16_t len) override;
virtual bool link_up() override { return m_link_up; }
virtual int link_speed() override;
private:
E1000() = default;
BAN::ErrorOr<void> initialize(PCI::Device&);
static void interrupt_handler();
uint32_t read32(uint16_t reg);
void write32(uint16_t reg, uint32_t value);
void detect_eeprom();
uint32_t eeprom_read(uint8_t addr);
BAN::ErrorOr<void> read_mac_address();
void initialize_rx();
void initialize_tx();
void enable_link();
void enable_interrupts();
void handle_receive();
private:
BAN::UniqPtr<PCI::BarRegion> m_bar_region;
bool m_has_eerprom { false };
uint8_t m_mac_address[6] {};
uint16_t m_rx_current {};
uint16_t m_tx_current {};
struct e1000_rx_desc* m_rx_descs[E1000_NUM_RX_DESC] {};
struct e1000_tx_desc* m_tx_descs[E1000_NUM_TX_DESC] {};
bool m_link_up { false };
};
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <BAN/Errors.h>
namespace Kernel
{
class NetworkDriver
{
public:
virtual ~NetworkDriver() {}
virtual uint8_t* get_mac_address() = 0;
virtual BAN::ErrorOr<void> send_packet(const void* data, uint16_t len) = 0;
virtual bool link_up() = 0;
virtual int link_speed() = 0;
};
}

View File

@ -41,13 +41,14 @@ namespace Kernel
void close_all();
void close_cloexec();
BAN::ErrorOr<size_t> read(int fd, void* buffer, size_t count);
BAN::ErrorOr<size_t> write(int fd, const void* buffer, size_t count);
BAN::ErrorOr<size_t> read(int fd, BAN::ByteSpan);
BAN::ErrorOr<size_t> write(int fd, BAN::ConstByteSpan);
BAN::ErrorOr<void> read_dir_entries(int fd, DirectoryEntryList* list, size_t list_size);
BAN::ErrorOr<BAN::StringView> path_of(int) const;
BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int);
BAN::ErrorOr<int> flags_of(int) const;
private:
struct OpenFileDescription : public BAN::RefCounted<OpenFileDescription>

View File

@ -1,20 +1,70 @@
#pragma once
#include <BAN/UniqPtr.h>
#include <BAN/Vector.h>
#include <kernel/Memory/Types.h>
#include <kernel/Storage/StorageController.h>
namespace Kernel
#include <sys/types.h>
namespace Kernel::PCI
{
class PCIDevice
enum class BarType
{
INVALID,
MEM,
IO,
};
class Device;
class BarRegion
{
BAN_NON_COPYABLE(BarRegion);
BAN_NON_MOVABLE(BarRegion);
public:
static BAN::ErrorOr<BAN::UniqPtr<BarRegion>> create(PCI::Device&, uint8_t bar_num);
~BarRegion();
BarType type() const { return m_type; }
vaddr_t iobase() const { ASSERT(m_type == BarType::IO); return m_paddr; }
vaddr_t vaddr() const { ASSERT(m_type == BarType::MEM); return m_vaddr; }
paddr_t paddr() const { ASSERT(m_type == BarType::MEM); return m_paddr; }
size_t size() const { return m_size; }
void write8(off_t, uint8_t);
void write16(off_t, uint16_t);
void write32(off_t, uint32_t);
uint8_t read8(off_t);
uint16_t read16(off_t);
uint32_t read32(off_t);
private:
BarRegion(BarType, paddr_t, size_t);
BAN::ErrorOr<void> initialize();
private:
const BarType m_type {};
const paddr_t m_paddr {};
const size_t m_size {};
vaddr_t m_vaddr {};
};
class Device
{
public:
PCIDevice(uint8_t, uint8_t, uint8_t);
Device(uint8_t, uint8_t, uint8_t);
uint32_t read_dword(uint8_t) const;
uint16_t read_word(uint8_t) const;
uint8_t read_byte(uint8_t) const;
void write_dword(uint8_t, uint32_t) const;
void write_dword(uint8_t, uint32_t);
void write_word(uint8_t, uint16_t);
void write_byte(uint8_t, uint8_t);
uint8_t bus() const { return m_bus; }
uint8_t dev() const { return m_dev; }
@ -24,38 +74,71 @@ namespace Kernel
uint8_t subclass() const { return m_subclass; }
uint8_t prog_if() const { return m_prog_if; }
void enable_bus_mastering() const;
void disable_bus_mastering() const;
uint8_t header_type() const { return m_header_type; }
void enable_memory_space() const;
void disable_memory_space() const;
uint16_t vendor_id() const { return m_vendor_id; }
uint16_t device_id() const { return m_device_id; }
void enable_pin_interrupts() const;
void disable_pin_interrupts() const;
BAN::ErrorOr<uint8_t> get_irq();
BAN::ErrorOr<BAN::UniqPtr<BarRegion>> allocate_bar_region(uint8_t bar_num);
void enable_bus_mastering();
void disable_bus_mastering();
void enable_memory_space();
void disable_memory_space();
void enable_io_space();
void disable_io_space();
void enable_pin_interrupts();
void disable_pin_interrupts();
private:
uint8_t m_bus;
uint8_t m_dev;
uint8_t m_func;
void enumerate_capabilites();
void set_command_bits(uint16_t mask);
void unset_command_bits(uint16_t mask);
private:
const uint8_t m_bus;
const uint8_t m_dev;
const uint8_t m_func;
uint8_t m_class_code;
uint8_t m_subclass;
uint8_t m_prog_if;
uint8_t m_header_type;
uint16_t m_vendor_id;
uint16_t m_device_id;
BAN::Optional<uint8_t> m_offset_msi;
BAN::Optional<uint8_t> m_offset_msi_x;
};
class PCI
class PCIManager
{
BAN_NON_COPYABLE(PCI);
BAN_NON_MOVABLE(PCI);
BAN_NON_COPYABLE(PCIManager);
BAN_NON_MOVABLE(PCIManager);
public:
static void initialize();
static PCI& get();
static PCIManager& get();
const BAN::Vector<PCIDevice>& devices() const { return m_devices; }
const BAN::Vector<PCI::Device>& devices() const { return m_devices; }
static uint32_t read_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
static uint16_t read_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
static uint8_t read_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset);
static void write_config_dword(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t value);
static void write_config_word(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint16_t value);
static void write_config_byte(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint8_t value);
private:
PCI() = default;
PCIManager() = default;
void check_function(uint8_t bus, uint8_t dev, uint8_t func);
void check_device(uint8_t bus, uint8_t dev);
void check_bus(uint8_t bus);
@ -63,7 +146,7 @@ namespace Kernel
void initialize_devices();
private:
BAN::Vector<PCIDevice> m_devices;
BAN::Vector<PCI::Device> m_devices;
};
}

View File

@ -2,17 +2,22 @@
#include <kernel/InterruptController.h>
class PIC final : public InterruptController
namespace Kernel
{
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
static void remap();
static void mask_all();
class PIC final : public InterruptController
{
public:
virtual void eoi(uint8_t) override;
virtual void enable_irq(uint8_t) override;
virtual bool is_in_service(uint8_t) override;
private:
static PIC* create();
friend class InterruptController;
};
static void remap();
static void mask_all();
private:
static PIC* create();
friend class InterruptController;
};
}

View File

@ -11,11 +11,11 @@ namespace Kernel::detail
template<typename... Args>
__attribute__((__noreturn__))
static void panic_impl(const char* file, int line, const char* message, Args... args)
static void panic_impl(const char* file, int line, const char* message, Args&&... args)
{
asm volatile("cli");
derrorln("Kernel panic at {}:{}", file, line);
derrorln(message, args...);
derrorln(message, BAN::forward<Args>(args)...);
if (!g_paniced)
{
g_paniced = true;

View File

@ -6,18 +6,18 @@
#include <BAN/Vector.h>
#include <kernel/Credentials.h>
#include <kernel/FS/Inode.h>
#include <kernel/Memory/FixedWidthAllocator.h>
#include <kernel/Memory/GeneralAllocator.h>
#include <kernel/Memory/Heap.h>
#include <kernel/Memory/VirtualRange.h>
#include <kernel/Memory/MemoryRegion.h>
#include <kernel/OpenFileDescriptorSet.h>
#include <kernel/SpinLock.h>
#include <kernel/Terminal/TTY.h>
#include <kernel/Thread.h>
#include <sys/banan-os.h>
#include <sys/mman.h>
#include <termios.h>
namespace LibELF { class ELF; }
namespace LibELF { class LoadableELF; }
namespace Kernel
{
@ -39,11 +39,13 @@ namespace Kernel
};
public:
static Process* create_kernel();
static Process* create_kernel(entry_t, void*);
static BAN::ErrorOr<Process*> create_userspace(const Credentials&, BAN::StringView);
~Process();
void cleanup_function();
void register_to_scheduler();
void exit(int status, int signal);
static void for_each_process(const BAN::Function<BAN::Iteration(Process&)>& callback);
@ -58,6 +60,8 @@ namespace Kernel
bool is_session_leader() const { return pid() == sid(); }
const Credentials& credentials() const { return m_credentials; }
BAN::ErrorOr<long> sys_exit(int status);
BAN::ErrorOr<long> sys_gettermios(::termios*);
@ -70,8 +74,6 @@ namespace Kernel
BAN::ErrorOr<long> sys_sleep(int seconds);
BAN::ErrorOr<long> sys_nanosleep(const timespec* rqtp, timespec* rmtp);
BAN::ErrorOr<long> sys_setenvp(char** envp);
BAN::ErrorOr<long> sys_setpwd(const char* path);
BAN::ErrorOr<long> sys_getpwd(char* buffer, size_t size);
@ -89,12 +91,21 @@ namespace Kernel
BAN::ErrorOr<long> sys_getegid() const { return m_credentials.egid(); }
BAN::ErrorOr<long> sys_getpgid(pid_t);
BAN::ErrorOr<void> create_file(BAN::StringView name, mode_t mode);
BAN::ErrorOr<long> sys_open(BAN::StringView, int, mode_t = 0);
BAN::ErrorOr<long> sys_openat(int, BAN::StringView, int, mode_t = 0);
BAN::ErrorOr<void> create_file_or_dir(BAN::StringView name, mode_t mode);
BAN::ErrorOr<long> open_file(BAN::StringView path, int, mode_t = 0);
BAN::ErrorOr<long> sys_open(const char* path, int, mode_t);
BAN::ErrorOr<long> sys_openat(int, const char* path, int, mode_t);
BAN::ErrorOr<long> sys_close(int fd);
BAN::ErrorOr<long> sys_read(int fd, void* buffer, size_t count);
BAN::ErrorOr<long> sys_write(int fd, const void* buffer, size_t count);
BAN::ErrorOr<long> sys_create(const char*, mode_t);
BAN::ErrorOr<long> sys_create_dir(const char*, mode_t);
BAN::ErrorOr<long> sys_unlink(const char*);
BAN::ErrorOr<long> readlink_impl(BAN::StringView absolute_path, char* buffer, size_t bufsize);
BAN::ErrorOr<long> sys_readlink(const char* path, char* buffer, size_t bufsize);
BAN::ErrorOr<long> sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize);
BAN::ErrorOr<long> sys_chmod(const char*, mode_t);
BAN::ErrorOr<long> sys_pipe(int fildes[2]);
BAN::ErrorOr<long> sys_dup(int fildes);
@ -109,12 +120,18 @@ namespace Kernel
BAN::ErrorOr<long> sys_fstatat(int fd, const char* path, struct stat* buf, int flag);
BAN::ErrorOr<long> sys_stat(const char* path, struct stat* buf, int flag);
BAN::ErrorOr<long> sys_sync(bool should_block);
BAN::ErrorOr<long> sys_poweroff(int command);
BAN::ErrorOr<void> mount(BAN::StringView source, BAN::StringView target);
BAN::ErrorOr<long> sys_read_dir_entries(int fd, DirectoryEntryList* buffer, size_t buffer_size);
BAN::ErrorOr<long> sys_alloc(size_t);
BAN::ErrorOr<long> sys_free(void*);
BAN::ErrorOr<long> sys_mmap(const sys_mmap_t*);
BAN::ErrorOr<long> sys_munmap(void* addr, size_t len);
BAN::ErrorOr<long> sys_tty_ctrl(int fildes, int command, int flags);
BAN::ErrorOr<long> sys_signal(int, void (*)(int));
BAN::ErrorOr<long> sys_raise(int signal);
@ -122,9 +139,9 @@ namespace Kernel
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
BAN::ErrorOr<long> sys_termid(char*) const;
BAN::ErrorOr<long> sys_termid(char*);
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*) const;
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);
TTY& tty() { ASSERT(m_controlling_terminal); return *m_controlling_terminal; }
@ -132,24 +149,32 @@ namespace Kernel
PageTable& page_table() { return m_page_table ? *m_page_table : PageTable::kernel(); }
size_t proc_meminfo(off_t offset, BAN::ByteSpan) const;
size_t proc_cmdline(off_t offset, BAN::ByteSpan) const;
size_t proc_environ(off_t offset, BAN::ByteSpan) const;
bool is_userspace() const { return m_is_userspace; }
const userspace_info_t& userspace_info() const { return m_userspace_info; }
// Returns error if page could not be allocated
// Returns true if the page was allocated successfully
// Return false if access was page violation (segfault)
BAN::ErrorOr<bool> allocate_page_for_demand_paging(vaddr_t addr);
private:
Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp);
static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0);
static void register_process(Process*);
// Load an elf file to virtual address space of the current page table
static BAN::ErrorOr<BAN::UniqPtr<LibELF::ELF>> load_elf_for_exec(const Credentials&, BAN::StringView file_path, const BAN::String& cwd, const BAN::Vector<BAN::StringView>& path_env);
// Copy an elf file from the current page table to the processes own
void load_elf_to_memory(LibELF::ELF&);
// Load elf from a file
static BAN::ErrorOr<BAN::UniqPtr<LibELF::LoadableELF>> load_elf_for_exec(const Credentials&, BAN::StringView file_path, const BAN::String& cwd, Kernel::PageTable&);
int block_until_exit();
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
void validate_string_access(const char*);
void validate_pointer_access(const void*, size_t);
private:
struct ExitStatus
{
@ -163,7 +188,8 @@ namespace Kernel
OpenFileDescriptorSet m_open_file_descriptors;
BAN::Vector<BAN::UniqPtr<VirtualRange>> m_mapped_ranges;
BAN::UniqPtr<LibELF::LoadableELF> m_loadable_elf;
BAN::Vector<BAN::UniqPtr<MemoryRegion>> m_mapped_regions;
pid_t m_sid;
pid_t m_pgrp;
@ -175,12 +201,12 @@ namespace Kernel
BAN::String m_working_directory;
BAN::Vector<Thread*> m_threads;
BAN::Vector<BAN::UniqPtr<FixedWidthAllocator>> m_fixed_width_allocators;
BAN::UniqPtr<GeneralAllocator> m_general_allocator;
vaddr_t m_signal_handlers[_SIGMAX + 1] { };
uint64_t m_signal_pending_mask { 0 };
BAN::Vector<BAN::String> m_cmdline;
BAN::Vector<BAN::String> m_environ;
bool m_is_userspace { false };
userspace_info_t m_userspace_info;
ExitStatus m_exit_status;

View File

@ -31,6 +31,7 @@ namespace Kernel
static bool is_valid_tid(pid_t tid);
[[noreturn]] void execute_current_thread();
[[noreturn]] void _execute_current_thread();
[[noreturn]] void delete_current_process_and_thread();
private:

View File

@ -0,0 +1,43 @@
#pragma once
#include <BAN/Array.h>
#include <BAN/RefPtr.h>
#include <kernel/InterruptController.h>
#include <kernel/Memory/DMARegion.h>
#include <kernel/PCI.h>
#include <kernel/Storage/ATA/AHCI/Definitions.h>
namespace Kernel
{
class AHCIController final : public StorageController, public Interruptable
{
BAN_NON_COPYABLE(AHCIController);
BAN_NON_MOVABLE(AHCIController);
public:
~AHCIController();
virtual void handle_irq() override;
uint32_t command_slot_count() const { return m_command_slot_count; }
private:
AHCIController(PCI::Device& pci_device)
: m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();
BAN::Optional<AHCIPortType> check_port_type(volatile HBAPortMemorySpace&);
private:
PCI::Device& m_pci_device;
BAN::UniqPtr<PCI::BarRegion> m_abar;
BAN::Array<AHCIDevice*, 32> m_devices;
uint32_t m_command_slot_count { 0 };
friend class ATAController;
};
}

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