Merge pull request 'update main' (#1) from Bananymous/banan-os:main into main
Reviewed-on: Sinipelto/banan-os#1
This commit is contained in:
commit
9ad2ea8205
|
@ -2,7 +2,4 @@
|
||||||
.idea/
|
.idea/
|
||||||
build/
|
build/
|
||||||
base/
|
base/
|
||||||
*.tar.*
|
script/fakeroot-context
|
||||||
toolchain/*/
|
|
||||||
|
|
||||||
!base-sysroot.tar.gz
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[submodule "kernel/lai"]
|
||||||
|
path = kernel/lai
|
||||||
|
url = https://github.com/managarm/lai.git
|
||||||
|
ignore = untracked
|
|
@ -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/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/New.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
{
|
{
|
||||||
|
|
||||||
String::String()
|
String::String()
|
||||||
{
|
{
|
||||||
MUST(copy_impl(""sv));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(const String& other)
|
String::String(const String& other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other.sv()));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(String&& other)
|
String::String(String&& other)
|
||||||
{
|
{
|
||||||
move_impl(move(other));
|
*this = move(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
String::String(StringView other)
|
String::String(StringView other)
|
||||||
{
|
{
|
||||||
MUST(copy_impl(other));
|
*this = other;
|
||||||
}
|
}
|
||||||
|
|
||||||
String::~String()
|
String::~String()
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(const String& other)
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(String&& other)
|
String& String::operator=(String&& other)
|
||||||
{
|
{
|
||||||
BAN::deallocator(m_data);
|
clear();
|
||||||
move_impl(move(other));
|
|
||||||
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String& String::operator=(StringView other)
|
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;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::push_back(char ch)
|
ErrorOr<void> String::push_back(char c)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + 2));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
m_data[m_size] = ch;
|
data()[m_size] = c;
|
||||||
m_size++;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(char ch, size_type index)
|
ErrorOr<void> String::insert(char c, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + 1 + 1));
|
TRY(ensure_capacity(m_size + 1));
|
||||||
memmove(m_data + index + 1, m_data + index, m_size - index);
|
memmove(data() + index + 1, data() + index, m_size - index);
|
||||||
m_data[index] = ch;
|
data()[index] = c;
|
||||||
m_size += 1;
|
m_size++;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::insert(StringView other, size_type index)
|
ErrorOr<void> String::insert(StringView str, size_type index)
|
||||||
{
|
{
|
||||||
ASSERT(index <= m_size);
|
ASSERT(index <= m_size);
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memmove(m_data + index + other.size(), m_data + index, m_size - index);
|
memmove(data() + index + str.size(), data() + index, m_size - index);
|
||||||
memcpy(m_data + index, other.data(), other.size());
|
memcpy(data() + index, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::append(StringView other)
|
ErrorOr<void> String::append(StringView str)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(m_size + other.size() + 1));
|
TRY(ensure_capacity(m_size + str.size()));
|
||||||
memcpy(m_data + m_size, other.data(), other.size());
|
memcpy(data() + m_size, str.data(), str.size());
|
||||||
m_size += other.size();
|
m_size += str.size();
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<void> String::append(const String& string)
|
|
||||||
{
|
|
||||||
TRY(append(string.sv()));
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,159 +111,159 @@ namespace BAN
|
||||||
{
|
{
|
||||||
ASSERT(m_size > 0);
|
ASSERT(m_size > 0);
|
||||||
m_size--;
|
m_size--;
|
||||||
m_data[m_size] = '\0';
|
data()[m_size] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::remove(size_type index)
|
void String::remove(size_type index)
|
||||||
{
|
{
|
||||||
erase(index, 1);
|
ASSERT(index < m_size);
|
||||||
}
|
memcpy(data() + index, data() + index + 1, m_size - index);
|
||||||
|
m_size--;
|
||||||
void String::erase(size_type index, size_type count)
|
data()[m_size] = '\0';
|
||||||
{
|
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void String::clear()
|
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_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);
|
if (size() != str.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)
|
|
||||||
return false;
|
return false;
|
||||||
return memcmp(m_data, other.m_data, m_size) == 0;
|
for (size_type i = 0; i < m_size; i++)
|
||||||
}
|
if (data()[i] != str.data()[i])
|
||||||
|
|
||||||
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])
|
|
||||||
return false;
|
return false;
|
||||||
return true;
|
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])
|
||||||
m_data[size] = '\0';
|
return false;
|
||||||
m_size = size;
|
if (cstr[size()] != '\0')
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (size > m_size)
|
|
||||||
|
ErrorOr<void> String::resize(size_type new_size, char init_c)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(size + 1));
|
if (m_size == new_size)
|
||||||
for (size_type i = m_size; i < size; i++)
|
return {};
|
||||||
m_data[i] = ch;
|
|
||||||
m_data[size] = '\0';
|
// expanding
|
||||||
m_size = size;
|
if (m_size < new_size)
|
||||||
}
|
{
|
||||||
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::reserve(size_type size)
|
m_size = new_size;
|
||||||
|
data()[m_size] = '\0';
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> String::reserve(size_type new_size)
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(size));
|
TRY(ensure_capacity(new_size));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::shrink_to_fit()
|
ErrorOr<void> String::shrink_to_fit()
|
||||||
{
|
{
|
||||||
size_type temp = m_capacity;
|
if (has_sso())
|
||||||
m_capacity = 0;
|
return {};
|
||||||
auto error_or = ensure_capacity(m_size);
|
|
||||||
if (error_or.is_error())
|
if (fits_in_sso())
|
||||||
{
|
{
|
||||||
m_capacity = temp;
|
char* data = m_storage.general_storage.data;
|
||||||
return error_or;
|
m_storage.sso_storage = SSOStorage();
|
||||||
}
|
m_has_sso = true;
|
||||||
|
memcpy(this->data(), data, m_size + 1);
|
||||||
|
deallocator(data);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView String::sv() const
|
GeneralStorage& storage = m_storage.general_storage;
|
||||||
{
|
if (storage.capacity == m_size)
|
||||||
return StringView(*this);
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
bool String::empty() const
|
char* new_data = (char*)allocator(m_size + 1);
|
||||||
{
|
if (new_data == nullptr)
|
||||||
return m_size == 0;
|
return Error::from_errno(ENOMEM);
|
||||||
}
|
|
||||||
|
|
||||||
String::size_type String::size() const
|
memcpy(new_data, storage.data, m_size);
|
||||||
{
|
deallocator(storage.data);
|
||||||
return m_size;
|
|
||||||
|
storage.capacity = m_size;
|
||||||
|
storage.data = new_data;
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
String::size_type String::capacity() const
|
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
|
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 {};
|
return {};
|
||||||
size_type new_cap = BAN::Math::max<size_type>(size, m_capacity * 2);
|
if (has_sso() && fits_in_sso(new_size))
|
||||||
void* new_data = BAN::allocator(new_cap);
|
return {};
|
||||||
|
|
||||||
|
char* new_data = (char*)allocator(new_size + 1);
|
||||||
if (new_data == nullptr)
|
if (new_data == nullptr)
|
||||||
return Error::from_errno(ENOMEM);
|
return Error::from_errno(ENOMEM);
|
||||||
if (m_data)
|
|
||||||
memcpy(new_data, m_data, m_size + 1);
|
memcpy(new_data, data(), m_size + 1);
|
||||||
BAN::deallocator(m_data);
|
|
||||||
m_data = (char*)new_data;
|
if (has_sso())
|
||||||
m_capacity = new_cap;
|
{
|
||||||
|
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 {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> String::copy_impl(StringView other)
|
bool String::has_sso() const
|
||||||
{
|
{
|
||||||
TRY(ensure_capacity(other.size() + 1));
|
return m_has_sso;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,17 +10,15 @@ set(BAN_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(ban-headers
|
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
|
DEPENDS sysroot
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(ban ${BAN_SOURCES})
|
add_library(ban ${BAN_SOURCES})
|
||||||
add_dependencies(ban headers libc-install)
|
add_dependencies(ban headers libc-install)
|
||||||
|
|
||||||
add_custom_target(ban-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
|
DEPENDS ban
|
||||||
BYPRODUCTS ${BANAN_LIB}/libban.a
|
BYPRODUCTS ${BANAN_LIB}/libban.a
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
|
||||||
#if defined(__is_kernel)
|
#if defined(__is_kernel)
|
||||||
#include <kernel/Panic.h>
|
#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")
|
#define ASSERT_NOT_REACHED() Kernel::panic("ASSERT_NOT_REACHED() failed")
|
||||||
#else
|
#else
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
||||||
|
}
|
|
@ -7,20 +7,62 @@
|
||||||
namespace BAN
|
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>
|
template<integral T>
|
||||||
struct LittleEndian
|
struct LittleEndian
|
||||||
{
|
{
|
||||||
constexpr operator T() const
|
constexpr operator T() const
|
||||||
{
|
{
|
||||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
return host_to_little_endian(raw);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
T raw;
|
T raw;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -29,14 +71,7 @@ namespace BAN
|
||||||
{
|
{
|
||||||
constexpr operator T() const
|
constexpr operator T() const
|
||||||
{
|
{
|
||||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
return host_to_big_endian(raw);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
T raw;
|
T raw;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/Traits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace BAN
|
namespace BAN
|
||||||
|
@ -14,5 +15,6 @@ namespace BAN
|
||||||
class StringView;
|
class StringView;
|
||||||
template<typename> class Vector;
|
template<typename> class Vector;
|
||||||
template<typename> class LinkedList;
|
template<typename> class LinkedList;
|
||||||
|
template<typename... Ts> requires (!is_const_v<Ts> && ...) class Variant;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,7 +195,7 @@ namespace BAN
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void LinkedList<T>::pop_back()
|
void LinkedList<T>::pop_back()
|
||||||
{
|
{
|
||||||
return remove(m_last);
|
remove(iterator(m_last, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|
|
@ -102,22 +102,4 @@ namespace BAN::Math
|
||||||
return result;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -13,6 +13,8 @@ namespace BAN
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Optional();
|
Optional();
|
||||||
|
Optional(Optional&&);
|
||||||
|
Optional(const Optional&);
|
||||||
Optional(const T&);
|
Optional(const T&);
|
||||||
Optional(T&&);
|
Optional(T&&);
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
@ -20,8 +22,8 @@ namespace BAN
|
||||||
|
|
||||||
~Optional();
|
~Optional();
|
||||||
|
|
||||||
Optional& operator=(const Optional&);
|
|
||||||
Optional& operator=(Optional&&);
|
Optional& operator=(Optional&&);
|
||||||
|
Optional& operator=(const Optional&);
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
Optional& emplace(Args&&...);
|
Optional& emplace(Args&&...);
|
||||||
|
@ -34,9 +36,9 @@ namespace BAN
|
||||||
|
|
||||||
bool has_value() const;
|
bool has_value() const;
|
||||||
|
|
||||||
T&& release_value();
|
T release_value();
|
||||||
const T& value() const;
|
|
||||||
T& value();
|
T& value();
|
||||||
|
const T& value() const;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
@ -50,6 +52,22 @@ namespace BAN
|
||||||
: m_has_value(false)
|
: 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>
|
template<typename T>
|
||||||
Optional<T>::Optional(const T& value)
|
Optional<T>::Optional(const T& value)
|
||||||
: m_has_value(true)
|
: m_has_value(true)
|
||||||
|
@ -61,7 +79,7 @@ namespace BAN
|
||||||
Optional<T>::Optional(T&& value)
|
Optional<T>::Optional(T&& value)
|
||||||
: m_has_value(true)
|
: m_has_value(true)
|
||||||
{
|
{
|
||||||
new (m_storage) T(BAN::move(value));
|
new (m_storage) T(move(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -69,7 +87,7 @@ namespace BAN
|
||||||
Optional<T>::Optional(Args&&... args)
|
Optional<T>::Optional(Args&&... args)
|
||||||
: m_has_value(true)
|
: m_has_value(true)
|
||||||
{
|
{
|
||||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
new (m_storage) T(forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -79,26 +97,22 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Optional<T>& Optional<T>::operator=(const Optional& other)
|
Optional<T>& Optional<T>::operator=(Optional&& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
m_has_value = other.has_value();
|
||||||
if (other.has_value())
|
if (other.has_value())
|
||||||
{
|
new (m_storage) T(move(other.release_value()));
|
||||||
m_has_value = true;
|
|
||||||
new (m_storage) T(other.value());
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Optional<T>& Optional<T>::operator=(Optional&& other)
|
Optional<T>& Optional<T>::operator=(const Optional& other)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
if (other.has_value())
|
m_has_value = other.has_value();
|
||||||
{
|
if (other.has_value)
|
||||||
m_has_value = true;
|
new (m_storage) T(other.value());
|
||||||
new (m_storage) T(BAN::move(other.release_value()));
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +122,7 @@ namespace BAN
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
m_has_value = true;
|
m_has_value = true;
|
||||||
new (m_storage) T(BAN::forward<Args>(args)...);
|
new (m_storage) T(forward<Args>(args)...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,18 +161,13 @@ namespace BAN
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T&& Optional<T>::release_value()
|
T Optional<T>::release_value()
|
||||||
{
|
{
|
||||||
ASSERT(has_value());
|
ASSERT(has_value());
|
||||||
|
T released_value = move(value());
|
||||||
|
value().~T();
|
||||||
m_has_value = false;
|
m_has_value = false;
|
||||||
return BAN::move((T&)m_storage);
|
return move(released_value);
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
const T& Optional<T>::value() const
|
|
||||||
{
|
|
||||||
ASSERT(has_value());
|
|
||||||
return (const T&)m_storage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -168,6 +177,13 @@ namespace BAN
|
||||||
return (T&)m_storage;
|
return (T&)m_storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T& Optional<T>::value() const
|
||||||
|
{
|
||||||
|
ASSERT(has_value());
|
||||||
|
return (const T&)m_storage;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void Optional<T>::clear()
|
void Optional<T>::clear()
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,6 +43,8 @@ namespace BAN
|
||||||
|
|
||||||
Span slice(size_type, size_type = ~size_type(0));
|
Span slice(size_type, size_type = ~size_type(0));
|
||||||
|
|
||||||
|
Span<const T> as_const() const { return Span<const T>(m_data, m_size); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
T* m_data = nullptr;
|
T* m_data = nullptr;
|
||||||
size_type m_size = 0;
|
size_type m_size = 0;
|
||||||
|
@ -125,8 +127,8 @@ namespace BAN
|
||||||
ASSERT(start <= m_size);
|
ASSERT(start <= m_size);
|
||||||
if (length == ~size_type(0))
|
if (length == ~size_type(0))
|
||||||
length = m_size - start;
|
length = m_size - start;
|
||||||
ASSERT(start + length <= m_size);
|
ASSERT(m_size - start >= length);
|
||||||
return Span(m_data + start, m_size - start - length);
|
return Span(m_data + start, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <BAN/ForwardList.h>
|
|
||||||
#include <BAN/Formatter.h>
|
#include <BAN/Formatter.h>
|
||||||
|
#include <BAN/ForwardList.h>
|
||||||
#include <BAN/Hash.h>
|
#include <BAN/Hash.h>
|
||||||
#include <BAN/Iterators.h>
|
#include <BAN/Iterators.h>
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ namespace BAN
|
||||||
using size_type = size_t;
|
using size_type = size_t;
|
||||||
using iterator = IteratorSimple<char, String>;
|
using iterator = IteratorSimple<char, String>;
|
||||||
using const_iterator = ConstIteratorSimple<char, String>;
|
using const_iterator = ConstIteratorSimple<char, String>;
|
||||||
|
static constexpr size_type sso_capacity = 15;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
String();
|
String();
|
||||||
|
@ -34,29 +35,26 @@ namespace BAN
|
||||||
ErrorOr<void> insert(char, size_type);
|
ErrorOr<void> insert(char, size_type);
|
||||||
ErrorOr<void> insert(StringView, size_type);
|
ErrorOr<void> insert(StringView, size_type);
|
||||||
ErrorOr<void> append(StringView);
|
ErrorOr<void> append(StringView);
|
||||||
ErrorOr<void> append(const String&);
|
|
||||||
|
|
||||||
void pop_back();
|
void pop_back();
|
||||||
void remove(size_type);
|
void remove(size_type);
|
||||||
void erase(size_type, size_type);
|
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
const_iterator begin() const { return const_iterator(m_data); }
|
const_iterator begin() const { return const_iterator(data()); }
|
||||||
iterator begin() { return iterator(m_data); }
|
iterator begin() { return iterator(data()); }
|
||||||
const_iterator end() const { return const_iterator(m_data + m_size); }
|
const_iterator end() const { return const_iterator(data() + size()); }
|
||||||
iterator end() { return iterator(m_data + m_size); }
|
iterator end() { return iterator(data() + size()); }
|
||||||
|
|
||||||
char front() const { ASSERT(!empty()); return m_data[0]; }
|
char front() const { ASSERT(m_size > 0); return data()[0]; }
|
||||||
char& front() { ASSERT(!empty()); return m_data[0]; }
|
char& front() { ASSERT(m_size > 0); return data()[0]; }
|
||||||
|
|
||||||
char back() const { ASSERT(!empty()); return m_data[m_size - 1]; }
|
char back() const { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
char& back() { ASSERT(!empty()); return m_data[m_size - 1]; }
|
char& back() { ASSERT(m_size > 0); return data()[m_size - 1]; }
|
||||||
|
|
||||||
char operator[](size_type) const;
|
char operator[](size_type index) const { ASSERT(index < m_size); return data()[index]; }
|
||||||
char& operator[](size_type);
|
char& operator[](size_type index) { ASSERT(index < m_size); return data()[index]; }
|
||||||
|
|
||||||
bool operator==(const String&) const;
|
|
||||||
bool operator==(StringView) const;
|
bool operator==(StringView) const;
|
||||||
bool operator==(const char*) const;
|
bool operator==(const char*) const;
|
||||||
|
|
||||||
|
@ -64,24 +62,41 @@ namespace BAN
|
||||||
ErrorOr<void> reserve(size_type);
|
ErrorOr<void> reserve(size_type);
|
||||||
ErrorOr<void> shrink_to_fit();
|
ErrorOr<void> shrink_to_fit();
|
||||||
|
|
||||||
StringView sv() const;
|
StringView sv() const { return StringView(data(), size()); }
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const { return m_size == 0; }
|
||||||
size_type size() const;
|
size_type size() const { return m_size; }
|
||||||
size_type capacity() const;
|
size_type capacity() const;
|
||||||
|
|
||||||
|
char* data();
|
||||||
const char* data() const;
|
const char* data() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorOr<void> ensure_capacity(size_type);
|
ErrorOr<void> ensure_capacity(size_type);
|
||||||
|
|
||||||
ErrorOr<void> copy_impl(StringView);
|
bool has_sso() const;
|
||||||
void move_impl(String&&);
|
|
||||||
|
bool fits_in_sso() const { return fits_in_sso(m_size); }
|
||||||
|
static bool fits_in_sso(size_type size) { return size < sso_capacity; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char* m_data = nullptr;
|
struct SSOStorage
|
||||||
size_type m_capacity = 0;
|
{
|
||||||
size_type m_size = 0;
|
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>
|
template<typename... Args>
|
||||||
|
|
|
@ -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>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,42 +1,30 @@
|
||||||
cmake_minimum_required(VERSION 3.26)
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
if(DEFINED ENV{BANAN_ARCH})
|
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "banan-os")
|
||||||
set(BANAN_ARCH $ENV{BANAN_ARCH})
|
message(FATAL_ERROR "CMAKE_SYSTEM_NAME is not banan-os")
|
||||||
else()
|
endif ()
|
||||||
set(BANAN_ARCH x86_64)
|
|
||||||
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)
|
project(banan-os CXX C ASM)
|
||||||
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)
|
|
||||||
|
|
||||||
set(BANAN_BASE_SYSROOT ${CMAKE_SOURCE_DIR}/base-sysroot.tar.gz)
|
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_INCLUDE ${BANAN_SYSROOT}/usr/include)
|
||||||
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
|
set(BANAN_LIB ${BANAN_SYSROOT}/usr/lib)
|
||||||
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
|
set(BANAN_BIN ${BANAN_SYSROOT}/usr/bin)
|
||||||
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
set(BANAN_BOOT ${BANAN_SYSROOT}/boot)
|
||||||
set(DISK_IMAGE_PATH ${CMAKE_BINARY_DIR}/banan-os.img)
|
|
||||||
|
|
||||||
add_subdirectory(kernel)
|
add_subdirectory(kernel)
|
||||||
|
add_subdirectory(bootloader)
|
||||||
add_subdirectory(BAN)
|
add_subdirectory(BAN)
|
||||||
add_subdirectory(libc)
|
add_subdirectory(libc)
|
||||||
add_subdirectory(LibELF)
|
add_subdirectory(LibELF)
|
||||||
add_subdirectory(userspace)
|
add_subdirectory(userspace)
|
||||||
|
|
||||||
add_custom_target(sysroot
|
add_custom_target(sysroot
|
||||||
COMMAND mkdir -p ${BANAN_SYSROOT}
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${BANAN_SYSROOT}
|
||||||
COMMAND cd ${BANAN_SYSROOT} && sudo tar xf ${BANAN_BASE_SYSROOT}
|
COMMAND cd ${BANAN_SYSROOT} && tar xf ${BANAN_BASE_SYSROOT}
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(headers
|
add_custom_target(headers
|
||||||
|
@ -46,51 +34,11 @@ add_custom_target(headers
|
||||||
DEPENDS libelf-headers
|
DEPENDS libelf-headers
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(toolchain
|
add_custom_target(install-sysroot
|
||||||
COMMAND ${CMAKE_COMMAND} -E env SYSROOT="${BANAN_SYSROOT}" PREFIX="${TOOLCHAIN_PREFIX}" ARCH="${BANAN_ARCH}" ${CMAKE_SOURCE_DIR}/toolchain/build.sh
|
COMMAND cd ${BANAN_SYSROOT} && tar cf ${BANAN_SYSROOT_TAR} *
|
||||||
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
|
|
||||||
DEPENDS kernel-install
|
DEPENDS kernel-install
|
||||||
DEPENDS ban-install
|
DEPENDS ban-install
|
||||||
DEPENDS libc-install
|
DEPENDS libc-install
|
||||||
DEPENDS userspace-install
|
DEPENDS userspace-install
|
||||||
DEPENDS libelf-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
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,9 +3,8 @@ cmake_minimum_required(VERSION 3.26)
|
||||||
project(LibELF CXX)
|
project(LibELF CXX)
|
||||||
|
|
||||||
add_custom_target(libelf-headers
|
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
|
DEPENDS sysroot
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(libelf-install
|
add_custom_target(libelf-install
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <kernel/Arch.h>
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace LibELF
|
namespace LibELF
|
||||||
|
@ -153,4 +155,32 @@ namespace LibELF
|
||||||
Elf64Xword p_align;
|
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
|
||||||
|
|
||||||
}
|
}
|
33
README.md
33
README.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
# banan-os
|
# 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)
|
![screenshot from qemu running banan-os](assets/banan-os.png)
|
||||||
|
|
||||||
|
@ -14,39 +14,34 @@ 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.
|
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.
|
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.
|
> ***NOTE:*** The following step has to be done only once. This might take a long time since we are compiling binutils and gcc.
|
||||||
```sh
|
```sh
|
||||||
ninja toolchain
|
./script/build.sh toolchain
|
||||||
cmake -G Ninja --fresh .. # We need to reconfigure cmake to use the new compiler
|
|
||||||
ninja libstdc++
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
```sh
|
||||||
ninja qemu
|
./script/build.sh qemu
|
||||||
ninja bochs
|
./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:
|
You can also build the kernel or disk image without running it:
|
||||||
```sh
|
```sh
|
||||||
ninja kernel
|
./script/build.sh kernel
|
||||||
ninja image
|
./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
|
```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
|
### Contributing
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,3 @@
|
||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
|
add_subdirectory(bios)
|
|
@ -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)
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,15 @@
|
||||||
|
ENTRY(stage1_main)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x7C00;
|
||||||
|
.stage1 : { *(.stage1) }
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
stage2_start = .;
|
||||||
|
.stage2 : { *(.stage2) }
|
||||||
|
stage2_end = .;
|
||||||
|
|
||||||
|
. = ALIGN(512);
|
||||||
|
.bss : { *(.bss) }
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
build/
|
|
@ -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)
|
|
@ -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 {};
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 }
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
uint32_t crc32_checksum(const uint8_t* data, std::size_t count);
|
|
@ -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;
|
||||||
|
}
|
10
check-fs.sh
10
check-fs.sh
|
@ -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
|
|
|
@ -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
|
|
22
image.sh
22
image.sh
|
@ -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
|
|
|
@ -1,6 +1,6 @@
|
||||||
cmake_minimum_required(VERSION 3.26)
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
|
||||||
project(kernel CXX ASM)
|
project(kernel CXX C ASM)
|
||||||
|
|
||||||
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||||
set(ELF_FORMAT elf64-x86-64)
|
set(ELF_FORMAT elf64-x86-64)
|
||||||
|
@ -12,6 +12,7 @@ set(KERNEL_SOURCES
|
||||||
font/prefs.psf.o
|
font/prefs.psf.o
|
||||||
kernel/ACPI.cpp
|
kernel/ACPI.cpp
|
||||||
kernel/APIC.cpp
|
kernel/APIC.cpp
|
||||||
|
kernel/BootInfo.cpp
|
||||||
kernel/CPUID.cpp
|
kernel/CPUID.cpp
|
||||||
kernel/Debug.cpp
|
kernel/Debug.cpp
|
||||||
kernel/Device/Device.cpp
|
kernel/Device/Device.cpp
|
||||||
|
@ -24,20 +25,25 @@ set(KERNEL_SOURCES
|
||||||
kernel/FS/Ext2/Inode.cpp
|
kernel/FS/Ext2/Inode.cpp
|
||||||
kernel/FS/Inode.cpp
|
kernel/FS/Inode.cpp
|
||||||
kernel/FS/Pipe.cpp
|
kernel/FS/Pipe.cpp
|
||||||
kernel/FS/RamFS/FileSystem.cpp
|
kernel/FS/ProcFS/FileSystem.cpp
|
||||||
kernel/FS/RamFS/Inode.cpp
|
kernel/FS/ProcFS/Inode.cpp
|
||||||
|
kernel/FS/TmpFS/FileSystem.cpp
|
||||||
|
kernel/FS/TmpFS/Inode.cpp
|
||||||
kernel/FS/VirtualFileSystem.cpp
|
kernel/FS/VirtualFileSystem.cpp
|
||||||
kernel/Input/PS2Controller.cpp
|
kernel/Input/PS2Controller.cpp
|
||||||
kernel/Input/PS2Keyboard.cpp
|
kernel/Input/PS2Keyboard.cpp
|
||||||
kernel/Input/PS2Keymap.cpp
|
kernel/Input/PS2Keymap.cpp
|
||||||
kernel/InterruptController.cpp
|
kernel/InterruptController.cpp
|
||||||
kernel/kernel.cpp
|
kernel/kernel.cpp
|
||||||
kernel/Memory/FixedWidthAllocator.cpp
|
kernel/Memory/DMARegion.cpp
|
||||||
kernel/Memory/GeneralAllocator.cpp
|
kernel/Memory/FileBackedRegion.cpp
|
||||||
kernel/Memory/Heap.cpp
|
kernel/Memory/Heap.cpp
|
||||||
kernel/Memory/kmalloc.cpp
|
kernel/Memory/kmalloc.cpp
|
||||||
|
kernel/Memory/MemoryBackedRegion.cpp
|
||||||
|
kernel/Memory/MemoryRegion.cpp
|
||||||
kernel/Memory/PhysicalRange.cpp
|
kernel/Memory/PhysicalRange.cpp
|
||||||
kernel/Memory/VirtualRange.cpp
|
kernel/Memory/VirtualRange.cpp
|
||||||
|
kernel/Networking/E1000.cpp
|
||||||
kernel/OpenFileDescriptorSet.cpp
|
kernel/OpenFileDescriptorSet.cpp
|
||||||
kernel/Panic.cpp
|
kernel/Panic.cpp
|
||||||
kernel/PCI.cpp
|
kernel/PCI.cpp
|
||||||
|
@ -47,9 +53,11 @@ set(KERNEL_SOURCES
|
||||||
kernel/Semaphore.cpp
|
kernel/Semaphore.cpp
|
||||||
kernel/SpinLock.cpp
|
kernel/SpinLock.cpp
|
||||||
kernel/SSP.cpp
|
kernel/SSP.cpp
|
||||||
kernel/Storage/ATABus.cpp
|
kernel/Storage/ATA/AHCI/Controller.cpp
|
||||||
kernel/Storage/ATAController.cpp
|
kernel/Storage/ATA/AHCI/Device.cpp
|
||||||
kernel/Storage/ATADevice.cpp
|
kernel/Storage/ATA/ATABus.cpp
|
||||||
|
kernel/Storage/ATA/ATAController.cpp
|
||||||
|
kernel/Storage/ATA/ATADevice.cpp
|
||||||
kernel/Storage/DiskCache.cpp
|
kernel/Storage/DiskCache.cpp
|
||||||
kernel/Storage/StorageDevice.cpp
|
kernel/Storage/StorageDevice.cpp
|
||||||
kernel/Syscall.cpp
|
kernel/Syscall.cpp
|
||||||
|
@ -97,6 +105,14 @@ else()
|
||||||
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
|
message(FATAL_ERROR "unsupported architecure ${BANAN_ARCH}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
file(GLOB_RECURSE LAI_SOURCES
|
||||||
|
lai/*.c
|
||||||
|
)
|
||||||
|
set(LAI_SOURCES
|
||||||
|
${LAI_SOURCES}
|
||||||
|
kernel/lai_host.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(BAN_SOURCES
|
set(BAN_SOURCES
|
||||||
../BAN/BAN/New.cpp
|
../BAN/BAN/New.cpp
|
||||||
../BAN/BAN/String.cpp
|
../BAN/BAN/String.cpp
|
||||||
|
@ -110,11 +126,12 @@ set(LIBC_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LIBELF_SOURCES
|
set(LIBELF_SOURCES
|
||||||
../LibELF/LibELF/ELF.cpp
|
../LibELF/LibELF/LoadableELF.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(KERNEL_SOURCES
|
set(KERNEL_SOURCES
|
||||||
${KERNEL_SOURCES}
|
${KERNEL_SOURCES}
|
||||||
|
${LAI_SOURCES}
|
||||||
${BAN_SOURCES}
|
${BAN_SOURCES}
|
||||||
${LIBC_SOURCES}
|
${LIBC_SOURCES}
|
||||||
${LIBELF_SOURCES}
|
${LIBELF_SOURCES}
|
||||||
|
@ -127,10 +144,12 @@ target_compile_definitions(kernel PUBLIC __is_kernel)
|
||||||
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
target_compile_definitions(kernel PUBLIC __arch=${BANAN_ARCH})
|
||||||
|
|
||||||
target_compile_options(kernel PUBLIC -O2 -g)
|
target_compile_options(kernel PUBLIC -O2 -g)
|
||||||
target_compile_options(kernel PUBLIC -Wno-literal-suffix)
|
target_compile_options(kernel PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-Wno-literal-suffix -fno-rtti -fno-exceptions>)
|
||||||
target_compile_options(kernel PUBLIC -fno-rtti -fno-exceptions)
|
|
||||||
target_compile_options(kernel PUBLIC -fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
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)
|
if(ENABLE_KERNEL_UBSAN)
|
||||||
target_compile_options(kernel PUBLIC -fsanitize=undefined)
|
target_compile_options(kernel PUBLIC -fsanitize=undefined)
|
||||||
|
@ -146,27 +165,15 @@ endif()
|
||||||
|
|
||||||
target_link_options(kernel PUBLIC -ffreestanding -nostdlib)
|
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
|
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
|
DEPENDS sysroot
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_target(kernel-install
|
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
|
DEPENDS kernel
|
||||||
USES_TERMINAL
|
|
||||||
)
|
)
|
||||||
|
|
||||||
execute_process(COMMAND ${CMAKE_CXX_COMPILER} -print-file-name=crtbegin.o OUTPUT_VARIABLE CRTBEGIN OUTPUT_STRIP_TRAILING_WHITESPACE)
|
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
|
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}/crti.S ${COMPILE_OPTIONS}
|
||||||
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
|
COMMAND ${CMAKE_CXX_COMPILER} -MD -c ${CMAKE_CURRENT_SOURCE_DIR}/arch/${BANAN_ARCH}/crtn.S ${COMPILE_OPTIONS}
|
||||||
COMMAND cp ${CRTBEGIN} .
|
COMMAND ${CMAKE_COMMAND} -E copy ${CRTBEGIN} .
|
||||||
COMMAND cp ${CRTEND} .
|
COMMAND ${CMAKE_COMMAND} -E copy ${CRTEND} .
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(
|
#add_custom_command(
|
||||||
TARGET kernel POST_BUILD
|
# TARGET kernel POST_BUILD
|
||||||
COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
# COMMAND x86_64-banan_os-strip ${CMAKE_CURRENT_BINARY_DIR}/kernel
|
||||||
)
|
#)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT font/prefs.psf.o
|
OUTPUT font/prefs.psf.o
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include <BAN/Assert.h>
|
#include <BAN/Array.h>
|
||||||
#include <kernel/GDT.h>
|
#include <kernel/GDT.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -56,44 +56,44 @@ namespace Kernel::GDT
|
||||||
|
|
||||||
static constexpr uint16_t s_tss_offset = 0x28;
|
static constexpr uint16_t s_tss_offset = 0x28;
|
||||||
|
|
||||||
static TaskStateSegment* s_tss = nullptr;
|
static TaskStateSegment s_tss;
|
||||||
static SegmentDescriptor* s_gdt = nullptr;
|
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 GDTR s_gdtr;
|
||||||
|
|
||||||
static void write_entry(uint8_t offset, uint32_t base, uint32_t limit, uint8_t access, uint8_t flags)
|
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);
|
ASSERT(offset % sizeof(SegmentDescriptor) == 0);
|
||||||
desc.base1 = base;
|
|
||||||
desc.base2 = base >> 16;
|
|
||||||
desc.base3 = base >> 24;
|
|
||||||
|
|
||||||
desc.limit1 = limit;
|
SegmentDescriptor& desc = s_gdt[offset / sizeof(SegmentDescriptor)];
|
||||||
desc.limit2 = limit >> 16;
|
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()
|
static void write_tss()
|
||||||
{
|
{
|
||||||
s_tss = new TaskStateSegment();
|
memset(&s_tss, 0x00, sizeof(TaskStateSegment));
|
||||||
ASSERT(s_tss);
|
s_tss.iopb = sizeof(TaskStateSegment);
|
||||||
|
|
||||||
memset(s_tss, 0x00, sizeof(TaskStateSegment));
|
uint64_t base = (uint64_t)&s_tss;
|
||||||
s_tss->rsp0 = 0;
|
|
||||||
|
|
||||||
uintptr_t base = (uintptr_t)s_tss;
|
|
||||||
|
|
||||||
write_entry(s_tss_offset, (uint32_t)base, sizeof(TaskStateSegment), 0x89, 0x0);
|
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.low = base >> 32;
|
||||||
desc.high = 0;
|
desc.high = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_tss_stack(uintptr_t rsp)
|
void set_tss_stack(uintptr_t rsp)
|
||||||
{
|
{
|
||||||
s_tss->rsp0 = rsp;
|
s_tss.rsp0 = rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_gdt()
|
static void flush_gdt()
|
||||||
|
@ -108,12 +108,8 @@ namespace Kernel::GDT
|
||||||
|
|
||||||
void initialize()
|
void initialize()
|
||||||
{
|
{
|
||||||
constexpr uint32_t descriptor_count = 6 + 1; // tss takes 2
|
s_gdtr.address = (uint64_t)&s_gdt;
|
||||||
s_gdt = new SegmentDescriptor[descriptor_count];
|
s_gdtr.size = s_gdt.size() * sizeof(SegmentDescriptor) - 1;
|
||||||
ASSERT(s_gdt);
|
|
||||||
|
|
||||||
s_gdtr.address = (uint64_t)s_gdt;
|
|
||||||
s_gdtr.size = descriptor_count * sizeof(SegmentDescriptor) - 1;
|
|
||||||
|
|
||||||
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
|
write_entry(0x00, 0x00000000, 0x00000, 0x00, 0x0); // null
|
||||||
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
|
write_entry(0x08, 0x00000000, 0xFFFFF, 0x9A, 0xA); // kernel code
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <BAN/Array.h>
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
#include <kernel/IDT.h>
|
#include <kernel/IDT.h>
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
|
@ -8,12 +9,10 @@
|
||||||
#include <kernel/Scheduler.h>
|
#include <kernel/Scheduler.h>
|
||||||
#include <kernel/Timer/PIT.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)
|
namespace Kernel::IDT
|
||||||
#define REGISTER_IRQ_HANDLER(i) register_interrupt_handler(IRQ_VECTOR_BASE + i, irq ## i)
|
|
||||||
|
|
||||||
namespace IDT
|
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Registers
|
struct Registers
|
||||||
|
@ -63,7 +62,9 @@ namespace IDT
|
||||||
static IDTR s_idtr;
|
static IDTR s_idtr;
|
||||||
static GateDescriptor* s_idt = nullptr;
|
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
|
enum ISR
|
||||||
{
|
{
|
||||||
|
@ -101,6 +102,29 @@ namespace IDT
|
||||||
UnkownException0x1F,
|
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[] =
|
static const char* isr_exceptions[] =
|
||||||
{
|
{
|
||||||
"Division Error",
|
"Division Error",
|
||||||
|
@ -137,15 +161,79 @@ namespace IDT
|
||||||
"Unkown Exception 0x1F",
|
"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;
|
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||||
|
|
||||||
if (from_userspace)
|
if (from_userspace)
|
||||||
Kernel::Thread::current().save_sse();
|
Thread::current().save_sse();
|
||||||
|
#endif
|
||||||
|
|
||||||
pid_t tid = Kernel::Scheduler::current_tid();
|
pid_t tid = Scheduler::current_tid();
|
||||||
pid_t pid = tid ? Kernel::Process::current().pid() : 0;
|
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(
|
dwarnln(
|
||||||
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
|
"{} (error code: 0x{16H}), pid {}, tid {}\r\n"
|
||||||
|
@ -160,14 +248,11 @@ namespace IDT
|
||||||
regs->rip, regs->rflags,
|
regs->rip, regs->rflags,
|
||||||
regs->cr0, regs->cr2, regs->cr3, regs->cr4
|
regs->cr0, regs->cr2, regs->cr3, regs->cr4
|
||||||
);
|
);
|
||||||
|
if (isr == ISR::PageFault)
|
||||||
|
PageTable::current().debug_dump();
|
||||||
|
Debug::dump_stack_trace();
|
||||||
|
|
||||||
if (tid)
|
if (tid && Thread::current().is_userspace())
|
||||||
{
|
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
// TODO: Confirm and fix the exception to signal mappings
|
// TODO: Confirm and fix the exception to signal mappings
|
||||||
|
|
||||||
|
@ -195,33 +280,38 @@ namespace IDT
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Kernel::Thread::current().handle_signal(signal);
|
Thread::current().handle_signal(signal);
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (from_userspace)
|
||||||
{
|
{
|
||||||
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
|
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||||
Kernel::Thread::current().load_sse();
|
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;
|
bool from_userspace = (interrupt_stack.cs & 0b11) == 0b11;
|
||||||
|
|
||||||
if (from_userspace)
|
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);
|
Thread::current().set_return_rsp(interrupt_stack.rsp);
|
||||||
Kernel::Thread::current().set_return_rip(interrupt_stack.rip);
|
Thread::current().set_return_rip(interrupt_stack.rip);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InterruptController::get().is_in_service(irq))
|
if (!InterruptController::get().is_in_service(irq))
|
||||||
|
@ -229,21 +319,23 @@ namespace IDT
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InterruptController::get().eoi(irq);
|
InterruptController::get().eoi(irq);
|
||||||
if (s_irq_handlers[irq])
|
if (s_interruptables[irq])
|
||||||
s_irq_handlers[irq]();
|
s_interruptables[irq]->handle_irq();
|
||||||
else
|
else
|
||||||
dprintln("no handler for irq 0x{2H}\n", irq);
|
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)
|
if (from_userspace)
|
||||||
{
|
{
|
||||||
ASSERT(Kernel::Thread::current().state() == Kernel::Thread::State::Executing);
|
ASSERT(Thread::current().state() == Thread::State::Executing);
|
||||||
Kernel::Thread::current().load_sse();
|
Thread::current().load_sse();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_idt()
|
static void flush_idt()
|
||||||
|
@ -269,60 +361,20 @@ namespace IDT
|
||||||
s_idt[index].flags = 0xEE;
|
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();
|
#define X(num) extern "C" void isr ## num();
|
||||||
extern "C" void isr1();
|
ISR_LIST_X
|
||||||
extern "C" void isr2();
|
#undef X
|
||||||
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();
|
|
||||||
|
|
||||||
extern "C" void irq0();
|
#define X(num) extern "C" void irq ## num();
|
||||||
extern "C" void irq1();
|
IRQ_LIST_X
|
||||||
extern "C" void irq2();
|
#undef X
|
||||||
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();
|
|
||||||
|
|
||||||
extern "C" void syscall_asm();
|
extern "C" void syscall_asm();
|
||||||
|
|
||||||
|
@ -335,59 +387,27 @@ namespace IDT
|
||||||
s_idtr.offset = (uint64_t)s_idt;
|
s_idtr.offset = (uint64_t)s_idt;
|
||||||
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
|
s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1;
|
||||||
|
|
||||||
REGISTER_ISR_HANDLER(0);
|
#define X(num) register_interrupt_handler(num, isr ## num);
|
||||||
REGISTER_ISR_HANDLER(1);
|
ISR_LIST_X
|
||||||
REGISTER_ISR_HANDLER(2);
|
#undef X
|
||||||
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);
|
|
||||||
|
|
||||||
REGISTER_IRQ_HANDLER(0);
|
#define X(num) register_interrupt_handler(IRQ_VECTOR_BASE + num, irq ## num);
|
||||||
REGISTER_IRQ_HANDLER(1);
|
IRQ_LIST_X
|
||||||
REGISTER_IRQ_HANDLER(2);
|
#undef X
|
||||||
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);
|
|
||||||
|
|
||||||
register_syscall_handler(0x80, syscall_asm);
|
register_syscall_handler(0x80, syscall_asm);
|
||||||
|
|
||||||
flush_idt();
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
#include <BAN/Errors.h>
|
|
||||||
#include <kernel/Arch.h>
|
#include <kernel/Arch.h>
|
||||||
#include <kernel/CPUID.h>
|
#include <kernel/CPUID.h>
|
||||||
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/LockGuard.h>
|
#include <kernel/LockGuard.h>
|
||||||
#include <kernel/Memory/kmalloc.h>
|
#include <kernel/Memory/kmalloc.h>
|
||||||
#include <kernel/Memory/PageTable.h>
|
#include <kernel/Memory/PageTable.h>
|
||||||
|
@ -91,6 +91,13 @@ namespace Kernel
|
||||||
s_has_pge = true;
|
s_has_pge = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enable write protect to kernel
|
||||||
|
asm volatile(
|
||||||
|
"movq %cr0, %rax;"
|
||||||
|
"orq $0x10000, %rax;"
|
||||||
|
"movq %rax, %cr0;"
|
||||||
|
);
|
||||||
|
|
||||||
ASSERT(s_kernel == nullptr);
|
ASSERT(s_kernel == nullptr);
|
||||||
s_kernel = new PageTable();
|
s_kernel = new PageTable();
|
||||||
ASSERT(s_kernel);
|
ASSERT(s_kernel);
|
||||||
|
@ -110,6 +117,13 @@ namespace Kernel
|
||||||
return *s_current;
|
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()
|
static uint64_t* allocate_zeroed_page_aligned_page()
|
||||||
{
|
{
|
||||||
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
|
void* page = kmalloc(PAGE_SIZE, PAGE_SIZE, true);
|
||||||
|
@ -128,8 +142,24 @@ namespace Kernel
|
||||||
uint64_t* pml4 = (uint64_t*)P2V(m_highest_paging_struct);
|
uint64_t* pml4 = (uint64_t*)P2V(m_highest_paging_struct);
|
||||||
pml4[511] = s_global_pml4e;
|
pml4[511] = s_global_pml4e;
|
||||||
|
|
||||||
// Map (0 -> phys_kernel_end) to (KERNEL_OFFSET -> virt_kernel_end)
|
prepare_fast_page();
|
||||||
map_range_at(0, KERNEL_OFFSET, (uintptr_t)g_kernel_end - KERNEL_OFFSET, Flags::ReadWrite | Flags::Present);
|
|
||||||
|
// 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 executable kernel memory as executable
|
||||||
map_range_at(
|
map_range_at(
|
||||||
|
@ -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()
|
BAN::ErrorOr<PageTable*> PageTable::create_userspace()
|
||||||
{
|
{
|
||||||
LockGuard _(s_kernel->m_lock);
|
LockGuard _(s_kernel->m_lock);
|
||||||
|
@ -209,13 +309,16 @@ namespace Kernel
|
||||||
void PageTable::invalidate(vaddr_t vaddr)
|
void PageTable::invalidate(vaddr_t vaddr)
|
||||||
{
|
{
|
||||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
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)
|
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);
|
Kernel::panic("unmapping {8H}, kernel: {}", vaddr, this == s_kernel);
|
||||||
|
|
||||||
ASSERT(is_canonical(vaddr));
|
ASSERT(is_canonical(vaddr));
|
||||||
|
@ -257,7 +360,11 @@ namespace Kernel
|
||||||
|
|
||||||
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags)
|
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);
|
Kernel::panic("mapping {8H} to {8H}, kernel: {}", paddr, vaddr, this == s_kernel);
|
||||||
|
|
||||||
ASSERT(is_canonical(vaddr));
|
ASSERT(is_canonical(vaddr));
|
||||||
|
@ -324,12 +431,11 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
ASSERT(is_canonical(vaddr));
|
ASSERT(is_canonical(vaddr));
|
||||||
|
|
||||||
|
ASSERT(vaddr);
|
||||||
ASSERT(paddr % PAGE_SIZE == 0);
|
ASSERT(paddr % PAGE_SIZE == 0);
|
||||||
ASSERT(vaddr % PAGE_SIZE == 0);
|
ASSERT(vaddr % PAGE_SIZE == 0);
|
||||||
|
|
||||||
size_t first_page = vaddr / PAGE_SIZE;
|
size_t page_count = range_page_count(vaddr, size);
|
||||||
size_t last_page = (vaddr + size - 1) / PAGE_SIZE;
|
|
||||||
size_t page_count = last_page - first_page + 1;
|
|
||||||
|
|
||||||
LockGuard _(m_lock);
|
LockGuard _(m_lock);
|
||||||
for (size_t page = 0; page < page_count; page++)
|
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)
|
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)
|
if (size_t rem = first_address % PAGE_SIZE)
|
||||||
first_address += PAGE_SIZE - rem;
|
first_address += PAGE_SIZE - rem;
|
||||||
if (size_t rem = last_address % PAGE_SIZE)
|
if (size_t rem = last_address % PAGE_SIZE)
|
||||||
|
@ -463,8 +571,9 @@ namespace Kernel
|
||||||
vaddr |= (uint64_t)pdpte << 30;
|
vaddr |= (uint64_t)pdpte << 30;
|
||||||
vaddr |= (uint64_t)pde << 21;
|
vaddr |= (uint64_t)pde << 21;
|
||||||
vaddr |= (uint64_t)pte << 12;
|
vaddr |= (uint64_t)pte << 12;
|
||||||
|
vaddr = canonicalize(vaddr);
|
||||||
ASSERT(reserve_page(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)
|
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)
|
if (size_t rem = first_address % PAGE_SIZE)
|
||||||
first_address += PAGE_SIZE - rem;
|
first_address += PAGE_SIZE - rem;
|
||||||
if (size_t rem = last_address % PAGE_SIZE)
|
if (size_t rem = last_address % PAGE_SIZE)
|
||||||
|
|
|
@ -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_PRESENT, 1<<0
|
||||||
.set PG_READ_WRITE, 1<<1
|
.set PG_READ_WRITE, 1<<1
|
||||||
.set PG_PAGE_SIZE, 1<<7
|
.set PG_PAGE_SIZE, 1<<7
|
||||||
|
@ -15,19 +7,37 @@
|
||||||
|
|
||||||
.code32
|
.code32
|
||||||
|
|
||||||
# Multiboot header
|
# multiboot2 header
|
||||||
.section .multiboot, "aw"
|
.section .multiboot, "aw"
|
||||||
.align 4
|
multiboot2_start:
|
||||||
.long MB_MAGIC
|
.align 8
|
||||||
.long MB_FLAGS
|
.long 0xE85250D6
|
||||||
.long MB_CHECKSUM
|
|
||||||
.skip 20
|
|
||||||
|
|
||||||
.long 0
|
.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 800
|
||||||
.long 600
|
.long 600
|
||||||
.long 32
|
.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
|
.section .bss, "aw", @nobits
|
||||||
# Create stack
|
# Create stack
|
||||||
.global g_boot_stack_bottom
|
.global g_boot_stack_bottom
|
||||||
|
@ -40,11 +50,9 @@
|
||||||
g_kernel_cmdline:
|
g_kernel_cmdline:
|
||||||
.skip 4096
|
.skip 4096
|
||||||
|
|
||||||
.global g_multiboot_info
|
bootloader_magic:
|
||||||
g_multiboot_info:
|
|
||||||
.skip 8
|
.skip 8
|
||||||
.global g_multiboot_magic
|
bootloader_info:
|
||||||
g_multiboot_magic:
|
|
||||||
.skip 8
|
.skip 8
|
||||||
|
|
||||||
.section .data
|
.section .data
|
||||||
|
@ -119,19 +127,6 @@ check_requirements:
|
||||||
.exit:
|
.exit:
|
||||||
jmp system_halt
|
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:
|
enable_sse:
|
||||||
movl %cr0, %eax
|
movl %cr0, %eax
|
||||||
andw $0xFFFB, %ax
|
andw $0xFFFB, %ax
|
||||||
|
@ -170,10 +165,9 @@ initialize_paging:
|
||||||
_start:
|
_start:
|
||||||
# Initialize stack and multiboot info
|
# Initialize stack and multiboot info
|
||||||
movl $V2P(g_boot_stack_top), %esp
|
movl $V2P(g_boot_stack_top), %esp
|
||||||
movl %eax, V2P(g_multiboot_magic)
|
movl %eax, V2P(bootloader_magic)
|
||||||
movl %ebx, V2P(g_multiboot_info)
|
movl %ebx, V2P(bootloader_info)
|
||||||
|
|
||||||
call copy_kernel_commandline
|
|
||||||
call check_requirements
|
call check_requirements
|
||||||
call enable_sse
|
call enable_sse
|
||||||
|
|
||||||
|
@ -201,13 +195,14 @@ long_mode:
|
||||||
jmp *%rcx
|
jmp *%rcx
|
||||||
|
|
||||||
higher_half:
|
higher_half:
|
||||||
addq $KERNEL_OFFSET, g_multiboot_info
|
|
||||||
|
|
||||||
# call global constuctors
|
# call global constuctors
|
||||||
call _init
|
call _init
|
||||||
|
|
||||||
# call to the kernel itself (clear ebp for stacktrace)
|
# call to the kernel itself (clear rbp for stacktrace)
|
||||||
xorq %rbp, %rbp
|
xorq %rbp, %rbp
|
||||||
|
|
||||||
|
movl V2P(bootloader_magic), %edi
|
||||||
|
movl V2P(bootloader_info), %esi
|
||||||
call kernel_main
|
call kernel_main
|
||||||
|
|
||||||
# call global destructors
|
# call global destructors
|
||||||
|
|
|
@ -158,6 +158,22 @@ irq 12
|
||||||
irq 13
|
irq 13
|
||||||
irq 14
|
irq 14
|
||||||
irq 15
|
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
|
// arguments in RAX, RBX, RCX, RDX, RSI, RDI
|
||||||
// System V ABI: RDI, RSI, RDX, RCX, R8, R9
|
// System V ABI: RDI, RSI, RDX, RCX, R8, R9
|
||||||
|
|
|
@ -108,26 +108,37 @@ namespace Kernel
|
||||||
static BAN::ErrorOr<void> initialize();
|
static BAN::ErrorOr<void> initialize();
|
||||||
static ACPI& get();
|
static ACPI& get();
|
||||||
|
|
||||||
const SDTHeader* get_header(const char[4]);
|
const SDTHeader* get_header(BAN::StringView signature, uint32_t index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ACPI() = default;
|
ACPI() = default;
|
||||||
BAN::ErrorOr<void> initialize_impl();
|
BAN::ErrorOr<void> initialize_impl();
|
||||||
|
|
||||||
const SDTHeader* get_header_from_index(size_t);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
paddr_t m_header_table_paddr = 0;
|
paddr_t m_header_table_paddr = 0;
|
||||||
vaddr_t m_header_table_vaddr = 0;
|
vaddr_t m_header_table_vaddr = 0;
|
||||||
uint32_t m_entry_size = 0;
|
uint32_t m_entry_size = 0;
|
||||||
uint32_t m_entry_count = 0;
|
|
||||||
|
|
||||||
struct MappedPage
|
struct MappedPage
|
||||||
{
|
{
|
||||||
Kernel::paddr_t paddr;
|
Kernel::paddr_t paddr;
|
||||||
Kernel::vaddr_t vaddr;
|
Kernel::vaddr_t vaddr;
|
||||||
|
|
||||||
|
SDTHeader* as_header() { return (SDTHeader*)vaddr; }
|
||||||
};
|
};
|
||||||
BAN::Vector<MappedPage> m_mapped_headers;
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,23 +4,26 @@
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
#include <kernel/Memory/Types.h>
|
#include <kernel/Memory/Types.h>
|
||||||
|
|
||||||
class APIC final : public InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
class APIC final : public InterruptController
|
||||||
|
{
|
||||||
|
public:
|
||||||
virtual void eoi(uint8_t) override;
|
virtual void eoi(uint8_t) override;
|
||||||
virtual void enable_irq(uint8_t) override;
|
virtual void enable_irq(uint8_t) override;
|
||||||
virtual bool is_in_service(uint8_t) override;
|
virtual bool is_in_service(uint8_t) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t read_from_local_apic(ptrdiff_t);
|
uint32_t read_from_local_apic(ptrdiff_t);
|
||||||
void write_to_local_apic(ptrdiff_t, uint32_t);
|
void write_to_local_apic(ptrdiff_t, uint32_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~APIC() { ASSERT_NOT_REACHED(); }
|
~APIC() { ASSERT_NOT_REACHED(); }
|
||||||
static APIC* create();
|
static APIC* create();
|
||||||
friend class InterruptController;
|
friend class InterruptController;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Processor
|
struct Processor
|
||||||
{
|
{
|
||||||
enum Flags : uint8_t
|
enum Flags : uint8_t
|
||||||
|
@ -45,10 +48,12 @@ private:
|
||||||
void write(uint8_t offset, uint32_t data);
|
void write(uint8_t offset, uint32_t data);
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<Processor> m_processors;
|
BAN::Vector<Processor> m_processors;
|
||||||
Kernel::paddr_t m_local_apic_paddr = 0;
|
Kernel::paddr_t m_local_apic_paddr = 0;
|
||||||
Kernel::vaddr_t m_local_apic_vaddr = 0;
|
Kernel::vaddr_t m_local_apic_vaddr = 0;
|
||||||
BAN::Vector<IOAPIC> m_io_apics;
|
BAN::Vector<IOAPIC> m_io_apics;
|
||||||
uint8_t m_irq_overrides[0x100] {};
|
uint8_t m_irq_overrides[0x100] {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -21,4 +21,8 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
extern "C" uintptr_t read_rip();
|
extern "C" uintptr_t read_rip();
|
||||||
|
#else
|
||||||
|
extern uintptr_t read_rip();
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#define ALWAYS_INLINE inline __attribute__((always_inline))
|
#define ALWAYS_INLINE inline __attribute__((always_inline))
|
||||||
|
#define NEVER_INLINE __attribute__((noinline))
|
||||||
|
|
|
@ -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));
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
|
@ -29,6 +29,24 @@
|
||||||
Debug::DebugLock::unlock(); \
|
Debug::DebugLock::unlock(); \
|
||||||
} while(false)
|
} 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")
|
#define BOCHS_BREAK() asm volatile("xchgw %bx, %bx")
|
||||||
|
|
||||||
namespace Debug
|
namespace Debug
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/FS/RamFS/Inode.h>
|
#include <kernel/FS/TmpFS/Inode.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
class Device : public RamInode
|
class Device : public TmpInode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Device() = default;
|
virtual ~Device() = default;
|
||||||
|
@ -13,9 +13,12 @@ namespace Kernel
|
||||||
|
|
||||||
virtual bool is_device() const override { return true; }
|
virtual bool is_device() const override { return true; }
|
||||||
virtual bool is_partition() const { return false; }
|
virtual bool is_partition() const { return false; }
|
||||||
|
virtual bool is_storage_device() const { return false; }
|
||||||
|
|
||||||
virtual dev_t rdev() const override = 0;
|
virtual dev_t rdev() const override = 0;
|
||||||
|
|
||||||
|
virtual BAN::StringView name() const = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Device(mode_t, uid_t, gid_t);
|
Device(mode_t, uid_t, gid_t);
|
||||||
};
|
};
|
||||||
|
@ -24,9 +27,9 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
BlockDevice(mode_t mode, uid_t uid, gid_t gid)
|
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:
|
protected:
|
||||||
CharacterDevice(mode_t mode, uid_t uid, gid_t gid)
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/Device/Device.h>
|
#include <kernel/Device/Device.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
|
@ -10,14 +12,16 @@ namespace Kernel
|
||||||
|
|
||||||
virtual dev_t rdev() const override { return m_rdev; }
|
virtual dev_t rdev() const override { return m_rdev; }
|
||||||
|
|
||||||
|
virtual BAN::StringView name() const override { return "null"sv; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NullDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
|
NullDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
|
||||||
: CharacterDevice(mode, uid, gid)
|
: CharacterDevice(mode, uid, gid)
|
||||||
, m_rdev(rdev)
|
, m_rdev(rdev)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual BAN::ErrorOr<size_t> read_impl(off_t, void*, size_t) override { return 0; }
|
virtual BAN::ErrorOr<size_t> read_impl(off_t, BAN::ByteSpan) 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> write_impl(off_t, BAN::ConstByteSpan buffer) override { return buffer.size(); };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const dev_t m_rdev;
|
const dev_t m_rdev;
|
||||||
|
|
|
@ -10,14 +10,16 @@ namespace Kernel
|
||||||
|
|
||||||
virtual dev_t rdev() const override { return m_rdev; }
|
virtual dev_t rdev() const override { return m_rdev; }
|
||||||
|
|
||||||
|
virtual BAN::StringView name() const override { return "zero"sv; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ZeroDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
|
ZeroDevice(mode_t mode, uid_t uid, gid_t gid, dev_t rdev)
|
||||||
: CharacterDevice(mode, uid, gid)
|
: CharacterDevice(mode, uid, gid)
|
||||||
, m_rdev(rdev)
|
, m_rdev(rdev)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
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;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t size) override { return size; };
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan buffer) override { return buffer.size(); };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const dev_t m_rdev;
|
const dev_t m_rdev;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/Device/Device.h>
|
#include <kernel/Device/Device.h>
|
||||||
#include <kernel/FS/RamFS/FileSystem.h>
|
#include <kernel/FS/TmpFS/FileSystem.h>
|
||||||
|
#include <kernel/Semaphore.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
class DevFileSystem final : public RamFileSystem
|
class DevFileSystem final : public TmpFileSystem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void initialize();
|
static void initialize();
|
||||||
|
@ -14,17 +15,26 @@ namespace Kernel
|
||||||
|
|
||||||
void initialize_device_updater();
|
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:
|
private:
|
||||||
DevFileSystem(size_t size)
|
DevFileSystem()
|
||||||
: RamFileSystem(size)
|
: TmpFileSystem(-1)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SpinLock m_device_lock;
|
mutable SpinLock m_device_lock;
|
||||||
|
|
||||||
|
Semaphore m_sync_done;
|
||||||
|
Semaphore m_sync_semaphore;
|
||||||
|
volatile bool m_should_sync { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/HashMap.h>
|
||||||
#include <kernel/Storage/StorageDevice.h>
|
#include <kernel/Storage/StorageDevice.h>
|
||||||
#include <kernel/FS/FileSystem.h>
|
#include <kernel/FS/FileSystem.h>
|
||||||
#include <kernel/FS/Ext2/Inode.h>
|
#include <kernel/FS/Ext2/Inode.h>
|
||||||
|
@ -9,6 +10,40 @@ namespace Kernel
|
||||||
|
|
||||||
class Ext2FS final : public FileSystem
|
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:
|
public:
|
||||||
static BAN::ErrorOr<Ext2FS*> create(Partition&);
|
static BAN::ErrorOr<Ext2FS*> create(Partition&);
|
||||||
|
|
||||||
|
@ -23,14 +58,19 @@ namespace Kernel
|
||||||
BAN::ErrorOr<void> initialize_root_inode();
|
BAN::ErrorOr<void> initialize_root_inode();
|
||||||
|
|
||||||
BAN::ErrorOr<uint32_t> create_inode(const Ext2::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);
|
BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
|
||||||
|
|
||||||
void read_block(uint32_t, BAN::Span<uint8_t>);
|
void read_block(uint32_t, BlockBufferWrapper&);
|
||||||
void write_block(uint32_t, BAN::Span<const uint8_t>);
|
void write_block(uint32_t, const BlockBufferWrapper&);
|
||||||
void sync_superblock();
|
void sync_superblock();
|
||||||
|
|
||||||
|
BlockBufferWrapper get_block_buffer();
|
||||||
|
|
||||||
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
|
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; }
|
const Ext2::Superblock& superblock() const { return m_superblock; }
|
||||||
|
|
||||||
|
@ -39,11 +79,30 @@ namespace Kernel
|
||||||
uint32_t block;
|
uint32_t block;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
};
|
};
|
||||||
BAN::ErrorOr<BlockLocation> locate_inode(uint32_t);
|
BlockLocation locate_inode(uint32_t);
|
||||||
BlockLocation locate_block_group_descriptior(uint32_t);
|
BlockLocation locate_block_group_descriptior(uint32_t);
|
||||||
|
|
||||||
uint32_t block_size() const { return 1024 << superblock().log_block_size; }
|
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:
|
private:
|
||||||
RecursiveSpinLock m_lock;
|
RecursiveSpinLock m_lock;
|
||||||
|
|
||||||
|
@ -52,6 +111,10 @@ namespace Kernel
|
||||||
BAN::RefPtr<Inode> m_root_inode;
|
BAN::RefPtr<Inode> m_root_inode;
|
||||||
BAN::Vector<uint32_t> m_superblock_backups;
|
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;
|
Ext2::Superblock m_superblock;
|
||||||
|
|
||||||
friend class Ext2Inode;
|
friend class Ext2Inode;
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace Kernel
|
||||||
class Ext2Inode final : public Inode
|
class Ext2Inode final : public Inode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
~Ext2Inode();
|
||||||
|
|
||||||
virtual ino_t ino() const override { return m_ino; };
|
virtual ino_t ino() const override { return m_ino; };
|
||||||
virtual Mode mode() const override { return { m_inode.mode }; }
|
virtual Mode mode() const override { return { m_inode.mode }; }
|
||||||
virtual nlink_t nlink() const override { return m_inode.links_count; }
|
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<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> 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_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<BAN::String> link_target_impl() override;
|
||||||
|
|
||||||
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;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) 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> truncate_impl(size_t) override;
|
||||||
|
virtual BAN::ErrorOr<void> chmod_impl(mode_t) override;
|
||||||
|
|
||||||
private:
|
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::Optional<uint32_t> block_from_indirect_block(uint32_t block, uint32_t index, uint32_t depth);
|
||||||
BAN::ErrorOr<uint32_t> allocate_new_block();
|
BAN::Optional<uint32_t> fs_block_of_data_block_index(uint32_t data_block_index);
|
||||||
BAN::ErrorOr<void> sync();
|
|
||||||
|
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;
|
uint32_t block_group() const;
|
||||||
|
|
||||||
|
@ -53,7 +70,7 @@ namespace Kernel
|
||||||
, m_inode(inode)
|
, m_inode(inode)
|
||||||
, m_ino(ino)
|
, m_ino(ino)
|
||||||
{}
|
{}
|
||||||
static BAN::ErrorOr<BAN::RefPtr<Inode>> create(Ext2FS&, uint32_t);
|
static BAN::ErrorOr<BAN::RefPtr<Ext2Inode>> create(Ext2FS&, uint32_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ext2FS& m_fs;
|
Ext2FS& m_fs;
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/ByteSpan.h>
|
||||||
#include <BAN/RefPtr.h>
|
#include <BAN/RefPtr.h>
|
||||||
#include <BAN/String.h>
|
#include <BAN/String.h>
|
||||||
#include <BAN/StringView.h>
|
#include <BAN/StringView.h>
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
|
#include <BAN/WeakPtr.h>
|
||||||
|
|
||||||
#include <kernel/API/DirectoryEntry.h>
|
#include <kernel/API/DirectoryEntry.h>
|
||||||
#include <kernel/Credentials.h>
|
#include <kernel/Credentials.h>
|
||||||
|
@ -17,6 +19,9 @@ namespace Kernel
|
||||||
|
|
||||||
using namespace API;
|
using namespace API;
|
||||||
|
|
||||||
|
class FileBackedRegion;
|
||||||
|
class SharedFileData;
|
||||||
|
|
||||||
class Inode : public BAN::RefCounted<Inode>
|
class Inode : public BAN::RefCounted<Inode>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -85,14 +90,17 @@ namespace Kernel
|
||||||
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode(BAN::StringView);
|
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode(BAN::StringView);
|
||||||
BAN::ErrorOr<void> list_next_inodes(off_t, DirectoryEntryList*, size_t);
|
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_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
|
// Link API
|
||||||
BAN::ErrorOr<BAN::String> link_target();
|
BAN::ErrorOr<BAN::String> link_target();
|
||||||
|
|
||||||
// General API
|
// General API
|
||||||
BAN::ErrorOr<size_t> read(off_t, void*, size_t);
|
BAN::ErrorOr<size_t> read(off_t, BAN::ByteSpan buffer);
|
||||||
BAN::ErrorOr<size_t> write(off_t, const void*, size_t);
|
BAN::ErrorOr<size_t> write(off_t, BAN::ConstByteSpan buffer);
|
||||||
BAN::ErrorOr<void> truncate(size_t);
|
BAN::ErrorOr<void> truncate(size_t);
|
||||||
|
BAN::ErrorOr<void> chmod(mode_t);
|
||||||
bool has_data() const;
|
bool has_data() const;
|
||||||
|
|
||||||
protected:
|
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<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> 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_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
|
// Link API
|
||||||
virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); }
|
virtual BAN::ErrorOr<BAN::String> link_target_impl() { return BAN::Error::from_errno(ENOTSUP); }
|
||||||
|
|
||||||
// General API
|
// 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> read_impl(off_t, BAN::ByteSpan) { 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> 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> 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; }
|
virtual bool has_data_impl() const { dwarnln("nonblock not supported"); return true; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable RecursiveSpinLock m_lock;
|
mutable RecursiveSpinLock m_lock;
|
||||||
|
|
||||||
|
BAN::WeakPtr<SharedFileData> m_shared_region;
|
||||||
|
friend class FileBackedRegion;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -31,8 +31,8 @@ namespace Kernel
|
||||||
virtual dev_t rdev() const override { return 0; } // FIXME
|
virtual dev_t rdev() const override { return 0; } // FIXME
|
||||||
|
|
||||||
protected:
|
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;
|
||||||
virtual BAN::ErrorOr<size_t> write_impl(off_t, const void*, size_t) override;
|
virtual BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pipe(const Credentials&);
|
Pipe(const Credentials&);
|
||||||
|
|
|
@ -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();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -4,10 +4,10 @@
|
||||||
|
|
||||||
constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
|
constexpr uint8_t IRQ_VECTOR_BASE = 0x20;
|
||||||
|
|
||||||
namespace IDT
|
namespace Kernel::IDT
|
||||||
{
|
{
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void register_irq_handler(uint8_t irq, void(*f)());
|
[[noreturn]] void force_triple_fault();
|
||||||
|
|
||||||
}
|
}
|
|
@ -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),
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,20 +1,23 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <kernel/Device/Device.h>
|
#include <kernel/Device/Device.h>
|
||||||
|
#include <kernel/InterruptController.h>
|
||||||
|
|
||||||
namespace Kernel::Input
|
namespace Kernel::Input
|
||||||
{
|
{
|
||||||
|
|
||||||
class PS2Device : public CharacterDevice
|
class PS2Device : public CharacterDevice, public Interruptable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
PS2Device();
|
||||||
virtual ~PS2Device() {}
|
virtual ~PS2Device() {}
|
||||||
virtual void on_byte(uint8_t) = 0;
|
|
||||||
|
|
||||||
public:
|
virtual void send_initialize() = 0;
|
||||||
PS2Device()
|
|
||||||
: CharacterDevice(Mode::IRUSR | Mode::IRGRP, 0, 0)
|
virtual BAN::StringView name() const override { return m_name; }
|
||||||
{ }
|
|
||||||
|
private:
|
||||||
|
const BAN::String m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PS2Controller
|
class PS2Controller
|
||||||
|
@ -33,9 +36,6 @@ namespace Kernel::Input
|
||||||
BAN::ErrorOr<void> reset_device(uint8_t);
|
BAN::ErrorOr<void> reset_device(uint8_t);
|
||||||
BAN::ErrorOr<void> set_scanning(uint8_t, bool);
|
BAN::ErrorOr<void> set_scanning(uint8_t, bool);
|
||||||
|
|
||||||
static void device0_irq();
|
|
||||||
static void device1_irq();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PS2Device* m_devices[2] { nullptr, nullptr };
|
PS2Device* m_devices[2] { nullptr, nullptr };
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,13 +28,13 @@ namespace Kernel::Input
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static BAN::ErrorOr<PS2Keyboard*> create(PS2Controller&);
|
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;
|
virtual void update() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PS2Keyboard(PS2Controller& controller);
|
PS2Keyboard(PS2Controller& controller);
|
||||||
BAN::ErrorOr<void> initialize();
|
|
||||||
|
|
||||||
void append_command_queue(uint8_t);
|
void append_command_queue(uint8_t);
|
||||||
void append_command_queue(uint8_t, 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; }
|
virtual dev_t rdev() const override { return m_rdev; }
|
||||||
|
|
||||||
protected:
|
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:
|
private:
|
||||||
const dev_t m_rdev;
|
const dev_t m_rdev;
|
||||||
|
|
|
@ -5,9 +5,29 @@
|
||||||
#define DISABLE_INTERRUPTS() asm volatile("cli")
|
#define DISABLE_INTERRUPTS() asm volatile("cli")
|
||||||
#define ENABLE_INTERRUPTS() asm volatile("sti")
|
#define ENABLE_INTERRUPTS() asm volatile("sti")
|
||||||
|
|
||||||
class InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
class Interruptable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void set_irq(int irq);
|
||||||
|
void enable_interrupt();
|
||||||
|
void disable_interrupt();
|
||||||
|
|
||||||
|
virtual void handle_irq() = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Interruptable() = default;
|
||||||
|
~Interruptable() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_irq { -1 };
|
||||||
|
};
|
||||||
|
|
||||||
|
class InterruptController
|
||||||
|
{
|
||||||
|
public:
|
||||||
virtual ~InterruptController() {}
|
virtual ~InterruptController() {}
|
||||||
|
|
||||||
virtual void eoi(uint8_t) = 0;
|
virtual void eoi(uint8_t) = 0;
|
||||||
|
@ -16,6 +36,15 @@ public:
|
||||||
|
|
||||||
static void initialize(bool force_pic);
|
static void initialize(bool force_pic);
|
||||||
static InterruptController& get();
|
static InterruptController& get();
|
||||||
};
|
|
||||||
|
|
||||||
bool interrupts_enabled();
|
bool is_using_apic() const { return m_using_apic; }
|
||||||
|
|
||||||
|
void enter_acpi_mode();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_using_apic { false };
|
||||||
|
};
|
||||||
|
|
||||||
|
bool interrupts_enabled();
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -21,6 +21,9 @@ namespace Kernel
|
||||||
paddr_t take_free_page();
|
paddr_t take_free_page();
|
||||||
void release_page(paddr_t);
|
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 used_pages() const;
|
||||||
size_t free_pages() const;
|
size_t free_pages() const;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1,12 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <BAN/Errors.h>
|
#include <BAN/Errors.h>
|
||||||
|
#include <BAN/Traits.h>
|
||||||
|
#include <kernel/CriticalScope.h>
|
||||||
#include <kernel/Memory/Types.h>
|
#include <kernel/Memory/Types.h>
|
||||||
#include <kernel/SpinLock.h>
|
#include <kernel/SpinLock.h>
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
concept with_fast_page_callback = requires(F func)
|
||||||
|
{
|
||||||
|
requires BAN::is_same_v<decltype(func()), void>;
|
||||||
|
};
|
||||||
|
|
||||||
class PageTable
|
class PageTable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -29,13 +37,50 @@ namespace Kernel
|
||||||
static PageTable& kernel();
|
static PageTable& kernel();
|
||||||
static PageTable& current();
|
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();
|
static BAN::ErrorOr<PageTable*> create_userspace();
|
||||||
~PageTable();
|
~PageTable();
|
||||||
|
|
||||||
void unmap_page(vaddr_t);
|
void unmap_page(vaddr_t);
|
||||||
void unmap_range(vaddr_t, size_t bytes);
|
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);
|
void map_page_at(paddr_t, vaddr_t, flags_t);
|
||||||
|
|
||||||
paddr_t physical_address_of(vaddr_t) const;
|
paddr_t physical_address_of(vaddr_t) const;
|
||||||
|
@ -62,7 +107,8 @@ namespace Kernel
|
||||||
uint64_t get_page_data(vaddr_t) const;
|
uint64_t get_page_data(vaddr_t) const;
|
||||||
void initialize_kernel();
|
void initialize_kernel();
|
||||||
void map_kernel_memory();
|
void map_kernel_memory();
|
||||||
void invalidate(vaddr_t);
|
void prepare_fast_page();
|
||||||
|
static void invalidate(vaddr_t);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
paddr_t m_highest_paging_struct { 0 };
|
paddr_t m_highest_paging_struct { 0 };
|
||||||
|
@ -73,7 +119,7 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
size_t first_page = start / PAGE_SIZE;
|
size_t first_page = start / PAGE_SIZE;
|
||||||
size_t last_page = BAN::Math::div_round_up<size_t>(start + bytes, 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
#include <kernel/Memory/Types.h>
|
#include <kernel/Memory/Types.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
@ -12,42 +11,40 @@ namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PhysicalRange(paddr_t, size_t);
|
PhysicalRange(paddr_t, size_t);
|
||||||
|
|
||||||
paddr_t reserve_page();
|
paddr_t reserve_page();
|
||||||
void release_page(paddr_t);
|
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 start() const { return m_paddr; }
|
||||||
paddr_t end() const { return m_paddr + m_size; }
|
paddr_t end() const { return m_paddr + m_size; }
|
||||||
bool contains(paddr_t addr) const { return m_paddr <= addr && addr < 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; }
|
size_t free_pages() const { return m_free_pages; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct node
|
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; }
|
||||||
node* next;
|
|
||||||
node* prev;
|
|
||||||
};
|
|
||||||
|
|
||||||
paddr_t page_address(const node*) const;
|
paddr_t paddr_for_bit(unsigned long long) const;
|
||||||
node* node_address(paddr_t) 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:
|
private:
|
||||||
paddr_t m_paddr { 0 };
|
const paddr_t m_paddr { 0 };
|
||||||
|
const size_t m_size { 0 };
|
||||||
|
|
||||||
vaddr_t m_vaddr { 0 };
|
vaddr_t m_vaddr { 0 };
|
||||||
size_t m_size { 0 };
|
|
||||||
|
|
||||||
uint64_t m_total_pages { 0 };
|
const size_t m_bitmap_pages { 0 };
|
||||||
uint64_t m_reservable_pages { 0 };
|
const size_t m_data_pages { 0 };
|
||||||
uint64_t m_list_pages { 0 };
|
|
||||||
|
|
||||||
size_t m_used_pages { 0 };
|
|
||||||
size_t m_free_pages { 0 };
|
size_t m_free_pages { 0 };
|
||||||
|
|
||||||
node* m_free_list { nullptr };
|
|
||||||
node* m_used_list { nullptr };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,9 +15,9 @@ namespace Kernel
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Create virtual range to fixed virtual address
|
// 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
|
// 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
|
// Create virtual range in kernel memory with kmalloc
|
||||||
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_kmalloc(size_t);
|
static BAN::ErrorOr<BAN::UniqPtr<VirtualRange>> create_kmalloc(size_t);
|
||||||
~VirtualRange();
|
~VirtualRange();
|
||||||
|
@ -28,19 +28,24 @@ namespace Kernel
|
||||||
size_t size() const { return m_size; }
|
size_t size() const { return m_size; }
|
||||||
PageTable::flags_t flags() const { return m_flags; }
|
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);
|
void copy_from(size_t offset, const uint8_t* buffer, size_t bytes);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VirtualRange(PageTable&);
|
VirtualRange(PageTable&, bool preallocated, bool kmalloc);
|
||||||
|
|
||||||
|
void set_zero();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PageTable& m_page_table;
|
PageTable& m_page_table;
|
||||||
bool m_kmalloc { false };
|
const bool m_preallocated;
|
||||||
|
const bool m_kmalloc;
|
||||||
vaddr_t m_vaddr { 0 };
|
vaddr_t m_vaddr { 0 };
|
||||||
size_t m_size { 0 };
|
size_t m_size { 0 };
|
||||||
PageTable::flags_t m_flags { 0 };
|
PageTable::flags_t m_flags { 0 };
|
||||||
BAN::Vector<paddr_t> m_physical_pages;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -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 };
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -41,13 +41,14 @@ namespace Kernel
|
||||||
void close_all();
|
void close_all();
|
||||||
void close_cloexec();
|
void close_cloexec();
|
||||||
|
|
||||||
BAN::ErrorOr<size_t> read(int fd, void* buffer, size_t count);
|
BAN::ErrorOr<size_t> read(int fd, BAN::ByteSpan);
|
||||||
BAN::ErrorOr<size_t> write(int fd, const void* buffer, size_t count);
|
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<void> read_dir_entries(int fd, DirectoryEntryList* list, size_t list_size);
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::StringView> path_of(int) const;
|
BAN::ErrorOr<BAN::StringView> path_of(int) const;
|
||||||
BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int);
|
BAN::ErrorOr<BAN::RefPtr<Inode>> inode_of(int);
|
||||||
|
BAN::ErrorOr<int> flags_of(int) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct OpenFileDescription : public BAN::RefCounted<OpenFileDescription>
|
struct OpenFileDescription : public BAN::RefCounted<OpenFileDescription>
|
||||||
|
|
|
@ -1,20 +1,70 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <BAN/UniqPtr.h>
|
||||||
#include <BAN/Vector.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:
|
public:
|
||||||
PCIDevice(uint8_t, uint8_t, uint8_t);
|
Device(uint8_t, uint8_t, uint8_t);
|
||||||
|
|
||||||
uint32_t read_dword(uint8_t) const;
|
uint32_t read_dword(uint8_t) const;
|
||||||
uint16_t read_word(uint8_t) const;
|
uint16_t read_word(uint8_t) const;
|
||||||
uint8_t read_byte(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 bus() const { return m_bus; }
|
||||||
uint8_t dev() const { return m_dev; }
|
uint8_t dev() const { return m_dev; }
|
||||||
|
@ -24,38 +74,71 @@ namespace Kernel
|
||||||
uint8_t subclass() const { return m_subclass; }
|
uint8_t subclass() const { return m_subclass; }
|
||||||
uint8_t prog_if() const { return m_prog_if; }
|
uint8_t prog_if() const { return m_prog_if; }
|
||||||
|
|
||||||
void enable_bus_mastering() const;
|
uint8_t header_type() const { return m_header_type; }
|
||||||
void disable_bus_mastering() const;
|
|
||||||
|
|
||||||
void enable_memory_space() const;
|
uint16_t vendor_id() const { return m_vendor_id; }
|
||||||
void disable_memory_space() const;
|
uint16_t device_id() const { return m_device_id; }
|
||||||
|
|
||||||
void enable_pin_interrupts() const;
|
BAN::ErrorOr<uint8_t> get_irq();
|
||||||
void disable_pin_interrupts() const;
|
|
||||||
|
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:
|
private:
|
||||||
uint8_t m_bus;
|
void enumerate_capabilites();
|
||||||
uint8_t m_dev;
|
|
||||||
uint8_t m_func;
|
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_class_code;
|
||||||
uint8_t m_subclass;
|
uint8_t m_subclass;
|
||||||
uint8_t m_prog_if;
|
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_COPYABLE(PCIManager);
|
||||||
BAN_NON_MOVABLE(PCI);
|
BAN_NON_MOVABLE(PCIManager);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void initialize();
|
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:
|
private:
|
||||||
PCI() = default;
|
PCIManager() = default;
|
||||||
void check_function(uint8_t bus, uint8_t dev, uint8_t func);
|
void check_function(uint8_t bus, uint8_t dev, uint8_t func);
|
||||||
void check_device(uint8_t bus, uint8_t dev);
|
void check_device(uint8_t bus, uint8_t dev);
|
||||||
void check_bus(uint8_t bus);
|
void check_bus(uint8_t bus);
|
||||||
|
@ -63,7 +146,7 @@ namespace Kernel
|
||||||
void initialize_devices();
|
void initialize_devices();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BAN::Vector<PCIDevice> m_devices;
|
BAN::Vector<PCI::Device> m_devices;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
#include <kernel/InterruptController.h>
|
#include <kernel/InterruptController.h>
|
||||||
|
|
||||||
class PIC final : public InterruptController
|
namespace Kernel
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
|
class PIC final : public InterruptController
|
||||||
|
{
|
||||||
|
public:
|
||||||
virtual void eoi(uint8_t) override;
|
virtual void eoi(uint8_t) override;
|
||||||
virtual void enable_irq(uint8_t) override;
|
virtual void enable_irq(uint8_t) override;
|
||||||
virtual bool is_in_service(uint8_t) override;
|
virtual bool is_in_service(uint8_t) override;
|
||||||
|
@ -12,7 +15,9 @@ public:
|
||||||
static void remap();
|
static void remap();
|
||||||
static void mask_all();
|
static void mask_all();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PIC* create();
|
static PIC* create();
|
||||||
friend class InterruptController;
|
friend class InterruptController;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -11,11 +11,11 @@ namespace Kernel::detail
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
__attribute__((__noreturn__))
|
__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");
|
asm volatile("cli");
|
||||||
derrorln("Kernel panic at {}:{}", file, line);
|
derrorln("Kernel panic at {}:{}", file, line);
|
||||||
derrorln(message, args...);
|
derrorln(message, BAN::forward<Args>(args)...);
|
||||||
if (!g_paniced)
|
if (!g_paniced)
|
||||||
{
|
{
|
||||||
g_paniced = true;
|
g_paniced = true;
|
||||||
|
|
|
@ -6,18 +6,18 @@
|
||||||
#include <BAN/Vector.h>
|
#include <BAN/Vector.h>
|
||||||
#include <kernel/Credentials.h>
|
#include <kernel/Credentials.h>
|
||||||
#include <kernel/FS/Inode.h>
|
#include <kernel/FS/Inode.h>
|
||||||
#include <kernel/Memory/FixedWidthAllocator.h>
|
|
||||||
#include <kernel/Memory/GeneralAllocator.h>
|
|
||||||
#include <kernel/Memory/Heap.h>
|
#include <kernel/Memory/Heap.h>
|
||||||
#include <kernel/Memory/VirtualRange.h>
|
#include <kernel/Memory/MemoryRegion.h>
|
||||||
#include <kernel/OpenFileDescriptorSet.h>
|
#include <kernel/OpenFileDescriptorSet.h>
|
||||||
#include <kernel/SpinLock.h>
|
#include <kernel/SpinLock.h>
|
||||||
#include <kernel/Terminal/TTY.h>
|
#include <kernel/Terminal/TTY.h>
|
||||||
#include <kernel/Thread.h>
|
#include <kernel/Thread.h>
|
||||||
|
|
||||||
|
#include <sys/banan-os.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
namespace LibELF { class ELF; }
|
namespace LibELF { class LoadableELF; }
|
||||||
|
|
||||||
namespace Kernel
|
namespace Kernel
|
||||||
{
|
{
|
||||||
|
@ -39,11 +39,13 @@ namespace Kernel
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static Process* create_kernel();
|
||||||
static Process* create_kernel(entry_t, void*);
|
static Process* create_kernel(entry_t, void*);
|
||||||
static BAN::ErrorOr<Process*> create_userspace(const Credentials&, BAN::StringView);
|
static BAN::ErrorOr<Process*> create_userspace(const Credentials&, BAN::StringView);
|
||||||
~Process();
|
~Process();
|
||||||
void cleanup_function();
|
void cleanup_function();
|
||||||
|
|
||||||
|
void register_to_scheduler();
|
||||||
void exit(int status, int signal);
|
void exit(int status, int signal);
|
||||||
|
|
||||||
static void for_each_process(const BAN::Function<BAN::Iteration(Process&)>& callback);
|
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(); }
|
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_exit(int status);
|
||||||
|
|
||||||
BAN::ErrorOr<long> sys_gettermios(::termios*);
|
BAN::ErrorOr<long> sys_gettermios(::termios*);
|
||||||
|
@ -70,8 +74,6 @@ namespace Kernel
|
||||||
BAN::ErrorOr<long> sys_sleep(int seconds);
|
BAN::ErrorOr<long> sys_sleep(int seconds);
|
||||||
BAN::ErrorOr<long> sys_nanosleep(const timespec* rqtp, timespec* rmtp);
|
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_setpwd(const char* path);
|
||||||
BAN::ErrorOr<long> sys_getpwd(char* buffer, size_t size);
|
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_getegid() const { return m_credentials.egid(); }
|
||||||
BAN::ErrorOr<long> sys_getpgid(pid_t);
|
BAN::ErrorOr<long> sys_getpgid(pid_t);
|
||||||
|
|
||||||
BAN::ErrorOr<void> create_file(BAN::StringView name, mode_t mode);
|
BAN::ErrorOr<void> create_file_or_dir(BAN::StringView name, mode_t mode);
|
||||||
BAN::ErrorOr<long> sys_open(BAN::StringView, int, mode_t = 0);
|
BAN::ErrorOr<long> open_file(BAN::StringView path, int, mode_t = 0);
|
||||||
BAN::ErrorOr<long> sys_openat(int, BAN::StringView, 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_close(int fd);
|
||||||
BAN::ErrorOr<long> sys_read(int fd, void* buffer, size_t count);
|
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_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_pipe(int fildes[2]);
|
||||||
BAN::ErrorOr<long> sys_dup(int fildes);
|
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_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_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<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_read_dir_entries(int fd, DirectoryEntryList* buffer, size_t buffer_size);
|
||||||
|
|
||||||
BAN::ErrorOr<long> sys_alloc(size_t);
|
BAN::ErrorOr<long> sys_mmap(const sys_mmap_t*);
|
||||||
BAN::ErrorOr<long> sys_free(void*);
|
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_signal(int, void (*)(int));
|
||||||
BAN::ErrorOr<long> sys_raise(int signal);
|
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_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; }
|
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(); }
|
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; }
|
bool is_userspace() const { return m_is_userspace; }
|
||||||
const userspace_info_t& userspace_info() const { return m_userspace_info; }
|
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:
|
private:
|
||||||
Process(const Credentials&, pid_t pid, pid_t parent, pid_t sid, pid_t pgrp);
|
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 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
|
// Load elf from a file
|
||||||
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);
|
static BAN::ErrorOr<BAN::UniqPtr<LibELF::LoadableELF>> load_elf_for_exec(const Credentials&, BAN::StringView file_path, const BAN::String& cwd, Kernel::PageTable&);
|
||||||
|
|
||||||
// Copy an elf file from the current page table to the processes own
|
|
||||||
void load_elf_to_memory(LibELF::ELF&);
|
|
||||||
|
|
||||||
int block_until_exit();
|
int block_until_exit();
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::String> absolute_path_of(BAN::StringView) const;
|
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:
|
private:
|
||||||
struct ExitStatus
|
struct ExitStatus
|
||||||
{
|
{
|
||||||
|
@ -163,7 +188,8 @@ namespace Kernel
|
||||||
|
|
||||||
OpenFileDescriptorSet m_open_file_descriptors;
|
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_sid;
|
||||||
pid_t m_pgrp;
|
pid_t m_pgrp;
|
||||||
|
@ -175,12 +201,12 @@ namespace Kernel
|
||||||
BAN::String m_working_directory;
|
BAN::String m_working_directory;
|
||||||
BAN::Vector<Thread*> m_threads;
|
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] { };
|
vaddr_t m_signal_handlers[_SIGMAX + 1] { };
|
||||||
uint64_t m_signal_pending_mask { 0 };
|
uint64_t m_signal_pending_mask { 0 };
|
||||||
|
|
||||||
|
BAN::Vector<BAN::String> m_cmdline;
|
||||||
|
BAN::Vector<BAN::String> m_environ;
|
||||||
|
|
||||||
bool m_is_userspace { false };
|
bool m_is_userspace { false };
|
||||||
userspace_info_t m_userspace_info;
|
userspace_info_t m_userspace_info;
|
||||||
ExitStatus m_exit_status;
|
ExitStatus m_exit_status;
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace Kernel
|
||||||
static bool is_valid_tid(pid_t tid);
|
static bool is_valid_tid(pid_t tid);
|
||||||
|
|
||||||
[[noreturn]] void execute_current_thread();
|
[[noreturn]] void execute_current_thread();
|
||||||
|
[[noreturn]] void _execute_current_thread();
|
||||||
[[noreturn]] void delete_current_process_and_thread();
|
[[noreturn]] void delete_current_process_and_thread();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue