--- /dev/null
+Memory mapping:
+ 0x0000000000000000···0x00007FFFFFFFFFFF (128 TiB)= user
+
+ 0xFFFF800000000000···0xFFFFBFFFFFFFFFFF ( 64 TiB) = physical memory mapping
+ 0xFFFFC00000000000···0xFFFFFFFF7FFFFFFF (~64 TiB) = unused
+ 0xFFFFFFFF80000000···0xFFFFFFFFFFFFFFFF ( 2 GiB) = kernel
+
+Unused pages: First 8 bytes: size of current streak, in pages
+ Next 8 bytes: pointer to next stream (or 0 if no free memory after this streak).
+ All other bytes are unused.
#include "multiboot2.hpp"
+#include "paging.hpp"
+#include "utils.hpp"
+#include "serial.hpp"
-extern "C" void kmain() {
- volatile short* vga_text = reinterpret_cast<volatile short*>((void*)0xFFFFFFFF800B8000);
- const char* str = "Success ! Took me long enough.";
- for (unsigned long i = 0; str[i] != '\0'; i++) {
- vga_text[i] = (0x02 << 8) + str[i];
+extern "C" void kmain(unsigned long magic, os::phys_ptr<const multiboot2::info_start> info) {
+ os::assert(magic == 0x36D76289, "Error: Incorrect magic number: wasn't booted with multiboot.");
+
+ if (!os::init_serial_port()) {
+ os::halt();
}
- while (true) {}
+
+ uintptr_t ram_ptr;
+ asm("mov $test_ram - 0xFFFFFFFF80000000,%0" : "=ri"(ram_ptr));
+ os::paging::page_allocator page_allocator(ram_ptr);
+
+ for (auto it = multiboot2::next(info); it->type != multiboot2::info::type_t::end; it = multiboot2::next(it)) {
+ switch (it->type) {
+ default: break;
+ }
+ }
+
+ os::halt();
}
--- /dev/null
+#include "paging.hpp"
+
+os::paging::page_allocator::page_allocator(os::phys_ptr<paging::page> head): head(head.get_phys_addr()) {
+ phys_ptr<page> prev{nullptr};
+ for (auto it = this->head; it != nullptr; prev = it, it = it->next_streak) {
+ if (prev >= it) {
+ os::println("Error: unordered memory given to the page allocator.");
+ break;
+ }
+ while (it.get_phys_addr() + it->streak_size * 0x1000 == it->next_streak.get_phys_addr()) {
+ it->streak_size += it->next_streak->streak_size;
+ it->next_streak = it->next_streak->next_streak;
+ }
+ }
+}
+
+void os::paging::page_allocator::print_all() const {
+ if (head == nullptr) {
+ os::println("No RAM left.");
+ } else for (auto it = head; it != nullptr; it = it->next_streak) {
+ os::print("addr:size = 0x");
+ os::print(it.get_phys_addr());
+ os::print(":0x");
+ os::print(it->streak_size);
+ os::printc('\n');
+ }
+}
+
+os::paging::page_allocator::block os::paging::page_allocator::allocate(uint64_t count) {
+ phys_ptr<page> prev{nullptr};
+ for (auto it = head; it != nullptr; prev = it, it = it->next_streak) {
+ if (it->streak_size == count) {
+ if (prev == nullptr) {
+ head = it->next_streak;
+ } else {
+ prev->next_streak = it->next_streak;
+ }
+ return {
+ .ptr = it.get_phys_addr(),
+ .size = count,
+ };
+ }
+ if (it->streak_size > count) {
+ if (prev == nullptr) {
+ head = it.get_phys_addr() + count * 0x1000;
+ head->streak_size = it->streak_size - count;
+ head->next_streak = it->next_streak;
+ } else {
+ prev->next_streak = it.get_phys_addr() + count * 0x1000;
+ prev->next_streak->streak_size = it->streak_size - count;
+ head->next_streak->next_streak = it->next_streak;
+ }
+ return {
+ .ptr = it.get_phys_addr(),
+ .size = count,
+ };
+ }
+ }
+ return {.ptr = nullptr, .size = 0};
+}
+
+void os::paging::page_allocator::deallocate(block b) {
+ const phys_ptr<page> ptr = b.ptr.get_phys_addr();
+ const uint64_t size = b.size;
+
+
+ phys_ptr<page> before{nullptr};
+ phys_ptr<page> after = head;
+ while (after != nullptr && after < ptr) {
+ before = after;
+ after = after->next_streak;
+ }
+ if (before != nullptr && before.get_phys_addr() + 0x1000 * before->streak_size == ptr) {
+ // Should merge with previous.
+ if (after != nullptr && ptr.get_phys_addr() + 0x1000 * size == after) {
+ // Should merge with next.
+ before->streak_size += size + after->streak_size;
+ before->next_streak = after->next_streak;
+ } else {
+ // Shouldn't merge with next.
+ before->streak_size += size;
+ }
+ } else {
+ // Shouldn't merge with previous.
+ if (after != nullptr && ptr.get_phys_addr() + 0x1000 * size == after) {
+ // Should merge with next.
+ if (before != nullptr) {
+ before->next_streak = before->next_streak.get_phys_addr() - 0x1000 * size;
+ before->next_streak->next_streak = after->next_streak;
+ before->next_streak->streak_size = after->streak_size + size;
+ } else {
+ head = head.get_phys_addr() - 0x1000 * size;
+ head->next_streak = after->next_streak;
+ head->streak_size = after->streak_size + size;
+ }
+ } else {
+ // Shouldn't merge with next.
+ if (before != nullptr) {
+ before->next_streak = ptr;
+ ptr->next_streak = after;
+ ptr->streak_size = size;
+ } else {
+ head = ptr;
+ ptr->next_streak = after;
+ ptr->streak_size = size;
+ }
+ }
+ }
+}
--- /dev/null
+#pragma once
+
+#include <stdint.h>
+#include "serial.hpp"
+#include "utils.hpp"
+
+namespace os { namespace paging {
+
+template <int order>
+struct __attribute__((aligned(0x1000))) paging_table;
+
+template <int order>
+struct paging_entry {
+public:
+ static_assert(order >= 0, "Error: negative order for paging entry.");
+ paging_entry(bool is_page) {
+ os::assert(order != 0 || is_page, "4KiB defined as non-page.");
+ if (order != 0 && is_page) {
+ data |= 1 << 7; // Mark as smallest page.
+ }
+ }
+ inline bool is_page() {
+ return order == 0 || (data & (1 << 7)) != 0;
+ }
+
+ inline bool NX() {
+ return (data & (1ul << 63)) != 0;
+ }
+ inline void NX(bool v) {
+ data = (data & ~(1ul << 63)) | (v ? (1ul << 63) : 0);
+ }
+ inline unsigned PK() {
+ os::assert(is_page(), "Error: read protection key of non-page.");
+ return (data >> 52) & 0xF;
+ }
+ inline void PK(unsigned v) {
+ os::assert(is_page(), "Error: write to protection key of non-page.");
+ os::assert((v & 0xF) == v, "Error: incorrect protection key.");
+ data = (data & ~(0xFul << 59)) | (uint64_t(v) << 59);
+ }
+ inline unsigned AVL_high() {
+ return (data >> 52) & (is_page() ? 0x7F : 0x7FF);
+ }
+ inline void AVL_high(unsigned v) {
+ os::assert((v & (is_page() ? 0x7F : 0x7FF)) == v, "Error: incorrect AVL high bits.");
+ data = (data & ~((is_page() ? 0x7Ful : 0x7FFul) << 52)) | (uint64_t(v) << 52);
+ }
+ inline phys_ptr<paging_table<order - 1>> base_address() {
+ return {data & 0x000FFFFFFFFFF000 & ~(order != 0 && is_page() ? 1ul << 12 : 0ul)};
+ }
+ inline void base_address(phys_ptr<paging_table<order - 1>> v) {
+ const uint64_t v_int = v.get_phys_addr();
+ os::assert((v_int & 0x000FFFFFFFFFF000 & (0xFFFFFFFFFFFFF000 << (is_page() ? 9 * order : 0))) == v_int, "Error: incorrect base address.");
+ data = (data & ~0x000FFFFFFFFFF000ul & ~(order != 0 && is_page() ? 1ul << 12 : 0ul)) | v_int;
+ }
+ inline unsigned AVL_low() {
+ return (data >> 9) & 0x7;
+ }
+ inline void AVL_low(unsigned v) {
+ os::assert((v & 0x7) == v, "Error: incorrect AVL low bits");
+ data = (data & ~(0x7ul << 9)) | (v << 9);
+ }
+ inline bool G() {
+ os::assert(is_page(), "Error: read global bit of non-page.");
+ return (data & (1 << 8)) != 0;
+ }
+ inline void G(bool v) {
+ os::assert(is_page(), "Error: write to global bit of non-page.");
+ data = (data & ~(1ul << 8)) | (v ? 1 << 8 : 0);
+ }
+ inline bool PAT() {
+ os::assert(is_page(), "Error: read PAT bit of non-page.");
+ return (data & (1 << (order == 0 ? 7 : 12))) != 0;
+ }
+ inline void PAT(bool v) {
+ os::assert(is_page(), "Error: write to PAT bit of non-page.");
+ data = (data & ~(1ul << (order == 0 ? 7 : 12))) | (v ? 1 << (order == 0 ? 7 : 12) : 0);
+ }
+ inline bool D() {
+ os::assert(is_page(), "Error: read dirty bit of non-page.");
+ return (data & (1 << 6)) != 0;
+ }
+ inline void D(bool v) {
+ os::assert(is_page(), "Error: write to dirty bit of non-page.");
+ data = (data & ~(1ul << 6)) | (v ? 1 << 6 : 0);
+ }
+ inline bool A() {
+ return (data & (1 << 5)) != 0;
+ }
+ inline void A(bool v) {
+ data = (data & ~(1ul << 5)) | (v ? 1 << 5 : 0);
+ }
+ inline bool PCD() {
+ return (data & (1 << 4)) != 0;
+ }
+ inline void PCD(bool v) {
+ data = (data & ~(1ul << 4)) | (v ? 1 << 4 : 0);
+ }
+ inline bool PWT() {
+ return (data & (1 << 3)) != 0;
+ }
+ inline void PWT(bool v) {
+ data = (data & ~(1ul << 3)) | (v ? 1 << 3 : 0);
+ }
+ inline bool U_S() {
+ return (data & (1 << 2)) != 0;
+ }
+ inline void U_S(bool v) {
+ data = (data & ~(1ul << 2)) | (v ? 1 << 2 : 0);
+ }
+ inline bool R_W() {
+ return (data & (1 << 1)) != 0;
+ }
+ inline void R_W(bool v) {
+ data = (data & ~(1ul << 1)) | (v ? 1 << 1 : 0);
+ }
+ inline bool P() {
+ return (data & (1 << 0)) != 0;
+ }
+ inline void P(bool v) {
+ data = (data & ~(1ul << 0)) | (v ? 1 << 0 : 0);
+ }
+
+private:
+ uint64_t data;
+};
+
+template <int order>
+struct __attribute__((aligned(0x1000))) paging_table {
+ static_assert(order >= 0, "Paging table of negative order.");
+ paging_entry<order> contents[512];
+};
+
+template<>
+struct paging_table<-1> {
+ uint8_t contents[0x1000];
+};
+
+using PML4T = paging_table<3>;
+using PML4E = paging_entry<3>;
+using PDPT = paging_table<2>;
+using PDPE = paging_entry<2>;
+using PDT = paging_table<1>;
+using PDE = paging_entry<1>;
+using PT = paging_table<0>;
+using PE = paging_entry<0>;
+using page = paging_table<-1>;
+
+class page_allocator {
+public:
+ struct block {
+ phys_ptr<paging::page> ptr = nullptr;
+ uint64_t size;
+ };
+
+ page_allocator(phys_ptr<paging::page> head);
+ void print_all() const;
+
+ block allocate(uint64_t page_count);
+ void deallocate(block b);
+
+private:
+ struct __attribute__((aligned(0x1000))) page {
+ uint64_t streak_size;
+ phys_ptr<page> next_streak;
+ char padding[0x1000 - 8 - 8];
+ };
+ static_assert(sizeof(page) == 0x1000);
+ static_assert(alignof(page) == 0x1000);
+ phys_ptr<page> head;
+};
+
+}} // os::paging
--- /dev/null
+#include <stddef.h>
+#include "serial.hpp"
+
+namespace {
+ void outb(uint16_t port, uint8_t data) {
+ asm volatile ("outb %1,%0" : : "dN"(port), "a"(data));
+ }
+ uint8_t inb(uint16_t port) {
+ uint8_t data;
+ asm volatile ("inb %1,%0" : "=a"(data) : "dN"(port));
+ return data;
+ }
+}
+
+bool os::init_serial_port() {
+ outb(serial_port + 1, 0x00); // Disable interrupts.
+ outb(serial_port + 3, 0x80); // Prepare to set baud rate divisor (38400 baud).
+ outb(serial_port + 0, 0x03); // lo byte
+ outb(serial_port + 1, 0x00); // hi byte
+ outb(serial_port + 3, 0x03); // 8 bits, no parity, 1 stop bit.
+ outb(serial_port + 2, 0xC7); // Enable FIFO, clear FIFO, 14-byte threshold.
+ outb(serial_port + 4, 0x0B); // IRQs enabled, RTS/DSR set.
+
+ outb(serial_port + 4, 0x1E); // Enable loopback mode (to check).
+ outb(serial_port + 0, 0xAE); // Send byte.
+ if (inb(serial_port + 0) != 0xAE) {
+ return false;
+ }
+ outb(serial_port + 4, 0x0B); // Disable loopback.
+ return true;
+}
+
+bool os::serial_received() {
+ return (inb(serial_port + 5) & 0x01) != 0;
+}
+uint8_t os::read_serial() {
+ while (!serial_received()) {}
+ return inb(serial_port + 0);
+}
+bool os::serial_transmit_empty() {
+ return (inb(serial_port + 5) & 0x20) != 0;
+}
+void os::write_serial(uint8_t v) {
+ while (!serial_transmit_empty()) {}
+ outb(serial_port + 0, v);
+}
+
+void os::printc(char c) {
+ write_serial(c);
+}
+void os::print(const char* str) {
+ for (size_t i = 0; str[i] != '\0'; i++) {
+ os::printc(str[i]);
+ }
+}
+void os::print(uint64_t v) {
+ for (int i = 60; i >= 0; i -= 4) {
+ const int c = (v >> i) & 0xF;
+ os::printc(c < 10 ? c + '0' : c + 'A' - 10);
+ }
+}
+void os::print(uint32_t v) {
+ for (int i = 28; i >= 0; i -= 4) {
+ const int c = (v >> i) & 0xF;
+ os::printc(c < 10 ? c + '0' : c + 'A' - 10);
+ }
+}
+void os::print(uint16_t v) {
+ for (int i = 12; i >= 0; i -= 4) {
+ const int c = (v >> i) & 0xF;
+ os::printc(c < 10 ? c + '0' : c + 'A' - 10);
+ }
+}
+void os::print(uint8_t v) {
+ for (int i = 4; i >= 0; i -= 4) {
+ const int c = (v >> i) & 0xF;
+ os::printc(c < 10 ? c + '0' : c + 'A' - 10);
+ }
+}
+void os::print(int64_t v) {
+ if (v < 0) {
+ os::printc('-');
+ os::print(uint64_t(-v));
+ } else {
+ os::printc(' ');
+ os::print(uint64_t(v));
+ }
+}
+void os::print(int32_t v) {
+ if (v < 0) {
+ os::printc('-');
+ os::print(uint32_t(-v));
+ } else {
+ os::printc(' ');
+ os::print(uint32_t(v));
+ }
+}
+void os::print(int16_t v) {
+ if (v < 0) {
+ os::printc('-');
+ os::print(uint16_t(-v));
+ } else {
+ os::printc(' ');
+ os::print(uint16_t(v));
+ }
+}
+void os::print(int8_t v) {
+ if (v < 0) {
+ os::printc('-');
+ os::print(uint8_t(-v));
+ } else {
+ os::printc(' ');
+ os::print(uint8_t(v));
+ }
+}
+void os::println(const char* str) {
+ os::print(str);
+ os::printc('\n');
+}
+void os::assert(bool cond, const char* diagnostic) {
+ if (!cond) { os::println(diagnostic); }
+}
--- /dev/null
+#pragma once
+
+#include <stdint.h>
+
+namespace os {
+
+constexpr uint16_t serial_port{0x3F8};
+
+bool init_serial_port();
+
+bool serial_received();
+uint8_t read_serial();
+bool serial_transmit_empty();
+void write_serial(uint8_t v);
+
+void printc(char c);
+void print(const char* str);
+void print(uint64_t v);
+void print(uint32_t v);
+void print(uint16_t v);
+void print(uint8_t v);
+void print(int64_t v);
+void print(int32_t v);
+void print(int16_t v);
+void print(int8_t v);
+void println(const char* str);
+void assert(bool cond, const char* diagnostic);
+
+}
--- /dev/null
+.section .text
+.globl halt
+halt:
+ cli
+1: hlt
+ jmp 1b
--- /dev/null
+#pragma once
+
+#include <stddef.h>
+
+namespace os {
+
+ template <typename T>
+ class phys_ptr {
+ public:
+ constexpr phys_ptr(uintptr_t phys_addr): phys_addr(phys_addr) {}
+ constexpr phys_ptr(nullptr_t): phys_addr(0) {}
+
+ constexpr T& operator[](size_t i) const {
+ return get_virt_addr()[i];
+ }
+ constexpr T& operator*() const {
+ return *get_virt_addr();
+ }
+ constexpr T* operator->() const {
+ return get_virt_addr();
+ }
+ constexpr bool operator< (phys_ptr<T> other) const { return phys_addr < other.phys_addr; }
+ constexpr bool operator<=(phys_ptr<T> other) const { return phys_addr <= other.phys_addr; }
+ constexpr bool operator==(phys_ptr<T> other) const { return phys_addr == other.phys_addr; }
+ constexpr bool operator!=(phys_ptr<T> other) const { return phys_addr != other.phys_addr; }
+ constexpr bool operator> (phys_ptr<T> other) const { return phys_addr > other.phys_addr; }
+ constexpr bool operator>=(phys_ptr<T> other) const { return phys_addr >= other.phys_addr; }
+
+ constexpr uintptr_t get_phys_addr() const {
+ return phys_addr;
+ }
+
+ private:
+ constexpr T* get_virt_addr() const {
+ return reinterpret_cast<T*>(phys_addr + 0xFFFF800000000000);
+ }
+
+ uintptr_t phys_addr;
+ };
+
+ extern "C" void halt();
+} // namespace os