WIP: paging and better malloc.
This commit is contained in:
parent
75ec3751fe
commit
836d589c9b
@ -15,6 +15,7 @@ target = GetOption('target')
|
||||
|
||||
env = Environment(tools = ['default', 'compilation_db'])
|
||||
env.Append(CCFLAGS = ['-g', '-O0'])
|
||||
env.Append(CPPDEFINES = ['BASTL_EXTENSIONS=1'])
|
||||
# env.Append(CCFLAGS = ['-O2'])
|
||||
|
||||
env['ISO_FILES'] = []
|
||||
|
@ -4,6 +4,10 @@
|
||||
#if !defined(BAD_APPLE_OS_ALGORITHM_INCLUDED)
|
||||
#define BAD_APPLE_OS_ALGORITHM_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename T>
|
||||
@ -17,6 +21,76 @@ constexpr const T& max(const T& left, const T& right)
|
||||
{
|
||||
return right > left ? right : left;
|
||||
}
|
||||
|
||||
template<typename RandomIt, class Compare>
|
||||
constexpr void sort(RandomIt first, RandomIt last, Compare comp)
|
||||
{
|
||||
// TODO: be smarter
|
||||
const size_t size = last - first;
|
||||
size_t sorted = 1;
|
||||
while (sorted < size)
|
||||
{
|
||||
// find insertion pos
|
||||
size_t pos = 0;
|
||||
while (pos < sorted)
|
||||
{
|
||||
if (comp(*(first + sorted), *(first + pos)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
++pos;
|
||||
}
|
||||
// bail if already sorted
|
||||
if (pos == sorted)
|
||||
{
|
||||
++sorted;
|
||||
continue;
|
||||
}
|
||||
// insert at pos
|
||||
for (size_t idx = pos; idx < sorted; ++idx)
|
||||
{
|
||||
swap(*(first + idx), *(first + sorted));
|
||||
}
|
||||
++sorted;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename RandomIt>
|
||||
constexpr void sort(RandomIt first, RandomIt last)
|
||||
{
|
||||
sort(first, last, [](auto left, auto right) { return left < right; });
|
||||
}
|
||||
|
||||
template<typename ForwardIt, typename UnaryPredicate>
|
||||
constexpr ForwardIt find_if(ForwardIt first, ForwardIt last, UnaryPredicate pred)
|
||||
{
|
||||
for (auto it = first; it != last; ++it)
|
||||
{
|
||||
if (pred(*it))
|
||||
{
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
template<typename ForwardIt, typename UnaryPredicate>
|
||||
constexpr ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate pred)
|
||||
{
|
||||
auto found = find_if(first, last, pred);
|
||||
if (found != last)
|
||||
{
|
||||
for (auto tomove = next(found); tomove != last; ++tomove)
|
||||
{
|
||||
if (!pred(*tomove))
|
||||
{
|
||||
*found = move(*tomove);
|
||||
++found;
|
||||
}
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_ALGORITHM_INCLUDED)
|
||||
|
18
bastl/include/iterator
Normal file
18
bastl/include/iterator
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_ITERATOR_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_ITERATOR_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename InputIt>
|
||||
InputIt next(InputIt it, std::ptrdiff_t n = 1) // TODO: this is not really correct...
|
||||
{
|
||||
return it + n;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_ITERATOR_HPP_INCLUDED)
|
44
bastl/include/memory
Normal file
44
bastl/include/memory
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_MEMORY_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_MEMORY_HPP_INCLUDED
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace std
|
||||
{
|
||||
constexpr void* align(std::size_t alignment, std::size_t size, void*& ptr, std::size_t& space) noexcept
|
||||
{
|
||||
if (space < size) {
|
||||
return nullptr;
|
||||
}
|
||||
std::size_t addr = __builtin_bit_cast(std::size_t, ptr);
|
||||
if (addr % alignment != 0)
|
||||
{
|
||||
std::size_t offset = alignment + (addr % alignment);
|
||||
if (space < size + offset)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
addr += offset;
|
||||
space -= offset;
|
||||
ptr = __builtin_bit_cast(void*, addr);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if defined(BASTL_EXTENSIONS)
|
||||
template<typename T>
|
||||
constexpr T alignUp(T value, T alignTo) noexcept
|
||||
{
|
||||
if (value % alignTo != 0)
|
||||
{
|
||||
return value + alignTo - (value % alignTo);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_MEMORY_HPP_INCLUDED)
|
93
bastl/include/span
Normal file
93
bastl/include/span
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_SPAN_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_SPAN_HPP_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
namespace std
|
||||
{
|
||||
inline constexpr size_t dynamic_extent = size_t(-1);
|
||||
|
||||
template<typename T, size_t Extent = dynamic_extent> // TODO: use the extent?
|
||||
class span
|
||||
{
|
||||
public:
|
||||
using element_type = T;
|
||||
using value_type = remove_cv_t<T>;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using reference = element_type&;
|
||||
using const_reference = const element_type&;
|
||||
using pointer = element_type*;
|
||||
using const_pointer = const element_type*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
static constexpr size_t extent = Extent;
|
||||
private:
|
||||
pointer _first = nullptr;
|
||||
pointer _last = nullptr;
|
||||
public:
|
||||
constexpr span() noexcept = default;
|
||||
constexpr span(const span&) noexcept = default;
|
||||
|
||||
template<typename It>
|
||||
explicit(extent != dynamic_extent)
|
||||
constexpr span(It first, size_type count) : _first(&*first), _last(&*(_first + count)) {}
|
||||
|
||||
template<typename It>
|
||||
explicit(extent != dynamic_extent)
|
||||
constexpr span(It first, It last) : _first(&*first), _last(&*last) {}
|
||||
|
||||
template<size_t N>
|
||||
constexpr span(type_identity_t<element_type> (&arr)[N]) noexcept : span(&arr[0], N) {}
|
||||
|
||||
template<typename U, size_t N>
|
||||
constexpr span(array<U, N>& arr) noexcept : span(&arr[0], N) {}
|
||||
|
||||
template<typename U, size_t N>
|
||||
constexpr span(const array<U, N>& arr) noexcept : span(&arr[0], N) {}
|
||||
|
||||
explicit(Extent != dynamic_extent)
|
||||
constexpr span(initializer_list<value_type> il) noexcept : span(il.begin(), il.size()) {}
|
||||
|
||||
template<typename U, size_t N>
|
||||
explicit(extent != dynamic_extent && N != dynamic_extent)
|
||||
constexpr span(const span<U, N>& source) noexcept : span(source.begin(), source.end()) {}
|
||||
|
||||
constexpr span& operator=(const span&) noexcept = default;
|
||||
|
||||
constexpr reference operator[](size_type idx) const { return _first[idx]; }
|
||||
|
||||
constexpr iterator begin() const noexcept { return _first; }
|
||||
constexpr const_iterator cbegin() const noexcept { return _first; }
|
||||
constexpr iterator end() const noexcept { return _last; }
|
||||
constexpr const_iterator cend() const noexcept { return _last; }
|
||||
|
||||
constexpr reference front() const { return *_first; }
|
||||
constexpr reference back() const { return *(_last - 1); }
|
||||
|
||||
constexpr reference at(size_type pos) const
|
||||
{
|
||||
if (pos >= size()) {
|
||||
__ba_throw std::out_of_range();
|
||||
}
|
||||
return (*this)[pos];
|
||||
}
|
||||
constexpr pointer data() const noexcept { return _first; }
|
||||
|
||||
constexpr size_type size() const noexcept { return _last - _first; }
|
||||
constexpr size_type size_bytes() const noexcept { return size() * sizeof(element_type); }
|
||||
constexpr bool empty() const noexcept { return size() == 0; }
|
||||
|
||||
// TODO: subspans
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_SPAN_HPP_INCLUDED)
|
@ -26,6 +26,66 @@ struct remove_reference<T&&>
|
||||
|
||||
template<typename T>
|
||||
using remove_reference_t = typename remove_reference<T>::type;
|
||||
|
||||
template<typename T>
|
||||
struct remove_const
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_const<const T>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_const_t = typename remove_const<T>::type;
|
||||
|
||||
template<typename T>
|
||||
struct remove_volatile
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_volatile<volatile T>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_volatile_t = typename remove_volatile<T>::type;
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv<const T>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct remove_cv<const volatile T>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using remove_cv_t = typename remove_cv<T>::type;
|
||||
|
||||
template<typename T>
|
||||
struct type_identity
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using type_identity_t = type_identity<T>::type;
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_TYPE_TRAITS_INCLUDED)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#if !defined(BAD_APPLE_OS_VECTOR_INCLUDED)
|
||||
#define BAD_APPLE_OS_VECTOR_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
@ -11,13 +12,13 @@
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<typename T> // TODO: allocator
|
||||
template<typename T, typename TAlloc = void> // TODO: allocator
|
||||
class vector
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using size_type = size_t;
|
||||
using difference_type = ptrdiff_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using pointer = value_type*;
|
||||
@ -210,7 +211,7 @@ public:
|
||||
constexpr void push_back(value_type&& value)
|
||||
{
|
||||
reserve(size() + 1);
|
||||
::new (&_elements[size()]) T(std::move(value));
|
||||
::new (&_elements[size()]) T(move(value));
|
||||
++_size;
|
||||
}
|
||||
|
||||
@ -223,7 +224,7 @@ public:
|
||||
constexpr reference emplace_back(TArgs&&... args)
|
||||
{
|
||||
reserve(size() + 1);
|
||||
::new (&_elements[size()]) T(std::forward<TArgs>(args)...);
|
||||
::new (&_elements[size()]) T(forward<TArgs>(args)...);
|
||||
++_size;
|
||||
}
|
||||
|
||||
@ -274,6 +275,27 @@ public:
|
||||
}
|
||||
_size = newSize;
|
||||
}
|
||||
|
||||
constexpr iterator erase(const_iterator first, const_iterator last)
|
||||
{
|
||||
// the spec wants the parameters to be const iterators...
|
||||
iterator realFirst = begin() + (first - begin());
|
||||
iterator realLast = begin() + (last - begin());
|
||||
|
||||
const size_t newSize = size() - (last - first);
|
||||
for (auto it = realLast; it != end(); ++it)
|
||||
{
|
||||
*realFirst = move(*it);
|
||||
++realFirst;
|
||||
}
|
||||
resize(newSize);
|
||||
return realFirst;
|
||||
}
|
||||
|
||||
constexpr iterator erase(const_iterator pos)
|
||||
{
|
||||
return erase(pos, pos + 1);
|
||||
}
|
||||
private:
|
||||
void updateCapacity(size_type newCapacity)
|
||||
{
|
||||
@ -281,6 +303,10 @@ private:
|
||||
return;
|
||||
}
|
||||
T* newElements = static_cast<T*>(malloc(newCapacity * sizeof(T)));
|
||||
if (newElements == nullptr)
|
||||
{
|
||||
__ba_throw bad_alloc();
|
||||
}
|
||||
for (size_type idx = 0; idx < size(); ++idx)
|
||||
{
|
||||
::new (&newElements[idx]) T(move(_elements[idx]));
|
||||
@ -291,6 +317,16 @@ private:
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename TAlloc, typename Pred>
|
||||
constexpr vector<T, TAlloc>::size_type erase_if(vector<T, TAlloc>& vec, Pred pred)
|
||||
{
|
||||
|
||||
auto it = remove_if(vec.begin(), vec.end(), pred);
|
||||
auto removed = vec.end() - it;
|
||||
vec.erase(it, vec.end());
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_VECTOR_INCLUDED)
|
||||
|
18
start.sh
18
start.sh
@ -1,8 +1,24 @@
|
||||
#!/bin/bash
|
||||
QEMU_ARGS=()
|
||||
WAIT=true
|
||||
while [ ! -z "$1" ] ; do
|
||||
case "$1" in
|
||||
"--detach")
|
||||
WAIT=false
|
||||
;;
|
||||
*)
|
||||
QEMU_ARGS+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
qemu-system-x86_64 \
|
||||
--bios /usr/share/edk2/x64/OVMF.fd \
|
||||
-vga virtio \
|
||||
-serial stdio \
|
||||
-drive if=none,id=stick,format=raw,file=staging/x86_64_iso/fat.img \
|
||||
-device nec-usb-xhci,id=xhci \
|
||||
-device usb-storage,bus=xhci.0,drive=stick "$@"
|
||||
-device usb-storage,bus=xhci.0,drive=stick "${QEMU_ARGS[@]}" &
|
||||
if ${WAIT} ; then
|
||||
wait
|
||||
fi
|
||||
|
@ -42,10 +42,12 @@ any_target_sources = Split('''
|
||||
src/app/main.cpp
|
||||
|
||||
src/drivers/pci.cpp
|
||||
src/drivers/usb.cpp
|
||||
|
||||
src/libs/psf.cpp
|
||||
|
||||
src/os/draw.cpp
|
||||
src/os/memory.cpp
|
||||
src/os/panic.cpp
|
||||
src/os/tty.cpp
|
||||
src/os/resources/lat9-08.psf.s
|
||||
|
17
targets/_any/include/drivers/usb.hpp
Normal file
17
targets/_any/include/drivers/usb.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED
|
||||
|
||||
namespace baos::pci
|
||||
{
|
||||
struct Header;
|
||||
}
|
||||
|
||||
namespace baos::usb
|
||||
{
|
||||
[[nodiscard]] bool initController(const pci::Header& pciHeader) noexcept;
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED)
|
72
targets/_any/include/os/memory.hpp
Normal file
72
targets/_any/include/os/memory.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
extern "C" int gKernelStart;
|
||||
extern "C" int gKernelEnd;
|
||||
|
||||
namespace baos
|
||||
{
|
||||
struct PageTableEntry
|
||||
{
|
||||
bool present : 1;
|
||||
bool writable : 1;
|
||||
bool user : 1;
|
||||
bool writeThrough : 1;
|
||||
bool cacheDisabled : 1;
|
||||
bool accessed : 1;
|
||||
bool dirty : 1;
|
||||
bool pageSize : 1;
|
||||
bool global : 1;
|
||||
std::uint8_t avl : 3;
|
||||
std::uint64_t address : 40;
|
||||
std::uint16_t avl2 : 11;
|
||||
bool executeDisable : 1;
|
||||
};
|
||||
|
||||
enum class MemoryType
|
||||
{
|
||||
USABLE,
|
||||
UNUSABLE,
|
||||
MMIO,
|
||||
RESERVED
|
||||
};
|
||||
|
||||
struct MemoryRange
|
||||
{
|
||||
std::uint64_t pageBase;
|
||||
std::uint64_t numPages;
|
||||
MemoryType type = MemoryType::USABLE;
|
||||
|
||||
MemoryRange& operator=(const MemoryRange&) noexcept = default;
|
||||
bool operator<(const MemoryRange& other) const noexcept
|
||||
{
|
||||
return pageBase < other.pageBase;
|
||||
}
|
||||
};
|
||||
|
||||
void setupPaging(std::span<const MemoryRange> memoryRanges) noexcept;
|
||||
[[nodiscard]] const char* memoryTypeName(MemoryType type) noexcept;
|
||||
|
||||
[[nodiscard]] void* allocatePages(unsigned numConsecutive) noexcept;
|
||||
[[nodiscard]] inline void* allocatePage() noexcept { return allocatePages(1); }
|
||||
|
||||
void setupIdentityPaging(std::uint64_t page) noexcept;
|
||||
void setupIdentityPaging2M(std::uint64_t page) noexcept;
|
||||
void setupIdentityPaging(std::uint64_t firstPage, std::uint64_t lastPage) noexcept;
|
||||
|
||||
inline void identityMapRegion(void* start, std::size_t bytes) noexcept
|
||||
{
|
||||
std::uint64_t firstPage = std::bit_cast<std::uint64_t>(start) / 4096;
|
||||
std::uint64_t lastPage = std::bit_cast<std::uint64_t>(static_cast<std::uint8_t*>(start) + bytes) / 4096 + 1;
|
||||
setupIdentityPaging(firstPage, lastPage);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED)
|
@ -1,8 +1,10 @@
|
||||
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
|
||||
#include "drivers/pci.hpp"
|
||||
#include "drivers/ps2.hpp"
|
||||
#include "os/memory.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -49,6 +51,61 @@ void cmdShowKeys() noexcept
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmdDumpPageTable() noexcept
|
||||
{
|
||||
std::uint64_t cr3 = 0;
|
||||
__asm__ __volatile__(
|
||||
"mov %%cr3, %0"
|
||||
: "=a"(cr3)
|
||||
:
|
||||
);
|
||||
std::uint64_t pml4Addr = cr3 & ~0xFFF;
|
||||
std::printf("pml4 address: 0x%lX\n", pml4Addr);
|
||||
baos::PageTableEntry* pml4 = std::bit_cast<baos::PageTableEntry*>(pml4Addr);
|
||||
auto printPDE = [](const baos::PageTableEntry& pde)
|
||||
{
|
||||
std::printf("%s%s%s%s%s%s%s%s%s, address=%lX\n",
|
||||
pde.present ? " P" : "",
|
||||
pde.writable ? " W" : "",
|
||||
pde.user ? " U" : "",
|
||||
pde.writeThrough ? " WT" : "",
|
||||
pde.cacheDisabled ? " CD" : "",
|
||||
pde.accessed ? " A" : "",
|
||||
pde.dirty ? " D" : "",
|
||||
pde.pageSize ? " PS" : "",
|
||||
pde.executeDisable ? " XD" : "",
|
||||
pde.address * 4096);
|
||||
};
|
||||
for (unsigned pml4e = 0; pml4e < 512; ++pml4e)
|
||||
{
|
||||
if (!pml4[pml4e].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d]", pml4e);
|
||||
printPDE(pml4[pml4e]);
|
||||
|
||||
baos::PageTableEntry* pdp = std::bit_cast<baos::PageTableEntry*>(pml4[pml4e].address * 4096);
|
||||
for (unsigned pdpe = 0; pdpe < 512; ++pdpe)
|
||||
{
|
||||
if (!pdp[pdpe].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d]", pml4e, pdpe);
|
||||
printPDE(pdp[pdpe]);
|
||||
|
||||
baos::PageTableEntry* pd = std::bit_cast<baos::PageTableEntry*>(pdp[pdpe].address * 4096);
|
||||
for (unsigned pde = 0; pde < 512; ++pde)
|
||||
{
|
||||
if (!pd[pde].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d.%d]", pml4e, pdpe, pde);
|
||||
printPDE(pd[pde]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void main()
|
||||
@ -72,6 +129,11 @@ extern "C" void main()
|
||||
cmdShowKeys();
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "dump-pages")
|
||||
{
|
||||
cmdDumpPageTable();
|
||||
continue;
|
||||
}
|
||||
std::printf("Unknown command: %s.\n", cmd.c_str());
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
namespace
|
||||
{
|
||||
inline const size_t INIT_MALLOC_SPACE_ELEMENTS = 1024 * 1024;
|
||||
inline const size_t INIT_MALLOC_SPACE_BYTES = INIT_MALLOC_SPACE_ELEMENTS * sizeof(max_align_t);
|
||||
inline const size_t INIT_MALLOC_SPACE_BYTES = INIT_MALLOC_SPACE_ELEMENTS * alignof(max_align_t);
|
||||
|
||||
max_align_t gInitMallocSpace[INIT_MALLOC_SPACE_ELEMENTS];
|
||||
|
||||
@ -20,8 +20,8 @@ struct AllocInfo
|
||||
{
|
||||
size_t elements;
|
||||
};
|
||||
static_assert(sizeof(MallocBlock) <= sizeof(max_align_t));
|
||||
static_assert(sizeof(AllocInfo) <= sizeof(max_align_t));
|
||||
static_assert(sizeof(MallocBlock) <= alignof(max_align_t));
|
||||
static_assert(sizeof(AllocInfo) <= alignof(max_align_t));
|
||||
|
||||
MallocBlock* gNextBlock = []()
|
||||
{
|
||||
|
272
targets/_any/src/drivers/usb.cpp
Normal file
272
targets/_any/src/drivers/usb.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
|
||||
#include "drivers/usb.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include "drivers/pci.hpp"
|
||||
|
||||
namespace baos::usb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline constexpr std::uint8_t USB_PROGIF_UHCI_CONTROLLER = 0x0;
|
||||
inline constexpr std::uint8_t USB_PROGIF_OHCI_CONTROLLER = 0x10;
|
||||
inline constexpr std::uint8_t USB_PROGIF_EHCI_CONTROLLER = 0x20;
|
||||
inline constexpr std::uint8_t USB_PROGIF_XHCI_CONTROLLER = 0x30;
|
||||
|
||||
#define BAOS_MEMORY_REGISTER_RO(offset, type, name) \
|
||||
[[nodiscard]] const Register<type> name() const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RW(offset, type, name) \
|
||||
[[nodiscard]] Register<type> name() const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RO_PARAM(offset, type, name, paramType, paramName) \
|
||||
[[nodiscard]] const Register<type> name(paramType paramName) const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RW_PARAM(offset, type, name, paramType, paramName) \
|
||||
[[nodiscard]] Register<type> name(paramType paramName) const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
|
||||
struct XHCIHCSParams1
|
||||
{
|
||||
std::uint8_t maxSlots;
|
||||
std::uint16_t maxInterrupts : 11;
|
||||
std::uint8_t reserved : 5;
|
||||
std::uint8_t maxPorts;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(XHCIHCSParams1) == 4);
|
||||
|
||||
#define BAOS_MEMORY_BARRIER() __asm__ __volatile__ ("" : : : "memory")
|
||||
#define BAOS_STORE_FENCE() __asm__ __volatile__ ("sfence" : : : "memory")
|
||||
#define BAOS_LOAD_FENCE() __asm__ __volatile__ ("lfence" : : : "memory")
|
||||
#define BAOS_MEMORY_FENCE() __asm__ __volatile__ ("mfence" : : : "memory")
|
||||
|
||||
template<typename T>
|
||||
class Register
|
||||
{
|
||||
private:
|
||||
T& mMemory;
|
||||
public:
|
||||
explicit Register(T& memory) noexcept : mMemory(memory) {}
|
||||
|
||||
Register& operator=(const T& value) noexcept
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const noexcept { return get(); }
|
||||
|
||||
void set(const T& value) noexcept
|
||||
{
|
||||
BAOS_STORE_FENCE();
|
||||
mMemory = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] T get() const noexcept
|
||||
{
|
||||
BAOS_MEMORY_BARRIER();
|
||||
T value = mMemory;
|
||||
BAOS_LOAD_FENCE();
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct XHCICapabilityRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RO(0x00, std::uint8_t, caplength)
|
||||
BAOS_MEMORY_REGISTER_RO(0x02, std::uint16_t, ifaceVersion)
|
||||
BAOS_MEMORY_REGISTER_RO(0x04, XHCIHCSParams1, hcsParams1)
|
||||
BAOS_MEMORY_REGISTER_RO(0x08, std::uint32_t, hcsParams2)
|
||||
BAOS_MEMORY_REGISTER_RO(0x0C, std::uint32_t, hcsParams3)
|
||||
BAOS_MEMORY_REGISTER_RO(0x10, std::uint32_t, hccParams1)
|
||||
BAOS_MEMORY_REGISTER_RO(0x14, std::uint32_t, dbOff)
|
||||
BAOS_MEMORY_REGISTER_RO(0x18, std::uint32_t, rtsOff)
|
||||
BAOS_MEMORY_REGISTER_RO(0x1C, std::uint32_t, hccParams2)
|
||||
BAOS_MEMORY_REGISTER_RO(0x20, std::uint32_t, vtiosOff)
|
||||
};
|
||||
|
||||
struct XHCIUSBCommand
|
||||
{
|
||||
/* 0 */ bool runStop : 1;
|
||||
/* 1 */ bool hostControllerReset : 1;
|
||||
/* 2 */ bool interrupterEnable : 1;
|
||||
/* 3 */ bool hostSystemErrorEnable : 1;
|
||||
/* 4 */ std::uint8_t reserved0 : 3;
|
||||
/* 7 */ bool lightHostControllerReset : 1;
|
||||
/* 8 */ bool controllerSaveState : 1;
|
||||
/* 9 */ bool controllerRestoreState : 1;
|
||||
/* 10 */ bool enableWrapEvent : 1;
|
||||
/* 11 */ bool enableU3MFINDEXStop : 1;
|
||||
/* 12 */ std::uint8_t reserved1 : 1;
|
||||
/* 13 */ bool cemEnable : 1;
|
||||
/* 14 */ bool extendedTBCEnable : 1;
|
||||
/* 15 */ bool extendedTBCTRBStatusEnable : 1;
|
||||
/* 16 */ bool vtioEnable : 1;
|
||||
/* 17 */ std::uint16_t reserved2 : 15;
|
||||
};
|
||||
static_assert(sizeof(XHCIUSBCommand) == 4);
|
||||
|
||||
struct XHCIUSBStatus
|
||||
{
|
||||
/* 0 */ bool hcHalted : 1;
|
||||
/* 1 */ std::uint8_t reserved0 : 1;
|
||||
/* 2 */ bool hostSystemError : 1;
|
||||
/* 3 */ bool eventInterrupt : 1;
|
||||
/* 4 */ bool portChangeDetect : 1;
|
||||
/* 5 */ std::uint8_t reserved1 : 3;
|
||||
/* 8 */ const bool saveStateStatus : 1;
|
||||
/* 9 */ const bool restoreStateStatus : 1;
|
||||
/* 10 */ bool saveRestoreError : 1;
|
||||
/* 11 */ const bool controllerNotReady : 1;
|
||||
/* 12 */ const bool hostControllerError : 1;
|
||||
/* 13 */ std::uint32_t reserved2 : 19;
|
||||
};
|
||||
static_assert(sizeof(XHCIUSBStatus) == 4);
|
||||
|
||||
struct XHCIConfig
|
||||
{
|
||||
std::uint8_t maxDeviceSlotsEnabled : 7;
|
||||
bool u3EntryEnable : 1;
|
||||
bool configurationInformationEnable : 1;
|
||||
std::uint32_t reserved : 22;
|
||||
};
|
||||
static_assert(sizeof(XHCIConfig) == 4);
|
||||
|
||||
struct XHCIOperationalRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RW(0x00, XHCIUSBCommand, usbCmd)
|
||||
BAOS_MEMORY_REGISTER_RW(0x04, XHCIUSBStatus, usbStatus)
|
||||
BAOS_MEMORY_REGISTER_RO(0x08, std::uint32_t, pageSize)
|
||||
BAOS_MEMORY_REGISTER_RW(0x14, std::uint32_t, dnCtrl)
|
||||
BAOS_MEMORY_REGISTER_RW(0x18, std::uint64_t, crcr)
|
||||
BAOS_MEMORY_REGISTER_RW(0x30, std::uint64_t, dcbaap)
|
||||
BAOS_MEMORY_REGISTER_RW(0x38, XHCIConfig, config)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x400 + 0x10 * (port-1), std::uint32_t, portSC, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x404 + 0x10 * (port-1), std::uint32_t, portPMSC, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RO_PARAM(0x408 + 0x10 * (port-1), std::uint32_t, portLI, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x40C + 0x10 * (port-1), std::uint32_t, portHLPMC, std::uint32_t, port)
|
||||
};
|
||||
|
||||
struct XHCIHostControllerRuntimeRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RO(0x00, std::uint32_t, mfIndex)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x20 + (32 * interrupter), std::uint32_t, iman, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x24 + (32 * interrupter), std::uint32_t, imod, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x28 + (32 * interrupter), std::uint32_t, erstsz, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x30 + (32 * interrupter), std::uint64_t, erstba, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x38 + (32 * interrupter), std::uint64_t, erdp, std::uint32_t, interrupter)
|
||||
};
|
||||
|
||||
struct XHCIHostController
|
||||
{
|
||||
XHCICapabilityRegisters capabilityRegs = {};
|
||||
XHCIOperationalRegisters operationalRegs = {};
|
||||
XHCIHostControllerRuntimeRegisters controllerRuntimeRegs = {};
|
||||
|
||||
explicit XHCIHostController(std::uint64_t base) noexcept : capabilityRegs(base)
|
||||
{
|
||||
operationalRegs.base = capabilityRegs.base + capabilityRegs.caplength();
|
||||
controllerRuntimeRegs.base = capabilityRegs.base + capabilityRegs.rtsOff();
|
||||
}
|
||||
XHCIHostController(const XHCIHostController&) noexcept = default;
|
||||
|
||||
XHCIHostController& operator=(const XHCIHostController&) noexcept = default;
|
||||
};
|
||||
|
||||
bool initUHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initOHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initEHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::array<void*, 32> gDeviceContextBasePointers alignas(64);
|
||||
|
||||
bool initXHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
return false;
|
||||
|
||||
pci::GeneralDeviceHeader gdHeader = {};
|
||||
if (!pci::getGeneralDeviceHeader(pciHeader, gdHeader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint64_t registerMemory = static_cast<std::uint64_t>(gdHeader.bar1) << 32 | gdHeader.bar0;
|
||||
std::printf("XHCI register memory: 0x%lX\n", registerMemory);
|
||||
|
||||
XHCIHostController controller(registerMemory);
|
||||
// see chapter 4.2 (Host Controller Initialization) of the XHCI spec
|
||||
// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
|
||||
|
||||
// 1. wait until the controller is ready
|
||||
while (controller.operationalRegs.usbStatus().get().controllerNotReady);
|
||||
|
||||
// 2. set maximum enabled slots
|
||||
const XHCIHCSParams1 hcsParams1 = controller.capabilityRegs.hcsParams1();
|
||||
XHCIConfig config = controller.operationalRegs.config();
|
||||
config.maxDeviceSlotsEnabled = std::min(hcsParams1.maxSlots, static_cast<std::uint8_t>(gDeviceContextBasePointers.size() - 1));
|
||||
controller.operationalRegs.config() = config;
|
||||
|
||||
// 3. setup the device context base address pointer
|
||||
const std::uint64_t pointer = std::bit_cast<std::uint64_t>(gDeviceContextBasePointers.data());
|
||||
assert((pointer & 0b11111) == 0);
|
||||
controller.operationalRegs.dcbaap() = pointer;
|
||||
|
||||
XHCIUSBCommand cmd = controller.operationalRegs.usbCmd();
|
||||
cmd.runStop = true;
|
||||
controller.operationalRegs.usbCmd() = cmd;
|
||||
|
||||
cmd = controller.operationalRegs.usbCmd();
|
||||
while (true)
|
||||
{
|
||||
const XHCIUSBStatus status = controller.operationalRegs.usbStatus();
|
||||
if (!status.hcHalted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::printf("MaxSlots: %d, MaxInterrupts: %d, MaxPorts: %d\n", hcsParams1.maxSlots, hcsParams1.maxInterrupts, hcsParams1.maxPorts);
|
||||
//const XHCIOperationalRegisters operationalRegisters{registerMemory + capabilityRegisters.caplength()};
|
||||
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool initController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
switch (pciHeader.progIf)
|
||||
{
|
||||
case USB_PROGIF_UHCI_CONTROLLER:
|
||||
return initUHCIController(pciHeader);
|
||||
case USB_PROGIF_OHCI_CONTROLLER:
|
||||
return initOHCIController(pciHeader);
|
||||
case USB_PROGIF_EHCI_CONTROLLER:
|
||||
return initEHCIController(pciHeader);
|
||||
case USB_PROGIF_XHCI_CONTROLLER:
|
||||
return initXHCIController(pciHeader);
|
||||
default:
|
||||
return false; // what is this?
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "libs/psf.hpp"
|
||||
#include "os/memory.hpp"
|
||||
|
||||
namespace draw
|
||||
{
|
||||
|
@ -31,6 +31,15 @@ void iprintf(const char* format, ...) noexcept
|
||||
PrintFHelper<InterruptPrinter>().vprintf(format, parameters);
|
||||
va_end(parameters);
|
||||
}
|
||||
|
||||
__attribute__((no_caller_saved_registers))
|
||||
void splitPageToIndices(std::uint64_t page, std::uint16_t& outPML4Entry, std::uint16_t& outPDPEntry, std::uint16_t& outPDEntry, std::uint16_t& outPTEntry) noexcept
|
||||
{
|
||||
outPML4Entry = (page >> 27);
|
||||
outPDPEntry = (page >> 18) & 0x1FF;
|
||||
outPDEntry = (page >> 9) & 0x1FF;
|
||||
outPTEntry = page & 0x1FF;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" [[noreturn]] void __halt();
|
||||
@ -110,6 +119,37 @@ void isrGeneralProtectionFault(InterruptFrame* interruptFrame, interrupt_error_c
|
||||
__attribute__((interrupt))
|
||||
void isrPageFault(InterruptFrame* interruptFrame, interrupt_error_code_t errorCode) noexcept
|
||||
{
|
||||
static constexpr std::uint64_t PRESENT_BIT = (1 << 0);
|
||||
static constexpr std::uint64_t WRITE_BIT = (1 << 1);
|
||||
static constexpr std::uint64_t USER_BIT = (1 << 2);
|
||||
static constexpr std::uint64_t RESERVED_WRITE_BIT = (1 << 3);
|
||||
static constexpr std::uint64_t INSTRUCTION_FETCH_BIT = (1 << 4);
|
||||
static constexpr std::uint64_t PROTECTION_KEY_BIT = (1 << 5);
|
||||
static constexpr std::uint64_t SHADOW_STACK_BIT = (1 << 6);
|
||||
static constexpr std::uint64_t SGX_BIT = (1 << 7);
|
||||
|
||||
std::uint64_t cr2 = 0;
|
||||
__asm__ __volatile__(
|
||||
"mov %%cr2, %0"
|
||||
: "=a"(cr2)
|
||||
:
|
||||
);
|
||||
iprintf("address: 0x%lX\n", cr2);
|
||||
std::uint16_t pml4Index = 0;
|
||||
std::uint16_t pdpIndex = 0;
|
||||
std::uint16_t pdIndex = 0;
|
||||
std::uint16_t ptIndex = 0;
|
||||
splitPageToIndices(cr2 >> 12, pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
iprintf("page: %d.%d.%d.%d\n", pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
|
||||
iprintf("present: %s\n", (errorCode & PRESENT_BIT) ? "yes" : "no");
|
||||
iprintf("access: %s\n", (errorCode & WRITE_BIT) ? "write" : "read");
|
||||
iprintf("mode: %s\n", (errorCode & USER_BIT) ? "user" : "supervisor");
|
||||
iprintf("reserved write: %s\n", (errorCode & RESERVED_WRITE_BIT) ? "yes" : "no");
|
||||
iprintf("instruction fetch: %s\n", (errorCode & INSTRUCTION_FETCH_BIT) ? "yes" : "no");
|
||||
iprintf("protection key: %s\n", (errorCode & PROTECTION_KEY_BIT) ? "yes" : "no");
|
||||
iprintf("shadow stack: %s\n", (errorCode & SHADOW_STACK_BIT) ? "yes" : "no");
|
||||
iprintf("software guard extensions: %s\n", (errorCode & SGX_BIT) ? "yes" : "no");
|
||||
handleException("PageFault", interruptFrame);
|
||||
}
|
||||
|
||||
|
326
targets/_any/src/os/memory.cpp
Normal file
326
targets/_any/src/os/memory.cpp
Normal file
@ -0,0 +1,326 @@
|
||||
|
||||
#include "os/memory.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
|
||||
namespace baos
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using PageTable = std::array<PageTableEntry, 512>;
|
||||
constinit PageTable gPageMapLevel4 alignas(4096);
|
||||
|
||||
struct MemoryRangeAllocationInfo
|
||||
{
|
||||
std::array<std::uint8_t, 4086> usedPages;
|
||||
std::uint16_t numFreePages = 0;
|
||||
MemoryRangeAllocationInfo* next = nullptr;
|
||||
};
|
||||
static_assert(sizeof(MemoryRangeAllocationInfo) == 4096);
|
||||
|
||||
inline constexpr unsigned MAX_USABLE_RANGES = 32;
|
||||
std::array<MemoryRange, MAX_USABLE_RANGES> gUsableMemoryRanges;
|
||||
std::array<MemoryRangeAllocationInfo, MAX_USABLE_RANGES> gRangeAllocationInfo;
|
||||
std::uint8_t gNumUsableRanges = 0;
|
||||
|
||||
// initial 2MB page for setting up the page table
|
||||
// std::array<std::uint8_t, 2ul << 20> gInitPage alignas(2ul << 20);
|
||||
std::span<std::uint8_t> gPageTableSpace;
|
||||
std::span<std::uint8_t> gNextPageTableSpace;
|
||||
|
||||
void splitPageToIndices(std::uint64_t page, std::uint16_t& outPML4Entry, std::uint16_t& outPDPEntry, std::uint16_t& outPDEntry, std::uint16_t& outPTEntry) noexcept
|
||||
{
|
||||
outPML4Entry = (page >> 27);
|
||||
outPDPEntry = (page >> 18) & 0x1FF;
|
||||
outPDEntry = (page >> 9) & 0x1FF;
|
||||
outPTEntry = page & 0x1FF;
|
||||
}
|
||||
|
||||
PageTable& allocatePageTable() noexcept
|
||||
{
|
||||
PageTable* pageTable = std::bit_cast<PageTable*>(&*gPageTableSpace.begin());
|
||||
if (!pageTable)
|
||||
{
|
||||
int i = 5;
|
||||
}
|
||||
::new(pageTable) PageTable;
|
||||
gPageTableSpace = {gPageTableSpace.begin() + sizeof(PageTable), gPageTableSpace.end()};
|
||||
return *pageTable;
|
||||
}
|
||||
|
||||
PageTableEntry& getOrCreatePageTableEntry(PageTable& pageTable, std::uint16_t index)
|
||||
{
|
||||
PageTableEntry& entry = pageTable[index];
|
||||
if (!entry.present)
|
||||
{
|
||||
PageTable& childTable = allocatePageTable();
|
||||
entry.address = std::bit_cast<std::uint64_t>(&childTable) >> 12;
|
||||
entry.writable = true;
|
||||
entry.present = true;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
PageTableEntry& getOrCreatePage(std::uint64_t page, bool bigPage = false)
|
||||
{
|
||||
std::uint16_t pml4Index = 0;
|
||||
std::uint16_t pdpIndex = 0;
|
||||
std::uint16_t pdIndex = 0;
|
||||
std::uint16_t ptIndex = 0;
|
||||
splitPageToIndices(page, pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
|
||||
PageTableEntry& pml4Entry = getOrCreatePageTableEntry(gPageMapLevel4, pml4Index);
|
||||
|
||||
PageTable& pdp = *std::bit_cast<PageTable*>(pml4Entry.address << 12);
|
||||
PageTableEntry& pdpEntry = getOrCreatePageTableEntry(pdp, pdpIndex);
|
||||
|
||||
PageTable& pd = *std::bit_cast<PageTable*>(pdpEntry.address << 12);
|
||||
PageTableEntry& pdEntry = getOrCreatePageTableEntry(pd, pdIndex);
|
||||
|
||||
if (bigPage)
|
||||
{
|
||||
pdEntry.pageSize = true;
|
||||
return pdEntry;
|
||||
}
|
||||
|
||||
PageTable& pt = *std::bit_cast<PageTable*>(pdEntry.address << 12);
|
||||
return getOrCreatePageTableEntry(pt, ptIndex);
|
||||
}
|
||||
|
||||
void addUsableRange(const MemoryRange& range) noexcept
|
||||
{
|
||||
if (gNumUsableRanges >= MAX_USABLE_RANGES) {
|
||||
return;
|
||||
}
|
||||
gUsableMemoryRanges[gNumUsableRanges] = range;
|
||||
gRangeAllocationInfo[gNumUsableRanges].numFreePages = range.numPages;
|
||||
++gNumUsableRanges;
|
||||
}
|
||||
|
||||
std::uint64_t findFreePages(unsigned numConsecutive) noexcept
|
||||
{
|
||||
if (numConsecutive == 0 || numConsecutive > 4086) {
|
||||
return 0;
|
||||
}
|
||||
if (numConsecutive != 1) {
|
||||
return 0; // TODO!!!
|
||||
}
|
||||
|
||||
for (std::uint8_t rangeIdx = 0; rangeIdx < gNumUsableRanges; ++rangeIdx)
|
||||
{
|
||||
std::uint64_t page = gUsableMemoryRanges[rangeIdx].pageBase;
|
||||
for (MemoryRangeAllocationInfo* allocInfo = &gRangeAllocationInfo[rangeIdx]; allocInfo != nullptr; page += allocInfo->usedPages.size(), allocInfo = allocInfo->next)
|
||||
{
|
||||
if (allocInfo->numFreePages < numConsecutive) {
|
||||
continue;
|
||||
}
|
||||
for (unsigned pagesIdx = 0; pagesIdx < allocInfo->usedPages.size(); ++pagesIdx)
|
||||
{
|
||||
if (allocInfo->usedPages[pagesIdx] != 0xFF)
|
||||
{
|
||||
for (unsigned bit = 0; bit < 8; ++bit)
|
||||
{
|
||||
if ((allocInfo->usedPages[pagesIdx] & (1 << bit)) == 0)
|
||||
{
|
||||
allocInfo->usedPages[pagesIdx] |= (1 << bit);
|
||||
return page + (8 * pagesIdx) + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PageTable& setupPageTableEntry(PageTableEntry& entry, std::uint64_t page) noexcept
|
||||
{
|
||||
entry.address = page;
|
||||
entry.present = true;
|
||||
entry.writable = true;
|
||||
return *(::new(std::bit_cast<void*>(entry.address << 12)) PageTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cmdDumpPageTable() noexcept
|
||||
{
|
||||
std::uint64_t pml4Addr = std::bit_cast<std::uint64_t>(&gPageMapLevel4);
|
||||
std::printf("pml4 address: 0x%lX\n", pml4Addr);
|
||||
baos::PageTableEntry* pml4 = std::bit_cast<baos::PageTableEntry*>(pml4Addr);
|
||||
auto printPDE = [](const baos::PageTableEntry& pde)
|
||||
{
|
||||
std::printf("%s%s%s%s%s%s%s%s%s, address=%lX\n",
|
||||
pde.present ? " P" : "",
|
||||
pde.writable ? " W" : "",
|
||||
pde.user ? " U" : "",
|
||||
pde.writeThrough ? " WT" : "",
|
||||
pde.cacheDisabled ? " CD" : "",
|
||||
pde.accessed ? " A" : "",
|
||||
pde.dirty ? " D" : "",
|
||||
pde.pageSize ? " PS" : "",
|
||||
pde.executeDisable ? " XD" : "",
|
||||
pde.address * 4096);
|
||||
};
|
||||
for (unsigned pml4e = 0; pml4e < 512; ++pml4e)
|
||||
{
|
||||
if (!pml4[pml4e].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d]", pml4e);
|
||||
printPDE(pml4[pml4e]);
|
||||
|
||||
baos::PageTableEntry* pdp = std::bit_cast<baos::PageTableEntry*>(pml4[pml4e].address * 4096);
|
||||
for (unsigned pdpe = 0; pdpe < 512; ++pdpe)
|
||||
{
|
||||
if (!pdp[pdpe].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d]", pml4e, pdpe);
|
||||
printPDE(pdp[pdpe]);
|
||||
|
||||
baos::PageTableEntry* pd = std::bit_cast<baos::PageTableEntry*>(pdp[pdpe].address * 4096);
|
||||
for (unsigned pde = 0; pde < 512; ++pde)
|
||||
{
|
||||
if (!pd[pde].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d.%d]", pml4e, pdpe, pde);
|
||||
printPDE(pd[pde]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool canFitPageTableSpace(MemoryRange range, std::uint64_t& outBase) noexcept
|
||||
{
|
||||
if (range.type != MemoryType::USABLE) {
|
||||
return false;
|
||||
}
|
||||
const std::uint64_t possibleBase = std::alignUp(range.pageBase, 512ul); // needs to be 2MB aligned
|
||||
if (range.numPages - (possibleBase - range.pageBase) >= 512)
|
||||
{
|
||||
outBase = possibleBase;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void setupPaging(std::span<const MemoryRange> memoryRanges) noexcept
|
||||
{
|
||||
// addUsableRange({
|
||||
// .pageBase = std::bit_cast<std::uint64_t>(gInitPages.data()) >> 12,
|
||||
// .numPages = gInitPages.size() / 4096,
|
||||
// .type = MemoryType::USABLE
|
||||
// });
|
||||
std::uint64_t firstKernelPage = std::bit_cast<std::uint64_t>(&gKernelStart) / 4096;
|
||||
std::uint64_t lastKernelPage = std::bit_cast<std::uint64_t>(&gKernelEnd) / 4096 + 1;
|
||||
for (MemoryRange range : memoryRanges)
|
||||
{
|
||||
if (range.type == MemoryType::USABLE) // ignore anything before the kernel
|
||||
{
|
||||
if (range.pageBase < lastKernelPage)
|
||||
{
|
||||
if (range.numPages < (lastKernelPage - range.pageBase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
range.numPages -= (lastKernelPage - range.pageBase);
|
||||
range.pageBase = lastKernelPage;
|
||||
}
|
||||
addUsableRange(range);
|
||||
|
||||
std::uint64_t tableSpaceBase = 0;
|
||||
if (gPageTableSpace.empty() && canFitPageTableSpace(range, tableSpaceBase))
|
||||
{
|
||||
std::uint8_t* tableSpacePtr = std::bit_cast<std::uint8_t*>(tableSpaceBase * 4096);
|
||||
gPageTableSpace = {tableSpacePtr, 2ul << 30};
|
||||
setupIdentityPaging2M(tableSpaceBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
// start by setting up identity paging for the kernel
|
||||
// __get_cpuid()
|
||||
setupIdentityPaging(firstKernelPage, lastKernelPage);
|
||||
// std::printf("Kernel pages: %ld-%ld\n", firstKernelPage, lastKernelPage);
|
||||
|
||||
// setup identity paging for all reserved ranges so we can continue using them
|
||||
// for (const MemoryRange& range : memoryRanges)
|
||||
// {
|
||||
// if (range.type == MemoryType::RESERVED || range.type == MemoryType::MMIO)
|
||||
// {
|
||||
// setupIdentityPaging(range.pageBase, range.pageBase + range.numPages);
|
||||
// }
|
||||
// }
|
||||
|
||||
// cmdDumpPageTable();
|
||||
std::uint64_t cr3 = std::bit_cast<std::uint64_t>(gPageMapLevel4.data());
|
||||
__asm__ __volatile__ (
|
||||
"mov %0, %%cr3"
|
||||
:
|
||||
: "a"(cr3)
|
||||
);
|
||||
}
|
||||
|
||||
const char* memoryTypeName(MemoryType type) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MemoryType::USABLE:
|
||||
return "USABLE";
|
||||
case MemoryType::UNUSABLE:
|
||||
return "UNUSABLE";
|
||||
case MemoryType::MMIO:
|
||||
return "MMIO";
|
||||
case MemoryType::RESERVED:
|
||||
return "RESERVED";
|
||||
}
|
||||
return "<INVALID>";
|
||||
}
|
||||
|
||||
void* allocatePages(unsigned numConsecutive) noexcept
|
||||
{
|
||||
const std::uint64_t firstPage = findFreePages(numConsecutive);
|
||||
if (firstPage == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
setupIdentityPaging(firstPage, firstPage + numConsecutive);
|
||||
return std::bit_cast<void*>(firstPage << 12);
|
||||
}
|
||||
|
||||
|
||||
void setupIdentityPaging(std::uint64_t page) noexcept
|
||||
{
|
||||
PageTableEntry& pageTableEntry = getOrCreatePage(page);
|
||||
pageTableEntry.address = page;
|
||||
}
|
||||
|
||||
void setupIdentityPaging2M(std::uint64_t page) noexcept
|
||||
{
|
||||
PageTableEntry& pageTableEntry = getOrCreatePage(page, true);
|
||||
pageTableEntry.address = page;
|
||||
}
|
||||
|
||||
void setupIdentityPaging(std::uint64_t firstPage, std::uint64_t lastPage) noexcept
|
||||
{
|
||||
for (std::uint64_t page = firstPage; page != lastPage; ++page)
|
||||
{
|
||||
if (page % 512 == 0 && (lastPage - page) >= 512)
|
||||
{
|
||||
setupIdentityPaging2M(page);
|
||||
page += 511; // loop will add the last 1
|
||||
}
|
||||
else
|
||||
{
|
||||
setupIdentityPaging(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ SECTIONS
|
||||
work around this issue. This does not use that feature, so 2M was
|
||||
chosen as a safer option than the traditional 1M. */
|
||||
. = 2M;
|
||||
gKernelStart = .;
|
||||
|
||||
/* First put the multiboot header, as it is required to be put very early
|
||||
in the image or the bootloader won't recognize the file format.
|
||||
|
@ -1,19 +1,22 @@
|
||||
|
||||
#include "boot.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "os/draw.hpp"
|
||||
#include "os/memory.hpp"
|
||||
#include "os/serial.hpp"
|
||||
#include "os/tty.hpp"
|
||||
#include "drivers/pci.hpp"
|
||||
#include "drivers/pic.hpp"
|
||||
#include "drivers/ps2.hpp"
|
||||
#include "drivers/usb.hpp"
|
||||
#include "./x86_64.hpp"
|
||||
|
||||
extern "C" int gKernelEnd;
|
||||
|
||||
namespace
|
||||
{
|
||||
inline constexpr std::uint8_t MASTER_PIC_OFFSET = 0x20;
|
||||
@ -67,61 +70,67 @@ inline constexpr std::uint16_t SEGIDX_TSS = 5 << 3;
|
||||
|
||||
constinit std::array<InterruptDescriptor, 256> IDT alignas(16);
|
||||
|
||||
void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
|
||||
|
||||
inline baos::MemoryType mapEfiMemoryType(UINT32 type) noexcept
|
||||
{
|
||||
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
|
||||
switch (type)
|
||||
{
|
||||
case EfiLoaderCode:
|
||||
case EfiLoaderData:
|
||||
case EfiBootServicesCode:
|
||||
case EfiBootServicesData:
|
||||
case EfiConventionalMemory:
|
||||
return baos::MemoryType::USABLE;
|
||||
case EfiRuntimeServicesCode:
|
||||
case EfiRuntimeServicesData:
|
||||
case EfiUnusableMemory:
|
||||
case EfiACPIReclaimMemory:
|
||||
case EfiMaxMemoryType:
|
||||
default:
|
||||
return baos::MemoryType::UNUSABLE;
|
||||
case EfiMemoryMappedIO:
|
||||
case EfiMemoryMappedIOPortSpace:
|
||||
return baos::MemoryType::MMIO;
|
||||
case EfiACPIMemoryNVS:
|
||||
case EfiPalCode:
|
||||
case EfiReservedMemoryType:
|
||||
return baos::MemoryType::RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<baos::MemoryRange> detectMemoryRangesFromEfi(const EfiMemoryMap& memoryMap) noexcept
|
||||
{
|
||||
std::vector<baos::MemoryRange> ranges;
|
||||
|
||||
ranges.reserve(memoryMap.mapSize / memoryMap.descriptorSize);
|
||||
for (const EFI_MEMORY_DESCRIPTOR& descriptor : memoryMap)
|
||||
{
|
||||
if (!isEfiMemoryTypeUsable(descriptor.Type))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const baos::MemoryType type = mapEfiMemoryType(descriptor.Type);
|
||||
|
||||
uint64_t addr = descriptor.PhysicalStart;
|
||||
uint64_t len = descriptor.NumberOfPages * 4096; // TODO: is this the correct page size?
|
||||
// anything before the kernel we ignore
|
||||
if (addr < minAddr)
|
||||
{
|
||||
// if the entire entry is before the kernel, continue
|
||||
if (addr + len <= minAddr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// otherwise shrink it and use the rest
|
||||
len -= (minAddr - addr);
|
||||
addr = minAddr;
|
||||
}
|
||||
__ba_registerAllocatableMemory(reinterpret_cast<void*>(addr), len);
|
||||
assert(descriptor.PhysicalStart % 4096 == 0);
|
||||
uint64_t pageBase = descriptor.PhysicalStart / 4096;
|
||||
uint64_t numPages = descriptor.NumberOfPages;
|
||||
ranges.push_back({.pageBase = pageBase, .numPages = numPages, .type = type});
|
||||
}
|
||||
#if 0
|
||||
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
|
||||
for (uint32_t addr = firstEntryAddr; addr < firstEntryAddr + bufferLength;)
|
||||
std::sort(ranges.begin(), ranges.end());
|
||||
|
||||
// merge adjacent ranges
|
||||
for (std::size_t idx = ranges.size() - 1; idx > 0; --idx)
|
||||
{
|
||||
multiboot_memory_map_t entry = *reinterpret_cast<multiboot_memory_map_t*>(addr);
|
||||
baos::MemoryRange& left = ranges[idx-1];
|
||||
baos::MemoryRange& right = ranges[idx];
|
||||
|
||||
if(entry.type == MULTIBOOT_MEMORY_AVAILABLE)
|
||||
if (left.type == right.type && left.pageBase + left.numPages == right.pageBase)
|
||||
{
|
||||
std::printf("Start Addr: %X | Length: %b\n",
|
||||
static_cast<unsigned>(entry.addr), static_cast<size_t>(entry.len));
|
||||
|
||||
// anything before the kernel we ignore
|
||||
if (entry.addr < minAddr)
|
||||
{
|
||||
// if the entire entry is before the kernel, continue
|
||||
if (entry.addr + entry.len <= minAddr)
|
||||
{
|
||||
addr += sizeof(entry.size) + entry.size;
|
||||
continue;
|
||||
}
|
||||
// otherwise shrink it and use the rest
|
||||
entry.len -= (minAddr - entry.addr);
|
||||
entry.addr = minAddr;
|
||||
}
|
||||
__ba_registerAllocatableMemory(reinterpret_cast<void*>(entry.addr), entry.len);
|
||||
left.numPages += right.numPages;
|
||||
right.numPages = 0;
|
||||
}
|
||||
addr += sizeof(entry.size) + entry.size;
|
||||
}
|
||||
#endif
|
||||
std::erase_if(ranges, [](const baos::MemoryRange& range)
|
||||
{
|
||||
return range.numPages == 0;
|
||||
});
|
||||
return ranges;
|
||||
}
|
||||
|
||||
using initfunc_t = void *();
|
||||
@ -161,6 +170,21 @@ void setupInterrupt(std::uint8_t index, THandler handler) noexcept
|
||||
.offsetHigh = (offset >> 16)
|
||||
};
|
||||
}
|
||||
|
||||
void initializePCIDevice(const baos::pci::Header& header) noexcept
|
||||
{
|
||||
using namespace baos;
|
||||
switch (header.baseClass)
|
||||
{
|
||||
case pci::BaseClass::SERIAL_BUS_CONTROLLER:
|
||||
switch (header.subClass)
|
||||
{
|
||||
case pci::SubClass::USB_CONTROLLER:
|
||||
(void) usb::initController(header); // TODO: print something on error?
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
@ -210,6 +234,11 @@ void kernel_main()
|
||||
initGlobals();
|
||||
|
||||
// init the heap (required for the double buffer)
|
||||
const std::vector<MemoryRange> ranges = detectMemoryRangesFromEfi(gBootInfo->memoryMap);
|
||||
setupPaging({ranges.begin(), ranges.end()}); // TODO: move this up when it's done
|
||||
identityMapRegion(gBootInfo, sizeof(*gBootInfo));
|
||||
identityMapRegion(gBootInfo->displayInfo.frameBufferBase, gBootInfo->displayInfo.frameBufferPitch * gBootInfo->displayInfo.frameBufferHeight);
|
||||
|
||||
// initHeapFromEfiMemoryMap(gBootInfo->memoryMap);
|
||||
|
||||
// initialize the framebuffer
|
||||
@ -223,6 +252,11 @@ void kernel_main()
|
||||
// initialize terminal interface
|
||||
tty::initialize();
|
||||
|
||||
for (const MemoryRange& range : ranges)
|
||||
{
|
||||
std::printf("Start: 0x%lX, Pages: %ld, Type: %s\n", range.pageBase << 12, range.numPages, memoryTypeName(range.type));
|
||||
}
|
||||
|
||||
if (!initSerialPort(PORT_COM1))
|
||||
{
|
||||
tty::write("Error initializing serial port.\n");
|
||||
@ -242,6 +276,11 @@ void kernel_main()
|
||||
return;
|
||||
}
|
||||
|
||||
for (const pci::Header header : pci::enumerateDevices())
|
||||
{
|
||||
initializePCIDevice(header);
|
||||
}
|
||||
|
||||
std::puts("This is BadAppleOS and everything is fine!\n");
|
||||
|
||||
// __enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main);
|
||||
|
Loading…
x
Reference in New Issue
Block a user