From d3db448188fb4a732c9130649f819cf76ed164d9 Mon Sep 17 00:00:00 2001 From: Amelia Coutard Date: Thu, 5 May 2022 19:17:47 +0200 Subject: [PATCH] Added serial logging and a simple physical page allocator --- docs.txt | 10 +++ src/kernel.cpp | 26 ++++++-- src/paging.cpp | 109 +++++++++++++++++++++++++++++++ src/paging.hpp | 173 +++++++++++++++++++++++++++++++++++++++++++++++++ src/serial.cpp | 122 ++++++++++++++++++++++++++++++++++ src/serial.hpp | 29 +++++++++ src/utils.S | 6 ++ src/utils.hpp | 42 ++++++++++++ 8 files changed, 511 insertions(+), 6 deletions(-) create mode 100644 docs.txt create mode 100644 src/paging.cpp create mode 100644 src/paging.hpp create mode 100644 src/serial.cpp create mode 100644 src/serial.hpp create mode 100644 src/utils.S create mode 100644 src/utils.hpp diff --git a/docs.txt b/docs.txt new file mode 100644 index 0000000..99fac45 --- /dev/null +++ b/docs.txt @@ -0,0 +1,10 @@ +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. diff --git a/src/kernel.cpp b/src/kernel.cpp index e7d9d6a..f6415ae 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -1,10 +1,24 @@ #include "multiboot2.hpp" +#include "paging.hpp" +#include "utils.hpp" +#include "serial.hpp" -extern "C" void kmain() { - volatile short* vga_text = reinterpret_cast((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 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(); } diff --git a/src/paging.cpp b/src/paging.cpp new file mode 100644 index 0000000..55ed826 --- /dev/null +++ b/src/paging.cpp @@ -0,0 +1,109 @@ +#include "paging.hpp" + +os::paging::page_allocator::page_allocator(os::phys_ptr head): head(head.get_phys_addr()) { + phys_ptr 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 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 ptr = b.ptr.get_phys_addr(); + const uint64_t size = b.size; + + + phys_ptr before{nullptr}; + phys_ptr 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; + } + } + } +} diff --git a/src/paging.hpp b/src/paging.hpp new file mode 100644 index 0000000..c2d0fa7 --- /dev/null +++ b/src/paging.hpp @@ -0,0 +1,173 @@ +#pragma once + +#include +#include "serial.hpp" +#include "utils.hpp" + +namespace os { namespace paging { + +template +struct __attribute__((aligned(0x1000))) paging_table; + +template +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> base_address() { + return {data & 0x000FFFFFFFFFF000 & ~(order != 0 && is_page() ? 1ul << 12 : 0ul)}; + } + inline void base_address(phys_ptr> 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 +struct __attribute__((aligned(0x1000))) paging_table { + static_assert(order >= 0, "Paging table of negative order."); + paging_entry 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 ptr = nullptr; + uint64_t size; + }; + + page_allocator(phys_ptr 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 next_streak; + char padding[0x1000 - 8 - 8]; + }; + static_assert(sizeof(page) == 0x1000); + static_assert(alignof(page) == 0x1000); + phys_ptr head; +}; + +}} // os::paging diff --git a/src/serial.cpp b/src/serial.cpp new file mode 100644 index 0000000..8cf52b7 --- /dev/null +++ b/src/serial.cpp @@ -0,0 +1,122 @@ +#include +#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); } +} diff --git a/src/serial.hpp b/src/serial.hpp new file mode 100644 index 0000000..a62a59a --- /dev/null +++ b/src/serial.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +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); + +} diff --git a/src/utils.S b/src/utils.S new file mode 100644 index 0000000..f1287d8 --- /dev/null +++ b/src/utils.S @@ -0,0 +1,6 @@ +.section .text +.globl halt +halt: + cli +1: hlt + jmp 1b diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..82abfb6 --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace os { + + template + 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 other) const { return phys_addr < other.phys_addr; } + constexpr bool operator<=(phys_ptr other) const { return phys_addr <= other.phys_addr; } + constexpr bool operator==(phys_ptr other) const { return phys_addr == other.phys_addr; } + constexpr bool operator!=(phys_ptr other) const { return phys_addr != other.phys_addr; } + constexpr bool operator> (phys_ptr other) const { return phys_addr > other.phys_addr; } + constexpr bool operator>=(phys_ptr 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(phys_addr + 0xFFFF800000000000); + } + + uintptr_t phys_addr; + }; + + extern "C" void halt(); +} // namespace os -- 2.47.0