diff --git a/.github/workflows/max-os.yml b/.github/workflows/max-os.yml index 3b1c199c..2743ebb8 100644 --- a/.github/workflows/max-os.yml +++ b/.github/workflows/max-os.yml @@ -42,7 +42,7 @@ jobs: - name: Build MaxOS (Release) run: | - cd toolchain/post_process + cd toolchain/pre_process ./version.sh --force cd ../../ mkdir -p cmake-build @@ -58,7 +58,7 @@ jobs: generate-docs: # The type of runner that the job will run on - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest # Don't generate docs on dev branch if: github.ref == 'refs/heads/main' diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 41aee021..7f9d6a5d 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cd1bb4f..b91d86d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,13 +2,13 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0) # Set the project name and the languages -project(MaxOS C CXX ASM) +PROJECT(MaxOS C CXX ASM) # Logs the compiler commands -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) # don't enable runtime type information -set(CMAKE_SKIP_RPATH TRUE) +SET(CMAKE_SKIP_RPATH TRUE) # Set the standard to C++20 SET(CMAKE_CXX_STANDARD 20) @@ -22,19 +22,37 @@ LINK_DIRECTORIES(${TOOLCHAIN_ROOT_DIR}/${TOOLCHAIN_PLATFORM}/lib/) # DEBUG / PROD IF(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) + SET(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) ENDIF() -## Set flags based on build type +# Set flags based on build type IF(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-DTARGET_DEBUG) + ADD_DEFINITIONS(-DTARGET_DEBUG) ENDIF() +# Function to make a library easier +function(MAKE_LIBRARY LIBNAME) + + # TODO: ADD_DEPENDENCIES(${LIBNAME} libc) (& for static) + + # Install dynamic library + ADD_LIBRARY(${LIBNAME} SHARED ${LIBRARY_SRCS}) + TARGET_INCLUDE_DIRECTORIES(${LIBNAME} PUBLIC include) + INSTALL(TARGETS ${LIBNAME} LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/filesystem/os/lib) + + # Install static library + ADD_LIBRARY(${LIBNAME}_static STATIC ${LIBRARY_SRCS}) + TARGET_INCLUDE_DIRECTORIES(${LIBNAME}_static PUBLIC include) + INSTALL(TARGETS ${LIBNAME}_static DESTINATION ${CMAKE_SOURCE_DIR}/filesystem/os/lib) + + # TODO: Install headers onto fs + +endfunction() + # Look to build in these directories ADD_SUBDIRECTORY(kernel) -#ADD_SUBDIRECTORY(libraries) +ADD_SUBDIRECTORY(libraries) ADD_SUBDIRECTORY(programs) -#ADD_SUBDIRECTORY(ports) ADD_CUSTOM_TARGET(image # Everything needs to be installed (compiled) before we can create the disk image diff --git a/README.md b/README.md index 236716ba..f98bdcb2 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ This is the list of required packages to build the operating system from source. * libisl-dev * cmake * telnet +* rsync Linux: ```sh @@ -194,7 +195,7 @@ No user usage so far (userland will be added in the future) ## Roadmap - +#### Core Kernel - [x] Bootloader - [x] GDT - [x] IDT @@ -210,27 +211,30 @@ No user usage so far (userland will be added in the future) - [x] Paging - [x] Userspace - [x] IPC -- [ ] VFS +- [x] VFS - [x] Loading ELF - [ ] Multiple Cores Support (SMP & Scheduler) -- [ ] Userland GUI -- [ ] CLI +- [ ] Move drivers to userspace +- [ ] Move VFS to userspace - [ ] Porting & Dynamically Linking Libc +- [ ] Move networking to userspace (& rewrite, fix) + +#### Userland +- [ ] GUI +- [ ] Terminal +- [ ] Connect to Clion with SMB for files and GDB for debugging in userspace +- [ ] DOOM Port - [ ] Self-hosted os +- [ ] GUI Framework - [ ] App Framework & System Apps -- [ ] DOOM Port -- [ ] UserSpace Drivers -- [ ] Userspace Networking -- [ ] Connect to Clion with SMB for files and GDB for debugging in userspace - [ ] Auto Updater & Image Builder (ISO Release) - [ ] Store +- [ ] Security of some sort - [ ] User Switching - [ ] Real Hardware Support -- [ ] Pretty GUI - [ ] Port NeoVim, Wakatime & Some hot reloader - [ ] Create port of my 2048 - [ ] Own LibC -- [ ] Compatibility Layer(s) See the [open issues](https://github.com/maxtyson123/MaxOS/issues) for a full list of proposed features (and known issues). diff --git a/docs/Notes.md b/docs/Notes.md index 9d29d110..ff994afc 100644 --- a/docs/Notes.md +++ b/docs/Notes.md @@ -42,6 +42,10 @@ These are my notes relative to various parts of the OS +NOTE TO SELF: FUNC AS VARIABLE: auto func_name = [&](uint32_t p) { + // Code +}; + # Hardware Communication Here are some notes on how the communication with hardware works, this is used for the keyboard and mouse communication and setting up other devices, e.g. GPU diff --git a/docs/Styles/.clang-format b/docs/Styles/.clang-format new file mode 100644 index 00000000..2d90114e --- /dev/null +++ b/docs/Styles/.clang-format @@ -0,0 +1,8 @@ +BasedOnStyle: LLVM +IndentWidth: 2 +ContinuationIndentWidth: 2 +BreakConstructorInitializers: AfterColon +ConstructorInitializerIndentWidth: 2 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowShortFunctionsOnASingleLine: None +ColumnLimit: 0 \ No newline at end of file diff --git a/docs/Syscalls.md b/docs/Syscalls.md index 1946fb2e..e07ad26c 100644 --- a/docs/Syscalls.md +++ b/docs/Syscalls.md @@ -14,17 +14,17 @@ This is helpful for updating syscall table (I don't know if that will even happe | rax | rdi | rsi | rdx | r10 | r8 | r9 | ## Syscall Table (v1) -| Number | Name | Description | Arg0 | Arg1 | Arg2 | Arg3 | Arg4 | Arg5 | Return | -|--------|-----------------------|---------------------------------|----------------------------------------|------------|-------------|------|------|------|------------------------------------------------| -| 0 | close_process | Exit a process | uint64_t pid (= 0 for current process) | int status | | | | | | -| 1 | klog | Log a message to the kernel log | char* msg | | | | | | | -| 2 | created_shared_memory | Create a shared memory region | size_t size | char* name | | | | | void* address (null if failed) | -| 3 | open_shared_memory | Open a shared memory region | char* name | | | | | | void* address (null if failed) | -| 4 | allocate_memory | Allocate memory | size_t size | | | | | | void* address (null if failed) | -| 5 | free_memory | Free memory | void* address | | | | | | | -| 6 | create_ipc_endpoint | Create an IPC endpoint | char* name | | | | | | void* message buffer address (null if failed) | -| 7 | send_ipc_message | Send an IPC message | char* name | void* data | size_t size | | | | | -| 8 | remove_ipc_endpoint | Remove an IPC endpoint | char* name | | | | | | | -| 9 | thread_yield | Yield the current thread | | | | | | | | -| 10 | thread_sleep | Put the current thread to sleep | uint64_t time (ms) | | | | | | | -| 11 | thread_exit | Exit the current thread | | | | | | | | \ No newline at end of file +| Number | Name | Description | Arg0 | Arg1 | Arg2 | Arg3 | Arg4 | Arg5 | Return | +|--------|-----------------|---------------------------------|----------------------------------------|--------------|--------------|--------------|------|------|---------------------------------------| +| 0 | close_process | Exit a process | uint64_t pid (= 0 for current process) | int status | | | | | | +| 1 | klog | Log a message to the kernel log | char* msg | | | | | | | +| 2 | allocate_memory | Allocate memory | size_t size | | | | | | void* address (null if failed) | +| 3 | free_memory | Free memory | void* address | | | | | | | +| 4 | resource_create | Create a resource | ResourceType type | char* name | size_t flags | | | | int success (1 = success, 0 = failed) | +| 5 | resource_open | Open a resource | ResourceType type | char* name | size_t flags | | | | uint64_t handle (0 if failed) | +| 6 | resource_close | Close a resource | uint64_t handle | size_t flags | | | | | | +| 7 | resource_write | Write to a resource | uint64_t handle | void* buffer | size_t size | size_t flags | | | size_t bytes written (0 if failed) | +| 8 | resource_read | Read from a resource | uint64_t handle | void* buffer | size_t size | size_t flags | | | size_t bytes read (0 if failed) | +| 9 | thread_yield | Yield the current thread | | | | | | | | +| 10 | thread_sleep | Put the current thread to sleep | uint64_t time (ms) | | | | | | | +| 11 | thread_exit | Exit the current thread | | | | | | | | \ No newline at end of file diff --git a/filesystem/boot/grub/grub.cfg b/filesystem/boot/grub/grub.cfg old mode 100644 new mode 100755 index dfe59552..1977c537 --- a/filesystem/boot/grub/grub.cfg +++ b/filesystem/boot/grub/grub.cfg @@ -7,7 +7,7 @@ menuentry "Max OS" { echo "Loading Kernel..." multiboot2 /boot/MaxOSk64 echo "Loading Test Elf..." - module2 /boot/test.elf Receiver - module2 /boot/test1.elf Sender + module2 /boot/test.elf File + module2 /boot/test1.elf Folder boot } \ No newline at end of file diff --git a/filesystem/os/.gitkeep b/filesystem/os/lib/.gitkeep old mode 100644 new mode 100755 similarity index 100% rename from filesystem/os/.gitkeep rename to filesystem/os/lib/.gitkeep diff --git a/filesystem/test/longfilename.extension b/filesystem/test/longfilename.extension new file mode 100755 index 00000000..0371c1f1 --- /dev/null +++ b/filesystem/test/longfilename.extension @@ -0,0 +1 @@ +test data to read \ No newline at end of file diff --git a/filesystem/user/.gitkeep b/filesystem/user/.gitkeep old mode 100644 new mode 100755 diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index cb05c1f0..7696a80d 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -13,25 +13,30 @@ SET_SOURCE_FILES_PROPERTIES(${ASM_SRCS} PROPERTIES LANGUAGE ASM) # Find all the cpp and s files in the src directory (recursive) FILE(GLOB_RECURSE KERNEL_SRCS src/*.cpp src/*.s) -# Create the kernel -ADD_EXECUTABLE(MaxOSk64 ${KERNEL_SRCS}) -TARGET_COMPILE_DEFINITIONS(MaxOSk64 PUBLIC MAXOS_KERNEL) - # Update the version before building +SET(VERSION_HEADER "${CMAKE_SOURCE_DIR}/kernel/include/common/version.h") +SET(VERSION_HEADER_TMP "${CMAKE_SOURCE_DIR}/kernel/include/common/version.h.tmp") +SET(POST_PROCESS_SCRIPT "${CMAKE_SOURCE_DIR}/toolchain/pre_process/run.sh") ADD_CUSTOM_COMMAND( - COMMENT "post_processing kernel" - TARGET MaxOSk64 - PRE_BUILD - COMMAND ${CMAKE_SOURCE_DIR}/toolchain/post_process/run.sh - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/kernel/include/common/version.h.tmp ${CMAKE_SOURCE_DIR}/kernel/include/common/version.h - COMMAND ${CMAKE_COMMAND} -E remove ${CMAKE_SOURCE_DIR}/kernel/include/common/version.h.tmp - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT "${VERSION_HEADER}" + COMMAND "${POST_PROCESS_SCRIPT}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${VERSION_HEADER_TMP}" "${VERSION_HEADER}" + COMMAND ${CMAKE_COMMAND} -E remove "${VERSION_HEADER_TMP}" + DEPENDS ${KERNEL_SRCS} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + COMMENT "Regenerating version.h because kernel sources changed" ) +ADD_CUSTOM_TARGET(VersionScript DEPENDS "${VERSION_HEADER}") + +# Create the kernel +ADD_EXECUTABLE(MaxOSk64 ${KERNEL_SRCS} ${VERSION_HEADER}) +ADD_DEPENDENCIES(MaxOSk64 VersionScript) +TARGET_COMPILE_DEFINITIONS(MaxOSk64 PUBLIC MAXOS_KERNEL) # Linker SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/kernel/src/linker.ld) SET_TARGET_PROPERTIES(MaxOSk64 PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) -TARGET_LINK_LIBRARIES(MaxOSk64 gcc) +TARGET_LINK_LIBRARIES(MaxOSk64 PRIVATE gcc syscore_static) TARGET_LINK_OPTIONS(MaxOSk64 PRIVATE -T ${LINKER_SCRIPT} -nostdlib -n) # Set the include directories diff --git a/kernel/include/common/buffer.h b/kernel/include/common/buffer.h new file mode 100644 index 00000000..4e825e08 --- /dev/null +++ b/kernel/include/common/buffer.h @@ -0,0 +1,73 @@ +// +// Created by 98max on 30/07/2025. +// + +#ifndef MAXOS_COMMON_BUFFER_H +#define MAXOS_COMMON_BUFFER_H + +#include +#include +#include +#include + +namespace MaxOS{ + + namespace common{ + + /** + * @class Buffer + * @brief Wrapper class for a region of bytes in memor in an attempt to add some memory safety. Automatically + * allocates the size specified and frees it once done, adds boundary to I/O. + */ + class Buffer{ + + private: + uint8_t* m_bytes = nullptr; + size_t m_capacity; + bool m_dont_delete = false; + + size_t m_offset = 0; + + public: + Buffer(size_t size, bool update_offset = true); + Buffer(void* source, size_t size, bool update_offset = true); + ~Buffer(); + + [[nodiscard]] uint8_t* raw() const; + + void clear(); + void full(uint8_t byte, size_t offset = 0, size_t amount = 0); + + bool update_offset = true; + void set_offset(size_t offset); + + [[nodiscard]] size_t capacity() const; + void resize(size_t size); + + void write(uint8_t byte); + void write(size_t offset, uint8_t byte); + uint8_t read(); + uint8_t read(size_t offset); + + void copy_from(const Buffer* buffer); + void copy_from(const Buffer* buffer, size_t length); + void copy_from(const Buffer* buffer, size_t length, size_t offset); + void copy_from(const Buffer* buffer, size_t length, size_t offset, size_t offset_other); + void copy_from(const void* source, size_t length); + void copy_from(const void* source, size_t length, size_t offset); + + void copy_to(Buffer* buffer); + void copy_to(Buffer* buffer, size_t length); + void copy_to(Buffer* buffer, size_t length, size_t offset); + void copy_to(Buffer* buffer, size_t length, size_t offset, size_t offset_other); + void copy_to(void* destination, size_t length); + void copy_to(void* destination, size_t length, size_t offset); + }; + + typedef Buffer buffer_t; + + } + +} + +#endif //MAXOS_COMMON_BUFFER_H diff --git a/kernel/include/common/eventHandler.h b/kernel/include/common/eventHandler.h index 4a82b0f0..57b3cb8b 100644 --- a/kernel/include/common/eventHandler.h +++ b/kernel/include/common/eventHandler.h @@ -8,6 +8,11 @@ #include #include + + +// TODO: - Event doesnt need to be a class should be a struct +// - With moving to micro kernel this should be moved to a external lib as not needed in the kernel + namespace MaxOS{ namespace common{ diff --git a/kernel/include/common/logger.h b/kernel/include/common/logger.h index d9f822dd..907315b7 100644 --- a/kernel/include/common/logger.h +++ b/kernel/include/common/logger.h @@ -7,7 +7,6 @@ #include #include -#include @@ -40,7 +39,7 @@ bool m_log_writers_enabled[m_max_log_writers] = {false, false, false, false, false}; // Progress bar - static const uint8_t s_progress_total = 25; + static inline uint8_t s_progress_total = 100; uint8_t m_progress_current = 0; static inline Logger* s_active_logger = nullptr; @@ -58,7 +57,6 @@ void set_log_level(LogLevel log_level); void write_char(char c) final; - void lineFeed() final; void printf(const char* format, ...); static void ASSERT(bool condition, const char* message, ...); @@ -77,5 +75,17 @@ Logger& operator << (LogLevel log_level); }; + /** + * @brief If the specified condition is not met then the kernel will crash with the specified message. + * + * This macro wraps Logger::ASSERT and supports printf-style formatting with variadic arguments. + * + * @param condition The condition to check. + * @param format The format string (like printf). + * @param ... Additional arguments to format. + * + * @see Logger::ASSERT + */ #define ASSERT(condition, format, ...) Logger::ASSERT(condition, format, ##__VA_ARGS__) + #endif // MAXOS_COMMON_LOGGER_H diff --git a/kernel/include/common/map.h b/kernel/include/common/map.h index f309e939..efe0aae1 100644 --- a/kernel/include/common/map.h +++ b/kernel/include/common/map.h @@ -42,27 +42,39 @@ namespace MaxOS{ Vector> m_elements; public: - typedef typename Vector >::iterator iterator; + typedef typename Vector>::iterator iterator; Map(); ~Map(); - Value& operator[](Key); + Value& operator[](Key); + + bool empty(); + int size(); + iterator begin(); iterator end(); iterator find(Key); - bool empty(); - void clear(); - void insert(Key, Value); - void erase(Key); + iterator push_back(Key, Value); + Pair pop_back(); + + iterator push_front(Key, Value); + Pair pop_front(); + + void insert(Key, Value); + + void erase(Key); + void erase(iterator position); + void clear(); void iterate(MapIterationHandler* handler); void iterate(void callback(Key&, Value&)); }; - /// ______________ TEMPLATE IMPLEMENTATION ______________ + + /// ______________ TEMPLATE IMPLEMENTATION ______________ template MapIterationHandler::MapIterationHandler() = default; template MapIterationHandler::~MapIterationHandler() = default; @@ -137,21 +149,33 @@ namespace MaxOS{ */ template typename Map::iterator Map::find(Key element) { - // Loop through the elements - for (iterator it = begin(); it != end(); it++) { - - // If the key of the current element is equal to the key we are looking for - if (it -> first == element) { - // Return the iterator + // Search for the element + for (iterator it = begin(); it != end(); it++) + if (it -> first == element) return it; - } - } - // If it is not found, return the end iterator + // Item not found return end(); } + template Map::iterator Map::push_back(Key key, Value value) { + return m_elements.push_back(Pair(key, value)); + } + + template Pair Map::pop_back() { + return m_elements.pop_back(); + } + + template Map::iterator Map::push_front(Key key, Value value) { + return m_elements.push_front({key, value}); + } + + template Pair Map::pop_front() { + return m_elements.pop_front(); + } + + /** * @brief Returns whether the map is empty * @@ -163,6 +187,18 @@ namespace MaxOS{ return m_elements.empty(); } + /** + * @brief The number of elements in the map + * + * @return The number of elements in the Map + */ + template int Map::size() + { + // Return the size of the vector + return m_elements.size(); + + } + /** * @brief Removes all elements from the map * @@ -214,6 +250,11 @@ namespace MaxOS{ } + template void Map::erase(Map::iterator position) { + m_elements.erase(position); + + } + /** * @brief Iterates through the map and calls the handler * diff --git a/kernel/include/common/string.h b/kernel/include/common/string.h index f898ab53..20da7721 100644 --- a/kernel/include/common/string.h +++ b/kernel/include/common/string.h @@ -7,6 +7,8 @@ #include +#include + namespace MaxOS { /** @@ -16,14 +18,21 @@ namespace MaxOS { class String { private: char* m_string = nullptr; - int m_length = 0; + int m_length = 0; // Does not include the null terminator + + const static uint8_t s_small_storage = 0x99; + char m_small_string[s_small_storage]; + bool m_using_small = true; [[nodiscard]] static int lex_value(String const &other) ; + void allocate_self(); public: String(); + explicit String(char c); String(char const* string); + String(uint8_t const* string, int length); String(String const &other); String(int value); String(uint64_t value); @@ -34,7 +43,14 @@ namespace MaxOS { [[nodiscard]] int length(bool count_ansi = true) const; char* c_str(); - [[nodiscard]] const char* c_str() const; + const char* c_str() const; + + bool starts_with(String const &other); + String substring(int start, int length) const; + + common::Vector split(String const &delimiter) const; + String strip(char strip_char = ' ') const; + [[nodiscard]] String center(int width, char fill = ' ') const; diff --git a/kernel/include/common/vector.h b/kernel/include/common/vector.h index 16a6a042..077ccd18 100644 --- a/kernel/include/common/vector.h +++ b/kernel/include/common/vector.h @@ -47,22 +47,26 @@ namespace MaxOS{ Vector(); Vector(int Size, Type element); + Vector(const Vector& other); + Vector(Vector&& other); ~Vector(); Type& operator[](uint32_t index) const; + Vector& operator=(const Vector& other); + Vector& operator=(Vector&& other); [[nodiscard]] bool empty() const; - [[nodiscard]] uint32_t size() const; + [[nodiscard]] uint32_t size() const; iterator begin() const; iterator end() const; iterator find(Type) const; iterator push_back(Type); - void pop_back(); + Type pop_back(); iterator push_front(Type); - void pop_front(); + Type pop_front(); void erase(Type); void erase(iterator position); @@ -72,7 +76,7 @@ namespace MaxOS{ void Iterate(void callback(Type&)); }; - ///______________________________________Implementation__________________________________________________ + ///______________________________________Implementation__________________________________________________ /** * @brief Constructor for Vector * @@ -96,12 +100,48 @@ namespace MaxOS{ // Allocate space for the array m_elements = new Type[size]; + m_capacity = size > 0 ? size : 1; + m_size = 0; // Push all the elements to the Vector for (int i = 0; i < size; ++i) push_back(element); } + /** + * @brief Copy constructor for Vector + * + * @tparam Type The type of data to be stored + * @param other The vector to copy from + */ + template Vector::Vector(const Vector& other) + : m_size(other.m_size), + m_capacity(other.m_capacity) + { + // Copy each element into a new array + m_elements = new Type[m_capacity]; + for (uint32_t i = 0; i < m_size; ++i) + m_elements[i] = other.m_elements[i]; + } + + /** + * @brief Move constructor for Vector + * + * @tparam Type The type of data to be stored + * @param other The vector to copy from + */ + template Vector::Vector(Vector &&other) + : m_elements(other.m_elements), + m_size(other.m_size), + m_capacity(other.m_capacity) + { + + // Clear the other Vector + other.m_elements = nullptr; + other.m_size = 0; + other.m_capacity = 0; + + } template Vector::~Vector() { @@ -117,6 +157,7 @@ namespace MaxOS{ */ template void Vector::increase_size() { + // Allocate more space for the array Type* new_elements = new Type[m_capacity * 2]; @@ -145,7 +186,7 @@ namespace MaxOS{ template Type &Vector::operator[](uint32_t index) const{ // If the index is in the Vector - if (index <= m_size) + if (index < m_size) return m_elements[index]; // Return the last element of the Vector @@ -153,6 +194,59 @@ namespace MaxOS{ } + /** + * @brief Assignment by copy, data is copied into a new buffer stored in this vector + * + * @tparam Type Type of the Vector + * @param other The vector to copy from + * @return This vector, with the copied elements + */ + template Vector& Vector::operator=(const Vector& other) { + + // Setting to itself? + if (this == &other) + return *this; + + // Create a new buffer to store the elements + delete[] m_elements; + m_elements = new Type[other.m_capacity]; + + // Copy data + m_size = other.m_size; + m_capacity = other.m_capacity; + for (uint32_t i = 0; i < m_size; ++i) + m_elements[i] = other.m_elements[i]; + + return *this; + } + + /** + * @brief Assignment by move, data is moved into the buffer stored in this vector and the other vector is cleared + * + * @tparam Type Type of the Vector + * @param other The vector to copy from + * @return This vector, with the copied elements + */ + template Vector& Vector::operator=(Vector&& other) noexcept { + + // Moving to itself? + if (this == &other) + return *this; + + // Move into this vector + delete[] m_elements; + m_elements = other.m_elements; + m_size = other.m_size; + m_capacity = other.m_capacity; + + // Remove from other vector + other.m_elements = nullptr; + other.m_size = 0; + other.m_capacity = 0; + + return *this; + } + /** * @brief Returns the number of elements in the Vector * @@ -164,10 +258,10 @@ namespace MaxOS{ } /** - * @brief Returns the m_first_memory_chunk element of the Vector + * @brief Returns the first element of the Vector * * @tparam Type Type of the Vector - * @return The m_first_memory_chunk element of the Vector + * @return The first element of the Vector */ template typename Vector::iterator Vector::begin() const{ return &m_elements[0]; @@ -234,11 +328,13 @@ namespace MaxOS{ * @brief Removes the last element from the Vector * @tparam Type Type of the Vector */ - template void Vector::pop_back() { + template Type Vector::pop_back() { // Remove the last element from the Vector if (m_size > 0) --m_size; + + return m_elements[m_size]; } /** @@ -251,9 +347,8 @@ namespace MaxOS{ template typename Vector::iterator Vector::push_front(Type element) { // Check if we need to allocate more space for the array - if(m_size == m_capacity){ + if(m_size == m_capacity) increase_size(); - } // Move all elements one index to the right for (iterator i = end(); i > begin(); --i) @@ -271,19 +366,25 @@ namespace MaxOS{ * @brief Removes the m_first_memory_chunk element from the Vector * * @tparam Type Type of the Vector + * @return The element that was removed, or a default constructed element if the Vector is empty */ - template void Vector::pop_front() { + template Type Vector::pop_front() { - // Make sure the Vector is not empty - if (m_size == 0) - return; + // Make sure the Vector is not empty + if (m_size == 0) + return Type(); - // Move all elements one index to the left - for (iterator i = begin(); i != end(); ++i) - *i = *(i + 1); + // Store the element to return - // Decrease the size of the Vector - --m_size; + Type element = m_elements[0]; + + // Move all elements one index to the left + for (uint32_t i = 0; i < m_size - 1; ++i) + m_elements[i] = m_elements[i + 1]; + + // Decrease the size of the Vector + --m_size; + return element; } /** diff --git a/kernel/include/drivers/clock/clock.h b/kernel/include/drivers/clock/clock.h index 6a0e8653..7785f01c 100644 --- a/kernel/include/drivers/clock/clock.h +++ b/kernel/include/drivers/clock/clock.h @@ -151,11 +151,13 @@ namespace MaxOS { uint8_t read_hardware_clock(uint8_t address); [[nodiscard]] uint8_t binary_representation(uint8_t number) const; + inline static Clock* s_active_clock = nullptr; + public: Clock(hardwarecommunication::AdvancedProgrammableInterruptController* apic, uint16_t time_between_events = 10); ~Clock(); - inline static uint64_t s_clock_accuracy = 1; + uint64_t clock_accuracy = 1; void activate() override; void delay(uint32_t milliseconds) const; @@ -165,6 +167,8 @@ namespace MaxOS { string vendor_name() final; string device_name() final; + + static Clock* active_clock(); }; } diff --git a/kernel/include/drivers/disk/ata.h b/kernel/include/drivers/disk/ata.h index 521bdc5a..4eff54e4 100644 --- a/kernel/include/drivers/disk/ata.h +++ b/kernel/include/drivers/disk/ata.h @@ -2,12 +2,13 @@ // Created by 98max on 24/10/2022. // -#ifndef MAXOS_DRIVERS_ATA_H -#define MAXOS_DRIVERS_ATA_H +#ifndef MAXOS_DRIVERS_DISK_ATA_H +#define MAXOS_DRIVERS_DISK_ATA_H +#include #include #include -#include +#include #include @@ -21,7 +22,7 @@ namespace MaxOS{ * @class AdvancedTechnologyAttachment * @brief Driver for the ATA controller, handles the reading and writing of data to the hard drive */ - class AdvancedTechnologyAttachment : public Driver { + class AdvancedTechnologyAttachment : public Disk { protected: hardwarecommunication::Port16Bit m_data_port; @@ -36,17 +37,14 @@ namespace MaxOS{ bool m_is_master; uint16_t m_bytes_per_sector { 512 }; - common::OutputStream* ata_message_stream; public: - AdvancedTechnologyAttachment(uint16_t port_base, bool master, common::OutputStream* output_stream); + AdvancedTechnologyAttachment(uint16_t port_base, bool master); ~AdvancedTechnologyAttachment(); - void identify(); - void read_28(uint32_t sector, uint8_t* data, int count); - void write_28(uint32_t sector, const uint8_t* data, int count); - void flush(); - - void activate() final; + bool identify(); + void read(uint32_t sector, common::buffer_t* data_buffer, size_t amount) final; + void write(uint32_t sector, const common::buffer_t* data, size_t count) final; + void flush() final; string device_name() final; string vendor_name() final; @@ -55,4 +53,4 @@ namespace MaxOS{ } } -#endif //MAXOS_DRIVERS_ATA_H +#endif //MAXOS_DRIVERS_DISK_ATA_H diff --git a/kernel/include/drivers/disk/disk.h b/kernel/include/drivers/disk/disk.h new file mode 100644 index 00000000..f51626e6 --- /dev/null +++ b/kernel/include/drivers/disk/disk.h @@ -0,0 +1,47 @@ +// +// Created by 98max on 18/04/2025. +// + +#ifndef MAXOS_DRIVERS_DISK_H +#define MAXOS_DRIVERS_DISK_H + +#include +#include +#include +#include + + +namespace MaxOS{ + + namespace drivers{ + + namespace disk{ + + /** + * @class Disk + * @brief Generic Disk, handles the reading and writing of data to the hard drive + */ + class Disk : public Driver { + + public: + Disk(); + ~Disk(); + + void read(uint32_t sector, common::buffer_t* data_buffer); + virtual void read(uint32_t sector, common::buffer_t* data_buffer, size_t amount); + + void write(uint32_t sector, const common::buffer_t* data); + virtual void write(uint32_t sector, const common::buffer_t* data, size_t count); + + virtual void flush(); + + void activate() override; + + string device_name() override; + string vendor_name() override; + }; + } + } +} + +#endif //MAXOS_DRIVERS_DISK_H diff --git a/kernel/include/drivers/disk/ide.h b/kernel/include/drivers/disk/ide.h new file mode 100644 index 00000000..2bcbd693 --- /dev/null +++ b/kernel/include/drivers/disk/ide.h @@ -0,0 +1,47 @@ +// +// Created by Max Tyson on 18/04/2025. +// + +#ifndef MAXOS_DRIVERS_IDE_H +#define MAXOS_DRIVERS_IDE_H + +#include +#include +#include +#include +#include + + +namespace MaxOS{ + + namespace drivers{ + + namespace disk{ + + /** + * * @class IntegratedDriveElectronicsController + * @brief Driver for the IDE controller, handles the creation and management of the IDE devices + */ + class IntegratedDriveElectronicsController : public Driver{ + + + common::Map devices; + + public: + IntegratedDriveElectronicsController(hardwarecommunication::PeripheralComponentInterconnectDeviceDescriptor* device_descriptor); + ~IntegratedDriveElectronicsController(); + + void initialise() final; + void activate() final; + + string vendor_name() final; + string device_name() final; + + + }; + } + } +} + + +#endif //MAXOS_DRIVERS_IDE_H diff --git a/kernel/include/drivers/driver.h b/kernel/include/drivers/driver.h index bd77ce12..9fb64b02 100644 --- a/kernel/include/drivers/driver.h +++ b/kernel/include/drivers/driver.h @@ -22,19 +22,11 @@ namespace MaxOS * @brief base class for all drivers, handles the activation, deactivation, initialisation and reset of the driver as well as error messages and identifying the device */ class Driver { - protected: public: - common::OutputStream* m_driver_message_stream; - - Driver(common::OutputStream* driverMessageStream = nullptr); + Driver(); ~Driver(); - void error_message(const string& message) const; - void error_message(char char_to_write) const; - void error_message(int int_to_write) const; - void error_message(uint32_t hex_to_write) const; - virtual void activate(); virtual void deactivate(); virtual void initialise(); diff --git a/kernel/include/drivers/ethernet/amd_am79c973.h b/kernel/include/drivers/ethernet/amd_am79c973.h index 442b1e40..8bdb41a3 100644 --- a/kernel/include/drivers/ethernet/amd_am79c973.h +++ b/kernel/include/drivers/ethernet/amd_am79c973.h @@ -8,7 +8,6 @@ #include #include #include -#include #include #include @@ -86,7 +85,7 @@ namespace MaxOS{ void FetchDataSent(); //Fetches the data from the buffer public: - AMD_AM79C973(hardwarecommunication::PeripheralComponentInterconnectDeviceDescriptor* deviceDescriptor, common::OutputStream* amdNetMessageStream = nullptr); + AMD_AM79C973(hardwarecommunication::PeripheralComponentInterconnectDeviceDescriptor* device_descriptor); ~AMD_AM79C973(); //Override driver default methods diff --git a/kernel/include/drivers/ethernet/ethernet.h b/kernel/include/drivers/ethernet/ethernet.h index 36b9363d..e300eda0 100644 --- a/kernel/include/drivers/ethernet/ethernet.h +++ b/kernel/include/drivers/ethernet/ethernet.h @@ -90,7 +90,7 @@ namespace MaxOS{ void FireDataSent(uint8_t* buffer, uint32_t size); public: - EthernetDriver(common::OutputStream* ethernetMessageStream); + EthernetDriver(); ~EthernetDriver(); static MediaAccessControlAddress CreateMediaAccessControlAddress(uint8_t digit1, uint8_t digit2, uint8_t digit3, uint8_t digit4, uint8_t digit5, uint8_t digit6); diff --git a/kernel/include/drivers/peripherals/keyboard.h b/kernel/include/drivers/peripherals/keyboard.h index 0e6f95e9..db0712b9 100644 --- a/kernel/include/drivers/peripherals/keyboard.h +++ b/kernel/include/drivers/peripherals/keyboard.h @@ -22,6 +22,12 @@ namespace MaxOS namespace peripherals{ + enum class ScanCodeType : int{ + REGULAR, + EXTENDED, + EXTENDED_BUFFER + }; + enum class KeyCode : uint16_t{ // Alphabet diff --git a/kernel/include/drivers/peripherals/mouse.h b/kernel/include/drivers/peripherals/mouse.h index f07daa4b..b0abd178 100644 --- a/kernel/include/drivers/peripherals/mouse.h +++ b/kernel/include/drivers/peripherals/mouse.h @@ -86,9 +86,9 @@ namespace MaxOS { void handle_interrupt() final; - uint8_t buffer[3] = {}; - uint8_t offset = 0; - uint8_t buttons = 0; + uint8_t m_buffer[3] = {}; + uint8_t m_offset = 0; + uint8_t m_buttons = 0; public: MouseDriver(); diff --git a/kernel/include/drivers/video/vesa.h b/kernel/include/drivers/video/vesa.h index 7b5ac4b4..3fb56234 100644 --- a/kernel/include/drivers/video/vesa.h +++ b/kernel/include/drivers/video/vesa.h @@ -25,9 +25,6 @@ namespace MaxOS { */ class VideoElectronicsStandardsAssociation : public VideoDriver { - private: - static bool init(); - protected: bool internal_set_mode(uint32_t width, uint32_t height, uint32_t) final; diff --git a/kernel/include/filesystem/fat32.h b/kernel/include/filesystem/fat32.h deleted file mode 100644 index 41fc8d01..00000000 --- a/kernel/include/filesystem/fat32.h +++ /dev/null @@ -1,88 +0,0 @@ -// -// Created by 98max on 1/01/2023. -// - -#ifndef MAXOS_FILESYSTEM_FAT32_H -#define MAXOS_FILESYSTEM_FAT32_H - -#include "drivers/disk/ata.h" -#include -#include -#include -#include -#include - -namespace MaxOS{ - - namespace filesystem{ - - /** - * @struct BiosParameterBlock - * @brief Stores information about the FAT32 filesystem - */ - struct BiosParameterBlock32{ - - uint8_t jump[3]; - uint8_t OEM_name[8]; - uint16_t bytes_per_sector; - uint8_t sectors_per_cluster; - uint16_t reserved_sectors; - uint8_t table_copies; - uint16_t root_entries; - uint16_t total_sectors_16; - uint8_t media_type; - uint16_t fat_sector_count; - uint16_t sectors_per_track; - uint16_t head_count; - uint32_t hidden_sectors; - uint32_t total_sectors_32; - - uint32_t table_size_32; - uint16_t extended_flags; - uint16_t fat_version; - uint32_t root_cluster; - uint16_t fat_info; - uint16_t backup_sector; - uint8_t reserved0[12]; - uint8_t drive_number; - uint8_t reserved_1; - uint8_t boot_signature; - uint32_t uint_32; - uint8_t volume_label[11]; - uint8_t file_system_type[8]; - - } __attribute__((packed)); - - /** - * @struct DirectoryEntry - * @brief Stores information about a file or directory - */ - struct DirectoryEntry{ - - uint8_t name[8]; - uint8_t extension[3]; - uint8_t attributes; - uint8_t reserved; - - uint8_t creation_time_tenth; - uint16_t creation_time; - uint16_t creation_date; - uint16_t last_access_date; - - uint16_t first_cluster_high; - - uint16_t last_write_time; - uint16_t last_write_date; - - uint16_t first_cluster_low; - - uint32_t size; - - } __attribute__((packed)); - - - // TODO: Redo FAT32 - } -} - -#endif //MAXOS_FILESYSTEM_FAT32_H diff --git a/kernel/include/filesystem/filesystem.h b/kernel/include/filesystem/filesystem.h index f6009cdb..67008069 100644 --- a/kernel/include/filesystem/filesystem.h +++ b/kernel/include/filesystem/filesystem.h @@ -6,21 +6,107 @@ #define MAXOS_FILESYSTEM_FILESYSTEM_H #include +#include #include +#include +#include +#include +#include namespace MaxOS{ namespace filesystem{ + // Easier to read + typedef uint32_t lba_t; + typedef syscore::filesystem::SeekType SeekType; - enum class SeekType{ - SET, - CUR, - END + /** + * @class File + * @brief Handles file operations and information + */ + class File + { + + protected: + uint32_t m_offset; + string m_name; + size_t m_size; + + public: + File(); + virtual ~File(); + + virtual void write(const common::buffer_t* data, size_t size); + virtual void read(common::buffer_t* data, size_t size); + virtual void flush(); + + void seek(SeekType seek_type, size_t offset); + uint32_t position(); + + size_t size(); + string name(); }; - // TODO: Redo this class + /** + * @class Directory + * @brief Handles a group of files (directory) + */ + class Directory + { + protected: + common::Vector m_files; + common::Vector m_subdirectories; + + string m_name; + + public: + Directory(); + virtual ~Directory(); + + virtual void read_from_disk(); + + common::Vector files(); + common::Vector subdirectories(); + + File* open_file(const string& name); + Directory* open_subdirectory(const string& name); + + virtual File* create_file(const string& name); + virtual void remove_file(const string& name); + + void rename_file(File* file, const string& new_name); + virtual void rename_file(const string& old_name, const string& new_name); + + void rename_subdirectory(Directory* directory, const string& new_name); + virtual void rename_subdirectory(const string& old_name, const string& new_name); + + virtual Directory* create_subdirectory(const string& name); + virtual void remove_subdirectory(const string& name); + + string name(); + size_t size(); + }; + + /** + * @class FileSystem + * @brief Handles the disk operations and file system information + */ + class FileSystem + { + protected: + Directory* m_root_directory; + + public: + FileSystem(); + virtual ~FileSystem(); + + Directory* root_directory(); + Directory* get_directory(const string& path); + + bool exists(const string& path); + }; } diff --git a/kernel/include/filesystem/format/ext2.h b/kernel/include/filesystem/format/ext2.h new file mode 100644 index 00000000..79df5e54 --- /dev/null +++ b/kernel/include/filesystem/format/ext2.h @@ -0,0 +1,382 @@ +// +// Created by 98max on 17/07/2025. +// + +#ifndef MAXOS_FILESYSTEM_EXT2_H +#define MAXOS_FILESYSTEM_EXT2_H + +#include +#include +#include +#include +#include +#include + +namespace MaxOS { + namespace filesystem { + namespace format{ + namespace ext2{ + + + typedef struct SuperBlock{ + uint32_t total_inodes; + uint32_t total_blocks; + uint32_t reserved_blocks; + uint32_t unallocated_blocks; + uint32_t unallocated_inodes; + uint32_t starting_block; + uint32_t block_size; + uint32_t fragment_size; + uint32_t blocks_per_group; + uint32_t fragments_per_group; + uint32_t inodes_per_group; + uint32_t last_mount_time; + uint32_t late_write_time; + uint16_t mounts_since_check; + uint16_t mounts_until_check; + uint16_t signature; + uint16_t state; + uint16_t error_operation; + uint16_t version_minor; + uint32_t last_check_time; + uint32_t time_until_check; + uint32_t os_id; + uint32_t version_major; + uint16_t reserved_user; + uint16_t reserved_group; + + // Extended Fields (version >= 1) + uint32_t first_inode; + uint16_t inode_size; + uint16_t superblock_group; + uint32_t optional_features; + uint32_t required_features; + uint16_t read_only_features; + uint8_t filesystem_id[16]; + uint8_t volume_name[16]; + uint8_t last_mount_path[64]; + uint32_t compression; + uint8_t file_preallocation_blocks; + uint8_t directory_preallocation_blocks; + uint16_t unused; + uint8_t journal_id[16]; + uint32_t journal_inode; + uint32_t journal_device; + uint32_t orphan_inodes_start; + uint8_t free[276]; + + } __attribute__((packed)) superblock_t; + + + enum class FileSystemState{ + CLEAN = 1, + ERROR = 2, + }; + + enum class ErrorOperation{ + IGNORE = 1, + REMOUNT = 2, + PANIC = 3, + }; + + enum class CreatorOS{ + LINUX, + GNU_HURD, + MASIX, + FREE_BSD, + OTHER_LITES, + }; + + enum class OptionalFeatures{ + PREALLOCATE_DIRECTORY = 0x1, + AFS_SERVER_INODES = 0x2, + JOURNAL_ENABLED = 0x4, + ATTRIBUTES_EXTENDED = 0x8, + RESIZEABLE = 0x10, + HASH_INDEXING = 0x20, + }; + + enum class RequiredFeatures { + COMPRESSION = 0x1, + DIRECTORY_HAS_TYPE = 0x2, + MUST_REPLAY_JOURNAL = 0x4, + JOURNAL_DEVICE = 0x8, + }; + + enum class ReadOnlyFeatures { + SPARSE_SUPER_BLOCKS = 0x1, + FILES_64_BIT = 0x2, + BINARY_TREE_DIRECTORIES = 0x4, + }; + + typedef struct BlockGroupDescriptor{ + uint32_t block_usage_bitmap; + uint32_t block_inode_bitmap; + uint32_t inode_table_address; + uint16_t free_blocks; + uint16_t free_inodes; + uint16_t directory_count; + uint8_t free[14]; + + } __attribute__((packed)) block_group_descriptor_t; + + typedef struct Inode{ + union { + uint16_t type_permissions; + struct { + uint16_t permissions : 12; + uint16_t type : 4; + }; + }; + uint16_t user_id; + uint32_t size_lower; + uint32_t last_access_time; + uint32_t creation_time; + uint32_t last_modification_time; + uint32_t deletion_time; + uint16_t group_id; + uint16_t hard_links; + uint32_t sectors_used; + uint32_t flags; + uint32_t os_1; + uint32_t block_pointers[12]; + uint32_t l1_indirect; + uint32_t l2_indirect; + uint32_t l3_indirect; + uint32_t generation; + uint32_t extended_attribute; // File ACL + uint32_t size_upper; // Dir ACL + uint32_t os_2[3]; + } __attribute__((packed)) inode_t; + + enum class InodeType { + UNKNOWN, + FIFO = 0x1000, + CHARACTER_DEVICE = 0x2000, + DIRECTORY = 0x4000, + BLOCK_DEVICE = 0x6000, + FILE = 0x8000, + SYMBOLIC_LINK = 0xA000, + SOCKET = 0xC000, + }; + + enum class InodePermissions{ + OTHER_EXECUTE = 0x1, + OTHER_WRITE = 0x2, + OTHER_READ = 0x4, + GROUP_EXECUTE = 0x8, + GROUP_WRITE = 0x10, + GROUP_READ = 0x20, + USER_EXECUTE = 0x40, + USER_WRITE = 0x80, + USER_READ = 0x100, + STICKY = 0x200, + GROUP_ID = 0x400, + USER_ID = 0x800, + }; + + enum class InodePermissionsDefaults{ + FILE = 0x1A4, + DIRECTORY = 0x1ED, + + }; + + enum class InodeFlags{ + SECURE_DELETE = 0x1, // Zero out data on deletion + KEEP_DATA = 0x2, + FILE_COMPRESSION = 0x4, + SYNC_UPDATES = 0x8, + FILE_IMMUTABLE = 0x10, + APPEND_ONLY = 0x20, + DONT_DUMP = 0x40, + NO_LAST_ACCESS = 0x80, + HASH_INDEXED = 0x10000, + AFS_DIRECTORY = 0x20000, + JOURNAL_FILE_DATA = 0x40000, + }; + + // TODO: Also HURD, MASIX + typedef struct InodeOS2Linux{ + uint8_t fragment; + uint8_t fragment_size; + uint16_t high_type_permissions; + uint16_t high_user_id; + uint16_t high_group_id; + uint32_t author_id; //0xFFFFFFFF = use user_id + } __attribute__((packed)) linux_os_2_t; + + typedef struct DirectoryEntry{ + uint32_t inode; + uint16_t size; + uint8_t name_length; + uint8_t type; + // Rest are name chars + } __attribute__((packed)) directory_entry_t; + + enum class EntryType { + UNKNOWN, + FILE, + DIRECTORY, + CHARACTER_DEVICE, + BLOCK_DEVICE, + FIFO, + SOCKET, + SYMBOLIC_LINK + }; + + class Ext2Volume { + + private: + common::Vector allocate_group_blocks(uint32_t block_group, uint32_t amount); + void free_group_blocks(uint32_t block_group, uint32_t amount, uint32_t start); + + + void write_back_block_groups() const; + void write_back_superblock(); + + public: + Ext2Volume(drivers::disk::Disk* disk, lba_t partition_offset); + ~Ext2Volume(); + + drivers::disk::Disk* disk; + lba_t partition_offset; + + superblock_t superblock; + block_group_descriptor_t** block_groups; + + size_t block_size; + uint32_t block_group_descriptor_table_block; + uint32_t block_group_descriptor_table_size; + uint32_t total_block_groups; + size_t pointers_per_block; + uint32_t inodes_per_block; + uint32_t sectors_per_block; + + uint32_t blocks_per_inode_table; + uint32_t sectors_per_inode_table; + + common::Spinlock ext2_lock; + + void write_block(uint32_t block_num, common::buffer_t* buffer); + void write_inode(uint32_t inode_num, inode_t* inode); + + [[nodiscard]] uint32_t create_inode(bool is_directory); + void free_inode(uint32_t inode); + + void read_block(uint32_t block_num, common::buffer_t* buffer) const; + [[nodiscard]] inode_t read_inode(uint32_t inode_num) const; + + [[nodiscard]] uint32_t allocate_block(); + [[nodiscard]] common::Vector allocate_blocks(uint32_t amount); + [[nodiscard]] uint32_t bytes_to_blocks(size_t bytes) const; + + void free_blocks(const common::Vector& blocks); + + + // TODO: free blocks + }; + + /** + * @class InodeHandler + * @brief Simplfies the management of an inode & its blocks + */ + class InodeHandler { + + private: + Ext2Volume* m_volume = nullptr; + + void parse_indirect(uint32_t level, uint32_t block, common::buffer_t* buffer); + void write_indirect(uint32_t level, uint32_t& block, size_t& index); + void store_blocks(const common::Vector& blocks); + + public: + InodeHandler(Ext2Volume* volume, uint32_t inode); + ~InodeHandler(); + + uint32_t inode_number; + inode_t inode; + common::Vector block_cache; + + [[nodiscard]] size_t size() const; + void set_size(size_t size); + size_t grow(size_t amount, bool flush = true); + + void save(); + + void free(); + + }; + + /** + * @class Ext2File + * @brief Handles the file operations on the ext2 filesystem + */ + class Ext2File final : public File { + private: + Ext2Volume* m_volume; + InodeHandler m_inode; + + public: + Ext2File(Ext2Volume* volume, uint32_t inode, const string& name); + ~Ext2File() final; + + void write(const common::buffer_t* data, size_t amount) final; + void read(common::buffer_t* data, size_t amount) final; + void flush() final; + }; + + /** + * @class Ext2Directory + * @brief Handles the directory operations on the ext2 filesystem + */ + class Ext2Directory final : public Directory { + + private: + Ext2Volume* m_volume; + InodeHandler m_inode; + + common::Vector m_entries; + common::Vector m_entry_names; + + void write_entries(); + directory_entry_t create_entry(const string& name, uint32_t inode, bool is_directory = false); + + void parse_block(common::buffer_t* buffer); + + void remove_entry(const string& name, bool is_directory, bool clear = true); + void rename_entry(const string& old_name, const string& new_name, bool is_directory); + + public: + Ext2Directory(Ext2Volume* volume, uint32_t inode, const string& name); + ~Ext2Directory() final; + + void read_from_disk() final; + + File* create_file(const string& name) final; + void remove_file(const string& name) final; + void rename_file(const string& old_name, const string& new_name) final; + + Directory* create_subdirectory(const string& name) final; + void remove_subdirectory(const string& name) final; + void rename_subdirectory(const string& old_name, const string& new_name) final; + }; + + /** + * @class Ext2FileSystem + * @brief Handles the ext2 filesystem operations + */ + class Ext2FileSystem final : public FileSystem { + private: + Ext2Volume m_volume; + + public: + Ext2FileSystem(drivers::disk::Disk* disk, uint32_t partition_offset); + ~Ext2FileSystem() final; + }; + + } + } + } +} + +#endif // MAXOS_FILESYSTEM_EXT2_H diff --git a/kernel/include/filesystem/format/fat32.h b/kernel/include/filesystem/format/fat32.h new file mode 100644 index 00000000..9e27fb94 --- /dev/null +++ b/kernel/include/filesystem/format/fat32.h @@ -0,0 +1,292 @@ +// +// Created by 98max on 1/01/2023. +// + +#ifndef MAXOS_FILESYSTEM_FAT32_H +#define MAXOS_FILESYSTEM_FAT32_H + +#include +#include +#include + +namespace MaxOS{ + namespace filesystem{ + namespace format{ + + // TODO: Revisit when I have the energy. + // BUG: Subdirectory seems to write to the disk this end but tools like + // fatcat complain the that the EOC isn't written (cluster 3037) + // - FAT32 Tests: + // - [x] Read subdirectories contents + // - [x] Read long path subdirectories contents + // - [ ] Create subdirectories + // - [ ] Create long path subdirectories + // - [ ] Delete subdirectories (need to add ability to free clusters first + // - [ ] Delete long path subdirectories + // - [ ] Rename directory + // - [ ] Rename file + // - [ ] Rename lfn directory + // - [ ] Rename lfn file + // - [x] Read files + // - [ ] Read large files + // - [x] Write files + // - [ ] Write large files + // - [ ] Create files + // - [ ] Delete files + // - [x] Read long path files + // - [ ] Create long path files + // - [ ] Delete long path files + // - [ ] Create files on a different mount point + // - [ ] Delete files on a different mount point + // - [ ] Read directories on a different mount point + // - [ ] Create directories on a different mount point + // - [ ] Stress test the filesystem: 1000s of files in a directory, long nested directories, long path files, etc + + /** + * @struct BiosParameterBlock + * @brief Stores information about the FAT32 filesystem + */ + typedef struct BiosParameterBlock32{ + + uint8_t jump[3]; + uint8_t OEM_name[8]; + uint16_t bytes_per_sector; + uint8_t sectors_per_cluster; + uint16_t reserved_sectors; + uint8_t table_copies; + uint16_t root_entries; + uint16_t total_sectors_16; + uint8_t media_type; + uint16_t fat_sector_count; + uint16_t sectors_per_track; + uint16_t head_count; + uint32_t hidden_sectors; + uint32_t total_sectors_32; + + uint32_t table_size_32; + uint16_t extended_flags; + uint16_t fat_version; + uint32_t root_cluster; + uint16_t fat_info; + uint16_t backup_sector; + uint8_t reserved0[12]; + uint8_t drive_number; + uint8_t reserved_1; + uint8_t boot_signature; + uint32_t uint_32; + uint8_t volume_label[11]; + uint8_t file_system_type[8]; + + } __attribute__((packed)) bpb32_t; + + /** + * @struct FileSystemInfo + * @brief Stores extra information about the FAT32 filesystem + */ + typedef struct FSInfo + { + uint32_t lead_signature; + uint8_t reserved1[480]; + uint32_t structure_signature; + uint32_t free_cluster_count; + uint32_t next_free_cluster; + uint8_t reserved2[12]; + uint32_t trail_signature; + } __attribute__((packed)) fs_info_t; + + /** + * @struct DirectoryEntry + * @brief Stores information about a file or directory + */ + typedef struct DirectoryEntry{ + + uint8_t name[8]; + uint8_t extension[3]; + uint8_t attributes; + uint8_t reserved; + + uint8_t creation_time_tenth; + uint16_t creation_time; + uint16_t creation_date; + uint16_t last_access_date; + + uint16_t first_cluster_high; + + uint16_t last_write_time; + uint16_t last_write_date; + + uint16_t first_cluster_low; + + uint32_t size; + + } __attribute__((packed)) dir_entry_t; + + enum class DirectoryEntryAttributes + { + FREE = 0x00, + READ_ONLY = 0x01, + HIDDEN = 0x02, + SYSTEM = 0x04, + VOLUME_ID = 0x08, + DIRECTORY = 0x10, + ARCHIVE = 0x20, + LONG_NAME = READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID, + }; + + enum class DirectoryEntryType + { + LAST = 0x00, + FREE = 0xE5, + }; + + typedef struct LongFileNameEntry + { + uint8_t order; + uint16_t name1[5]; + uint8_t attributes; + uint8_t type; + uint8_t checksum; + uint16_t name2[6]; + uint16_t zero; + uint16_t name3[2]; + + } __attribute__((packed)) long_file_name_entry_t; + + enum class ClusterState: uint32_t + { + FREE = 0x00000000, + BAD = 0x0FFFFFF7, + END_OF_CHAIN = 0xFFFFFFFF, + }; + + /** + * @class Fat32Volume + * @brief Handles the FAT table that stores the information about the files on the disk and operations on the disk + */ + class Fat32Volume + { + public: + Fat32Volume(drivers::disk::Disk* disk, lba_t partition_offset); + ~Fat32Volume(); + + bpb32_t bpb; + fs_info_t fsinfo; + + size_t fat_total_clusters; + lba_t fat_lba; + lba_t fat_info_lba; + lba_t fat_copies; + + lba_t data_lba; + lba_t root_lba; + + drivers::disk::Disk* disk; + + uint32_t next_cluster(uint32_t cluster); + uint32_t set_next_cluster(uint32_t cluster, uint32_t next_cluster); + uint32_t find_free_cluster(); + + uint32_t allocate_cluster(uint32_t cluster); + uint32_t allocate_cluster(uint32_t cluster, size_t amount); + + void free_cluster(uint32_t cluster); + void free_cluster(uint32_t cluster, size_t amount); + }; + + // Forward def + class Fat32Directory; + + /** + * @class Fat32File + * @brief Handles the file operations on the FAT32 filesystem + */ + class Fat32File final : public File + { + + private: + Fat32Volume* m_volume; + Fat32Directory* m_parent_directory; + + dir_entry_t* m_entry; + uint32_t m_first_cluster; + + public: + Fat32File(Fat32Volume* volume, Fat32Directory* parent, dir_entry_t* info, const string& name); + ~Fat32File() final; + + void write(const common::buffer_t* data, size_t amount) final; + void read(common::buffer_t* data, size_t amount) final; + void flush() final; + + uint32_t first_cluster() const { return m_first_cluster; } + }; + + /** + * @class Fat32Directory + * @brief Handles the directory operations on the FAT32 filesystem + */ + class Fat32Directory : public Directory + { + friend class Fat32File; + + private: + Fat32Volume* m_volume; + + lba_t m_first_cluster; + lba_t m_last_cluster; + size_t m_current_cluster_length = 0; + + common::Vector m_entries; + + dir_entry_t* create_entry(const string& name, bool is_directory); + void remove_entry(lba_t cluster, const string& name); + void read_all_entries(); + + int entry_index(lba_t cluster); + int find_free_entries(size_t amount); + int expand_directory(size_t amount); + + static common::Vector to_long_filenames(string name); + static string parse_long_filename(long_file_name_entry_t* entry, const string& current); + + protected: + + void save_entry_to_disk(dir_entry_t* entry); + void update_entry_on_disk(int index); + + public: + Fat32Directory(Fat32Volume* volume, lba_t cluster, const string& name); + ~Fat32Directory(); + + static const size_t MAX_NAME_LENGTH = 255; + + void read_from_disk() final; + + File* create_file(const string& name) final; + void remove_file(const string& name) final; + + Directory* create_subdirectory(const string& name) final; + void remove_subdirectory(const string& name) final; + + [[nodiscard]] lba_t first_cluster() const { return m_first_cluster; } + }; + + /** + * @class Fat32FileSystem + * @brief Handles the FAT32 filesystem operations + */ + class Fat32FileSystem : public FileSystem + { + private: + Fat32Volume m_volume; + + public: + Fat32FileSystem(drivers::disk::Disk* disk, uint32_t partition_offset); + ~Fat32FileSystem(); + }; + + } + } +} + +#endif //MAXOS_FILESYSTEM_FAT32_H diff --git a/kernel/include/filesystem/msdospart.h b/kernel/include/filesystem/msdospart.h deleted file mode 100644 index fcd9c682..00000000 --- a/kernel/include/filesystem/msdospart.h +++ /dev/null @@ -1,67 +0,0 @@ -// -// Created by 98max on 12/28/2022. -// - -#ifndef MAXOS_FILESYSTEM_MSDOSPART_H -#define MAXOS_FILESYSTEM_MSDOSPART_H - -#include "drivers/disk/ata.h" -#include -#include - -namespace MaxOS{ - - namespace filesystem{ - - /** - * @struct PartitionTableEntry - * @brief Stores information about a partition - */ - struct PartitionTableEntry{ - - uint8_t bootable; // 0x80 = bootable, 0x00 = not bootable - - uint8_t startHead; - uint8_t startSector : 6; - uint16_t startCylinder : 10; - - uint8_t partitionId; - - uint8_t endHead; - uint8_t endSector : 6; - uint16_t endCylinder : 10; - - uint32_t startLBA; - uint32_t length; - - } __attribute__((packed)); - - /** - * @struct MasterBootRecord - * @brief Stores information about the master boot record - */ - struct MasterBootRecord{ - - uint8_t bootloader[440]; - uint32_t diskSignature; - uint16_t unused; - - PartitionTableEntry primaryPartition[4]; - - uint16_t magicNumber; - - } __attribute__((packed)); - - /** - * @class MSDOSPartitionTable - * @brief Reads the partition table of the hard drive - */ - class MSDOSPartitionTable{ - public: - static void read_partitions(drivers::disk::AdvancedTechnologyAttachment *hd); - - }; - } -} - -#endif //MAXOS_FILESYSTEM_MSDOSPART_H diff --git a/kernel/include/filesystem/partition/msdos.h b/kernel/include/filesystem/partition/msdos.h new file mode 100644 index 00000000..96fed8f9 --- /dev/null +++ b/kernel/include/filesystem/partition/msdos.h @@ -0,0 +1,350 @@ +// +// Created by Max Tyson on 18/04/2025. +// + +#ifndef MAXOS_FILESYSTEM_PARTITION_MSDOS_H +#define MAXOS_FILESYSTEM_PARTITION_MSDOS_H + +#include +#include +#include +#include +#include + +namespace MaxOS{ + + namespace filesystem{ + + namespace partition{ + + + /// Credit: http://www.osdever.net/documents/pdf/partitiontypes.pdf + enum class PartitionType { + EMPTY, // 0x00 + FAT12, // 0x01 + XENIX_ROOT, // 0x02 + XENIX_USR, // 0x03 + FAT16, // 0x04 + EXTENDED, // 0x05 + FAT16_32MB, // 0x06 + NTFS, // 0x07 + QNX_QNY, // 0x08 + AIX_DATA, // 0x09 + OS2_BOOT_MANAGER, // 0x0A + FAT32, // 0x0B + FAT32_LBA, // 0x0C + UNUSED_1, // 0x0D + FAT16_LBA, // 0x0E + EXTENDED_LBA, // 0x0F + OPUS, // 0x10 + FAT12_HIDDEN, // 0x11 + COMPAQ, // 0x12 + UNUSED_2, // 0x13 + FAT16_HIDDEN, // 0x14 + UNUSED_3, // 0x15 + FAT16_32MB_HIDDEN, // 0x16 + IFS_HIDDEN, // 0x17 + AST_SMART_SLEEP, // 0x18 + UNUSED_4, // 0x19 + UNUSED_5, // 0x1A + FAT32_HIDDEN, // 0x1B + FAT32_LBA_HIDDEN, // 0x1C + UNUSED_6, // 0x1D + FAT16_LBA_HIDDEN, // 0x1E + + UNUSED_7, // 0x1F + UNUSED_8, // 0x20 + RESERVED_1, // 0x21 + UNUSED_10, // 0x22 + RESERVED_2, // 0x23 + NEC_DOS, // 0x24 + UNUSED_11, // 0x25 + RESERVED_3, // 0x26 + + // 0x27 - 0x30: unassigned + UNUSED_12, // 0x27 + UNUSED_13, // 0x28 + UNUSED_14, // 0x29 + UNUSED_15, // 0x2A + UNUSED_16, // 0x2B + UNUSED_17, // 0x2C + UNUSED_18, // 0x2D + UNUSED_19, // 0x2E + UNUSED_20, // 0x2F + UNUSED_21, // 0x30 + + RESERVED_4, // 0x31 + NOS, // 0x32 + RESERVED_5, // 0x33 + RESERVED_6, // 0x34 + JFS, // 0x35 + RESERVED_7, // 0x36 + UNUSED_22, // 0x37 + THEOS_3_2, // 0x38 + PLAN9, // 0x39 + THEOS_4_4GB, // 0x3A + THEOS_4_EXTENDED, // 0x3B + PARTITIONMAGIC_RECOVERY, // 0x3C + HIDDEN_NETWARE, // 0x3D + UNUSED_23, // 0x3E + UNUSED_24, // 0x3F + + VENIX, // 0x40 + LINUX_MINIX, // 0x41 + LINUX_SWAP, // 0x42 + LINUX_NATIVE, // 0x43 + GOBACK, // 0x44 + BOOT_US, // 0x45 + EUMEL_ELAN_46, // 0x46 + EUMEL_ELAN_47, // 0x47 + EUMEL_ELAN_48, // 0x48 + UNUSED_25, // 0x49 + ADAOS, // 0x4A + UNUSED_26, // 0x4B + OBERON, // 0x4C + QNX4, // 0x4D + QNX4_SECOND, // 0x4E + QNX4_THIRD, // 0x4F + + ONTRACK_DM, // 0x50 + ONTRACK_DM_RW, // 0x51 + CPM, // 0x52 + DISK_MANAGER_AUX3, // 0x53 + DISK_MANAGER_DDO, // 0x54 + EZ_DRIVE, // 0x55 + GOLDEN_BOW, // 0x56 + DRIVE_PRO, // 0x57 + + UNUSED_27, // 0x58 + UNUSED_28, // 0x59 + UNUSED_29, // 0x5A + UNUSED_30, // 0x5B + PRIAM_EDISK, // 0x5C + UNUSED_31, // 0x5D + UNUSED_32, // 0x5E + UNUSED_33, // 0x5F + + UNUSED_34, // 0x60 + SPEEDSTOR, // 0x61 + UNUSED_35, // 0x62 + UNIX, // 0x63 + PC_ARMOUR, // 0x64 + NOVELL_NETWARE386, // 0x65 + NOVELL_SMS, // 0x66 + NOVELL, // 0x67 + NOVELL_OLD, // 0x68 + NOVELL_NETWARE_NSS, // 0x69 + + UNUSED_36, // 0x6A + UNUSED_37, // 0x6B + UNUSED_38, // 0x6C + UNUSED_39, // 0x6D + UNUSED_40, // 0x6E + UNUSED_41, // 0x6F + + DISKSECURE, // 0x70 + RESERVED_8, // 0x71 + UNUSED_42, // 0x72 + RESERVED_9, // 0x73 + SCRAMDISK, // 0x74 + IBM_PCIX, // 0x75 + RESERVED_10, // 0x76 + M2FS, // 0x77 + XOSL_FS, // 0x78 + UNUSED_43, // 0x79 + UNUSED_44, // 0x7A + UNUSED_45, // 0x7B + UNUSED_46, // 0x7C + UNUSED_47, // 0x7D + UNUSED_48, // 0x7E + UNUSED_49, // 0x7F + + MINIX, // 0x80 + MINIX2, // 0x81 + LINUX_SWAP_ALT, // 0x82 + LINUX_EXT2, // 0x83 + HIBERNATION, // 0x84 + LINUX_EXTENDED, // 0x85 + LINUX_RAID, // 0x86 + NTFS_VOLUME_SET, // 0x87 + UNUSED_50, // 0x88 + UNUSED_51, // 0x89 + LINUX_KERNEL, // 0x8A + FAULT_TOLERANT_FAT32, // 0x8B + FT_FAT32_LBA, // 0x8C + FREEFDISK_FAT12, // 0x8D + LINUX_LVM, // 0x8E + UNUSED_52, // 0x8F + FREEFDISK_FAT16, // 0x90 + FREEFDISK_EXTENDED, // 0x91 + FREEFDISK_FAT16_LARGE, // 0x92 + HIDDEN_LINUX_NATIVE, // 0x93 + AMOEBA_BAD_BLOCK, // 0x94 + MIT_EXOPC, // 0x95 + UNUSED_53, // 0x96 + FREEFDISK_FAT32, // 0x97 + FREEFDISK_FAT32_LBA, // 0x98 + DCE376, // 0x99 + FREEFDISK_FAT16_LBA, // 0x9A + FREEFDISK_EXTENDED_LBA,// 0x9B + + UNUSED_54, // 0x9C + UNUSED_55, // 0x9D + UNUSED_56, // 0x9E + + BSD_OS, // 0x9F + LAPTOP_HIBERNATION, // 0xA0 + HP_VOLUME_EXPANSION, // 0xA1 + UNUSED_57, // 0xA2 + RESERVED_11, // 0xA3 + RESERVED_12, // 0xA4 + BSD_386, // 0xA5 + OPENBSD, // 0xA6 + NEXTSTEP, // 0xA7 + MAC_OS_X, // 0xA8 + NETBSD, // 0xA9 + OLIVETTI_SERVICE, // 0xAA + MAC_OS_X_BOOT, // 0xAB + UNUSED_58, // 0xAC + UNUSED_59, // 0xAD + SHAGOS, // 0xAE + SHAGOS_SWAP, // 0xAF + BOOTSTAR, // 0xB0 + + RESERVED_13, // 0xB1 + UNUSED_60, // 0xB2 + RESERVED_14, // 0xB3 + RESERVED_15, // 0xB4 + UNUSED_61, // 0xB5 + RESERVED_16, // 0xB6 + + BSDI_BSD386_FS, // 0xB7 + BSDI_BSD386_SWAP, // 0xB8 + UNUSED_62, // 0xB9 + UNUSED_63, // 0xBA + BOOT_WIZARD_HIDDEN, // 0xBB + UNUSED_64, // 0xBC + UNUSED_65, // 0xBD + SOLARIS8_BOOT, // 0xBE + UNUSED_66, // 0xBF + CTOS, // 0xC0 + DRDOS_FAT12, // 0xC1 + RESERVED_17, // 0xC2 + HIDDEN_LINUX_SWAP, // 0xC3 + DRDOS_FAT16, // 0xC4 + DRDOS_EXTENDED, // 0xC5 + DRDOS_FAT16_LARGE, // 0xC6 + NTFS_CORRUPT, // 0xC7 + + UNUSED_67, // 0xC8 + UNUSED_68, // 0xC9 + UNUSED_69, // 0xCA + + DRDOS_FAT32, // 0xCB + DRDOS_FAT32_LBA, // 0xCC + CTOS_MEMDUMP, // 0xCD + DRDOS_FAT16_LBA, // 0xCE + UNUSED_70, // 0xCF + REAL32_SECURE_BIG, // 0xD0 + OLD_MULTIUSER_DOS_FAT12,// 0xD1 + UNUSED_71, // 0xD2 + UNUSED_72, // 0xD3 + OLD_MULTIUSER_DOS_FAT16,// 0xD4 + OLD_MULTIUSER_DOS_EXTENDED,// 0xD5 + OLD_MULTIUSER_DOS_FAT16_LARGE,// 0xD6 + UNUSED_73, // 0xD7 + CPM86, // 0xD8 + UNUSED_74, // 0xD9 + NON_FS_DATA, // 0xDA + DIGITAL_RESEARCH_CPM, // 0xDB + UNUSED_75, // 0xDC + HIDDEN_CTOS_MEMDUMP, // 0xDD + DELL_POWEREDGE, // 0xDE + DGUX, // 0xDF + STM_AVFS, // 0xE0 + SPEEDSTOR_FAT_EXTENDED,// 0xE1 + UNUSED_76, // 0xE2 + SPEEDSTOR_RO, // 0xE3 + SPEEDSTOR_EXTENDED, // 0xE4 + TANDY_DOS, // 0xE5 + + RESERVED_18, // 0xE6 + UNUSED_77, // 0xE7 + UNUSED_78, // 0xE8 + UNUSED_79, // 0xE9 + + BEFS, // 0xEA + SPRYTIX, // 0xEB + EFI_PROTECTIVE, // 0xEE + EFI_SYSTEM, // 0xEF + LINUX_PA_RISC, // 0xF0 + SPEEDSTOR_2, // 0xF1 + DOS_3_3_SECONDARY, // 0xF2 + RESERVED_19, // 0xF3 + SPEEDSTOR_LARGE, // 0xF4 + PROLOGUE_MULTI, // 0xF5 + RESERVED_20, // 0xF6 + UNUSED_80, // 0xF7 + UNUSED_81, // 0xF8 + UNUSED_82, // 0xF9 + BOCHS, // 0xFA + VMFS, // 0xFB + VMWARE_SWAP, // 0xFC + LINUX_RAID_AUTO, // 0xFD + NT_HIDDEN, // 0xFE + XENIX_BAD_BLOCK // 0xFF + }; + + /** + * @struct PartitionTableEntry + * @brief Stores information about a partition + */ + struct PartitionTableEntry{ + + uint8_t bootable; // 0x80 = bootable, 0x00 = not bootable + + uint8_t start_head; + uint8_t start_sector : 6; + uint16_t start_cylinder : 10; + + uint8_t type; + + uint8_t end_head; + uint8_t end_sector : 6; + uint16_t end_cylinder : 10; + + uint32_t start_LBA; + uint32_t length; + + } __attribute__((packed)); + + /** + * @struct MasterBootRecord + * @brief Stores information about the master boot record + */ + struct MasterBootRecord{ + + uint8_t bootloader[440]; + uint32_t disk_signature; + uint16_t unused; + + PartitionTableEntry primary_partition[4]; + + uint16_t magic; + + } __attribute__((packed)); + + + class MSDOSPartition + { + public: + static void mount_partitions(drivers::disk::Disk* disk); + }; + + + // TODO: Abstract some of this into a base class and use it for GPT and other partition tables + } + } +} + +#endif //MSDOS_H diff --git a/kernel/include/filesystem/path.h b/kernel/include/filesystem/path.h new file mode 100644 index 00000000..e612f0d8 --- /dev/null +++ b/kernel/include/filesystem/path.h @@ -0,0 +1,39 @@ +// +// Created by 98max on 9/2/2025. +// + +#ifndef MAXOS_FILESYSTEM_PATH_H +#define MAXOS_FILESYSTEM_PATH_H + +#include + +namespace MaxOS { + + namespace filesystem { + + /** + * @class Path + * @brief Handles file & directory paths + */ + class Path + { + public: + static bool valid(string path); + static bool is_file(const string& path); + + static string file_name(string path); + static string file_extension(string path); + static string file_path(string path); + + static string top_directory(string path); + static string parent_directory(string path); + + static string absolute_path(string path); + static string join_path(string base, string extended); + }; + + + } +} + +#endif //MAXOS_FILESYSTEM_PATH_H diff --git a/kernel/include/filesystem/vfs.h b/kernel/include/filesystem/vfs.h new file mode 100644 index 00000000..a371f052 --- /dev/null +++ b/kernel/include/filesystem/vfs.h @@ -0,0 +1,63 @@ +// +// Created by Max Tyson on 20/04/2025. +// + +#ifndef MAXOS_FILESYSTEM_VFS_H +#define MAXOS_FILESYSTEM_VFS_H + +#include +#include +#include + +namespace MaxOS{ + + namespace filesystem{ + + class VirtualFileSystem{ + + private: + common::Map filesystems; + inline static VirtualFileSystem* s_current_file_system = nullptr; + + public: + VirtualFileSystem(); + ~VirtualFileSystem(); + + static VirtualFileSystem* current_file_system(); + + void mount_filesystem(FileSystem* filesystem); + void mount_filesystem(FileSystem* filesystem, const string& mount_point); + void unmount_filesystem(FileSystem* filesystem); + void unmount_filesystem(const string& mount_point); + void unmount_all(); + + Directory* root_directory(); + FileSystem* root_filesystem(); + + FileSystem* get_filesystem(const string& mount_point); + FileSystem* find_filesystem(string path); + string get_relative_path(FileSystem* filesystem, string path); + + Directory* open_directory(const string& path); + static Directory* open_directory(Directory* parent, const string& name); + + Directory* create_directory(string path); + static Directory* create_directory(Directory* parent, const string& name); + + void delete_directory(string path); + static void delete_directory(Directory* parent, const string& name); + static void delete_directory(Directory* parent, Directory* directory); + + File* create_file(const string& path); + static File* create_file(Directory* parent, const string& name); + + File* open_file(const string& path, size_t offset = 0); + static File* open_file(Directory* parent, const string& name, size_t offset = 0); + + void delete_file(const string& path); + static void delete_file(Directory* parent, const string& name); + }; + } +} + +#endif //MAXOS_FILESYSTEM_VFS_H diff --git a/kernel/include/filesystem/vfsresource.h b/kernel/include/filesystem/vfsresource.h new file mode 100644 index 00000000..ede19f8a --- /dev/null +++ b/kernel/include/filesystem/vfsresource.h @@ -0,0 +1,72 @@ +// +// Created by 98max on 9/1/2025. +// + +#ifndef MAXOS_FILESYSTEM_VFSRESOURCE_H +#define MAXOS_FILESYSTEM_VFSRESOURCE_H + +#include +#include +#include +#include +#include +#include + +namespace MaxOS { + + namespace filesystem { + + class FileResource final : public processes::Resource{ + + public: + FileResource(const string& name, size_t flags, processes::resource_type_t type); + ~FileResource() final; + + File* file; + + int read(void* buffer, size_t size, size_t flags) final; + int write(const void* buffer, size_t size, size_t flags) final; + + }; + + class DirectoryResource final : public processes::Resource{ + + private: + + void write_entries(const void* buffer, size_t size) const; + [[nodiscard]] size_t entries_size() const; + + public: + + DirectoryResource(const string& name, size_t flags, processes::resource_type_t type); + ~DirectoryResource() final; + + Directory* directory; + + int read(void* buffer, size_t size, size_t flags) final; + int write(const void* buffer, size_t size, size_t flags) final; + + }; + + class VFSResourceRegistry : processes::BaseResourceRegistry{ + + private: + VirtualFileSystem* m_vfs; + + processes::Resource* open_as_resource(const string& name, Directory* directory); + processes::Resource* open_as_resource(const string& name, File* file); + + public: + + explicit VFSResourceRegistry(VirtualFileSystem* vfs); + ~VFSResourceRegistry(); + + processes::Resource* get_resource(const string& name) final; + processes::Resource* create_resource(const string& name, size_t flags) final; + + }; + + } +} + +#endif //MAXOS_FILESYSTEM_VFSRESOURCE_H diff --git a/kernel/include/hardwarecommunication/pci.h b/kernel/include/hardwarecommunication/pci.h index d4faf211..858b4fe0 100644 --- a/kernel/include/hardwarecommunication/pci.h +++ b/kernel/include/hardwarecommunication/pci.h @@ -82,8 +82,8 @@ namespace MaxOS Port32Bit m_command_port; // I/O - uint32_t read(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset); - void write(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset, uint32_t value); + uint32_t read(uint16_t bus, uint16_t device, uint16_t function, uint32_t register_offset); + void write(uint16_t bus, uint16_t device, uint16_t function, uint32_t register_offset, uint32_t value); // Device PeripheralComponentInterconnectDeviceDescriptor get_device_descriptor(uint16_t bus, uint16_t device, uint16_t function); diff --git a/kernel/include/memory/physical.h b/kernel/include/memory/physical.h index ba4f8e11..bb98180c 100644 --- a/kernel/include/memory/physical.h +++ b/kernel/include/memory/physical.h @@ -100,7 +100,7 @@ namespace MaxOS { // Table Management pml_t* get_or_create_table(pml_t* table, size_t index, size_t flags); pml_t* get_and_create_table(pml_t* parent_table, uint64_t table_index, pml_t* table); - pte_t create_page_table_entry(uintptr_t address, size_t flags); + pte_t create_page_table_entry(uintptr_t address, size_t flags) const; static uint64_t physical_address_of_entry(pte_t* entry); pte_t* get_entry(virtual_address_t* virtual_address, pml_t* pml4_root); diff --git a/kernel/include/processes/elf.h b/kernel/include/processes/elf.h index d0ff7891..8c412519 100644 --- a/kernel/include/processes/elf.h +++ b/kernel/include/processes/elf.h @@ -158,7 +158,7 @@ namespace MaxOS static char elf_magic[4]; - void load_program_headers(); + void load_program_headers() const; public: @@ -166,11 +166,11 @@ namespace MaxOS ~Elf64(); void load(); - bool is_valid(); + bool is_valid() const; [[nodiscard]] elf_64_header_t* header() const; - elf_64_program_header_t* get_program_header(size_t index); - elf_64_section_header_t* get_section_header(size_t index); + elf_64_program_header_t* get_program_header(size_t index) const; + elf_64_section_header_t* get_section_header(size_t index) const; static uint64_t to_vmm_flags(uint32_t type); diff --git a/kernel/include/processes/ipc.h b/kernel/include/processes/ipc.h index 4a82dc09..b4a62dbe 100644 --- a/kernel/include/processes/ipc.h +++ b/kernel/include/processes/ipc.h @@ -9,92 +9,52 @@ #include #include #include +#include #include #include #include - +#include namespace MaxOS { namespace processes { - //TODO: LibIPC - - class SharedMemory{ + class SharedMemory final : public Resource { private: uintptr_t m_physical_address; size_t m_size; - uint64_t m_owner_pid; + common::Map m_mappings; public: - SharedMemory(string name, size_t size); - ~SharedMemory(); + SharedMemory(const string& name, size_t size, resource_type_t type); + ~SharedMemory() final; - string* name; + string name; uint64_t use_count = 1; + void open(size_t flags) final; + void close(size_t flags) final; + + int read(void* buffer, size_t size, size_t flags) final; + [[nodiscard]] uintptr_t physical_address() const; [[nodiscard]] size_t size() const; }; - - typedef struct SharedMessage{ - void* message_buffer; - size_t message_size; - uintptr_t next_message; - } ipc_message_t; - - typedef struct SharedMessageQueue{ - ipc_message_t* messages; - } ipc_message_queue_t; - - class SharedMessageEndpoint{ + class SharedMessageEndpoint final : public Resource{ private: - ipc_message_queue_t* m_queue = nullptr; - uint64_t m_owner_pid; + common::Vector m_queue {}; common::Spinlock m_message_lock; public: - SharedMessageEndpoint(string name); - ~SharedMessageEndpoint(); - - string* name; - [[nodiscard]] ipc_message_queue_t* queue() const; + SharedMessageEndpoint(const string& name, size_t size, resource_type_t type); + ~SharedMessageEndpoint() final; - void queue_message(void* message, size_t size); - [[nodiscard]] bool owned_by_current_process() const; + int read(void* buffer, size_t size, size_t flags) final; + int write(const void* buffer, size_t size, size_t flags) final; }; - - /** - * @class InterProcessCommunicationManager (IPC) - * @brief Manages the Inter-Process Communication between processes via shared memory and message passing - */ - class InterProcessCommunicationManager { - - common::Vector m_shared_memory_blocks; - common::Vector m_message_endpoints; - - common::Spinlock m_lock; - - public: - - InterProcessCommunicationManager(); - ~InterProcessCommunicationManager(); - - SharedMemory* alloc_shared_memory(size_t size, string name); - SharedMemory* get_shared_memory(const string& name); - void free_shared_memory(const string& name); - void free_shared_memory(uintptr_t physical_address); - void free_shared_memory(SharedMemory* block); - - SharedMessageEndpoint* create_message_endpoint(const string& name); - SharedMessageEndpoint* get_message_endpoint(const string& name); - void free_message_endpoint(const string& name); - static void free_message_endpoint(SharedMessageEndpoint* endpoint); - }; - } } diff --git a/kernel/include/processes/process.h b/kernel/include/processes/process.h index e98d5cf0..a0903c19 100644 --- a/kernel/include/processes/process.h +++ b/kernel/include/processes/process.h @@ -7,12 +7,14 @@ #include #include +#include #include #include #include #include #include #include +#include namespace MaxOS @@ -67,8 +69,6 @@ namespace MaxOS void save_sse_state(); void restore_sse_state(); - - }; /** @@ -79,8 +79,6 @@ namespace MaxOS { private: - - common::Vector m_resource_ids; common::Vector m_threads; uint64_t m_pid = 0; @@ -102,7 +100,10 @@ namespace MaxOS bool is_kernel; string name; + string working_directory = "/"; + memory::MemoryManager* memory_manager = nullptr; + ResourceManager resource_manager; }; diff --git a/kernel/include/processes/resource.h b/kernel/include/processes/resource.h new file mode 100644 index 00000000..897ff5d4 --- /dev/null +++ b/kernel/include/processes/resource.h @@ -0,0 +1,123 @@ +// +// Created by 98max on 8/26/2025. +// + +#ifndef MAXOS_PROCESSES_RESOURCE_H +#define MAXOS_PROCESSES_RESOURCE_H + +#include +#include +#include +#include +#include +#include + +namespace MaxOS { + namespace processes { + + typedef ::syscore::ResourceType resource_type_t; + typedef ::syscore::ResourceErrorBase resource_error_base_t; + + class Resource { + + private: + string m_name; + resource_type_t m_type; + + public: + + Resource(const string& name, size_t flags, resource_type_t type); + virtual ~Resource(); + + string name(); + resource_type_t type(); + + virtual void open(size_t flags); + virtual void close(size_t flags); + + virtual int read(void* buffer, size_t size, size_t flags); + virtual int write(const void* buffer, size_t size, size_t flags); + }; + + class BaseResourceRegistry{ + + protected: + common::Map m_resources; + common::Map m_resource_uses; + + resource_type_t m_type; + + public: + explicit BaseResourceRegistry(resource_type_t type); + ~BaseResourceRegistry(); + + resource_type_t type(); + + virtual Resource* get_resource(const string& name); + virtual bool register_resource(Resource* resource); + + virtual void close_resource(Resource* resource, size_t flags); + virtual Resource* create_resource(const string& name, size_t flags); + }; + + template class ResourceRegistry : public BaseResourceRegistry{ + + public: + explicit ResourceRegistry(resource_type_t type); + ~ResourceRegistry() = default; + + Resource* create_resource(const string& name, size_t flags) override { + + auto resource = new Type(name, flags, type()); + + // Creation failed + if(!register_resource(resource)){ + delete resource; + return nullptr; + } + + return resource; + } + }; + + template ResourceRegistry::ResourceRegistry(resource_type_t type):BaseResourceRegistry(type) {} + + class GlobalResourceRegistry{ + + private: + common::Map m_registries; + inline static GlobalResourceRegistry* s_current; + + public: + GlobalResourceRegistry(); + ~GlobalResourceRegistry(); + + static BaseResourceRegistry* get_registry(resource_type_t type); + + static void add_registry(resource_type_t type, BaseResourceRegistry* registry); + static void remove_registry(BaseResourceRegistry* registry); + }; + + class ResourceManager{ + + private: + common::Map m_resources; + + uint64_t m_next_handle = 1; + + public: + ResourceManager(); + ~ResourceManager(); + + common::Map resources(); + + uint64_t open_resource(resource_type_t type, const string& name, size_t flags); + void close_resource(uint64_t handle, size_t flags); + + Resource* get_resource(uint64_t handle); + Resource* get_resource(const string& name); + }; + } +} + +#endif //MAXOS_PROCESSES_RESOURCE_H diff --git a/kernel/include/processes/scheduler.h b/kernel/include/processes/scheduler.h index a8aaf5aa..614b2382 100644 --- a/kernel/include/processes/scheduler.h +++ b/kernel/include/processes/scheduler.h @@ -36,13 +36,14 @@ namespace MaxOS{ inline static Scheduler* s_instance = nullptr; static const uint64_t s_ticks_per_event = { 3 }; - InterProcessCommunicationManager* m_ipc; + GlobalResourceRegistry m_global_resource_registry = {}; + ResourceRegistry m_shared_memory_registry; + ResourceRegistry m_shared_messages_registry; public: Scheduler(system::Multiboot& multiboot); ~Scheduler(); - system::cpu_status_t* handle_interrupt(system::cpu_status_t* status) final; system::cpu_status_t* schedule(system::cpu_status_t* status); @@ -59,7 +60,6 @@ namespace MaxOS{ static Process* get_process(uint64_t pid); static Thread* current_thread(); static Thread* get_thread(uint64_t tid); - static InterProcessCommunicationManager* scheduler_ipc(); [[nodiscard]] uint64_t ticks() const; diff --git a/kernel/include/runtime/ubsan.h b/kernel/include/runtime/ubsan.h index e41c5ef2..1e1198ee 100644 --- a/kernel/include/runtime/ubsan.h +++ b/kernel/include/runtime/ubsan.h @@ -77,6 +77,12 @@ namespace MaxOS { "cast to virtual base of", }; + + typedef struct vla_bound_not_positive_info{ + source_location_t location; + type_descriptor_t* type; + } vla_bound_not_positive_info_t; + /** * @class UBSanHandler * @brief Handles undefined behaviour sanitizer diff --git a/kernel/include/system/syscalls.h b/kernel/include/system/syscalls.h index 8b712009..fe437280 100644 --- a/kernel/include/system/syscalls.h +++ b/kernel/include/system/syscalls.h @@ -12,6 +12,7 @@ #include #include #include +#include // TODO: Rename / make clear that this references the system lib namespace MaxOS{ @@ -20,22 +21,6 @@ namespace MaxOS{ // Forward declaration class SyscallManager; - /// DO NOT REARRANGE ONLY APPEND TO - enum SyscallType{ - CLOSE_PROCESS, - KLOG, - CREATE_SHARED_MEMORY, - OPEN_SHARED_MEMORY, - ALLOCATE_MEMORY, - FREE_MEMORY, - CREATE_IPC_ENDPOINT, - SEND_IPC_MESSAGE, - REMOVE_IPC_ENDPOINT, - THREAD_YIELD, - THREAD_SLEEP, - THREAD_CLOSE, - }; - typedef struct SyscallArguments{ uint64_t arg0; uint64_t arg1; @@ -44,7 +29,7 @@ namespace MaxOS{ uint64_t arg4; uint64_t arg5; uint64_t return_value; - system::cpu_status_t* return_state; + cpu_status_t* return_state; } syscall_args_t; // Could use a class based response but a single class might want multiple handlers e.g. fs @@ -65,25 +50,24 @@ namespace MaxOS{ SyscallManager(); ~SyscallManager(); - system::cpu_status_t* handle_interrupt(system::cpu_status_t* esp) final; - - void set_syscall_handler(uint8_t syscall, syscall_func_t handler); - void remove_syscall_handler(uint8_t syscall); - - - // Syscalls - static system::syscall_args_t* syscall_close_process(system::syscall_args_t* args); - static system::syscall_args_t* syscall_klog(system::syscall_args_t* args); - static system::syscall_args_t* syscall_create_shared_memory(system::syscall_args_t* args); - static system::syscall_args_t* syscall_open_shared_memory(system::syscall_args_t* args); - static system::syscall_args_t* syscall_allocate_memory(system::syscall_args_t* args); - static system::syscall_args_t* syscall_free_memory(system::syscall_args_t* args); - static system::syscall_args_t* syscall_create_ipc_endpoint(system::syscall_args_t* args); - static system::syscall_args_t* syscall_send_ipc_message(system::syscall_args_t* args); - static system::syscall_args_t* syscall_remove_ipc_endpoint(system::syscall_args_t* args); - static system::syscall_args_t* syscall_thread_yield(system::syscall_args_t* args); - static system::syscall_args_t* syscall_thread_sleep(system::syscall_args_t* args); - static system::syscall_args_t* syscall_thread_close(system::syscall_args_t* args); + cpu_status_t* handle_interrupt(cpu_status_t* esp) final; + + void set_syscall_handler(::syscore::SyscallType syscall, syscall_func_t handler); + void remove_syscall_handler(::syscore::SyscallType syscall); + + // Syscalls (TODO: Very c style, should be made class based that automatically registers) + static syscall_args_t* syscall_close_process(syscall_args_t* args); + static syscall_args_t* syscall_klog(syscall_args_t* args); + static syscall_args_t* syscall_allocate_memory(syscall_args_t* args); + static syscall_args_t* syscall_free_memory(syscall_args_t* args); + static syscall_args_t* syscall_resource_create(syscall_args_t* args); + static syscall_args_t* syscall_resource_open(syscall_args_t* args); + static syscall_args_t* syscall_resource_close(syscall_args_t* args); + static syscall_args_t* syscall_resource_write(syscall_args_t* args); + static syscall_args_t* syscall_resource_read(syscall_args_t* args); + static syscall_args_t* syscall_thread_yield(syscall_args_t* args); + static syscall_args_t* syscall_thread_sleep(syscall_args_t* args); + static syscall_args_t* syscall_thread_close(syscall_args_t* args); }; } diff --git a/kernel/src/asm/interruptstubs.s b/kernel/src/asm/interrupts.s similarity index 100% rename from kernel/src/asm/interruptstubs.s rename to kernel/src/asm/interrupts.s diff --git a/kernel/src/asm/loader.s b/kernel/src/asm/loader.s index 4fd42a69..db13be80 100644 --- a/kernel/src/asm/loader.s +++ b/kernel/src/asm/loader.s @@ -1,8 +1,8 @@ %define KERNEL_VIRTUAL_ADDR 0xFFFFFFFF80000000 %define PAGE_SIZE 0x1000 %define FLAGS 0b10 | 1 -%define LOOP_LIMIT 1024 -%define PD_LOOP_LIMIT 2 +%define LOOP_LIMIT 2048 +%define PD_LOOP_LIMIT 4 global p2_table @@ -27,17 +27,7 @@ start: ; Setup lower half of the stack mov esp, stack.top - KERNEL_VIRTUAL_ADDR - ; Map the kernel into the higher half - mov eax, p3_table_hh - KERNEL_VIRTUAL_ADDR - or eax, FLAGS - mov dword [(p4_table - KERNEL_VIRTUAL_ADDR) + 511 * 8], eax - - ; Map the kernel into the higher half (second level) - mov eax, p2_table - KERNEL_VIRTUAL_ADDR - or eax, FLAGS - mov dword[(p3_table_hh - KERNEL_VIRTUAL_ADDR) + 510 * 8], eax - - ; Map the pml4 into itself + ; Identity map the p4 table mov eax, p4_table - KERNEL_VIRTUAL_ADDR or eax, FLAGS mov dword [(p4_table - KERNEL_VIRTUAL_ADDR) + 510 * 8], eax @@ -52,6 +42,16 @@ start: or eax, FLAGS mov dword [(p3_table - KERNEL_VIRTUAL_ADDR) + 0], eax + ; Map the kernel into the higher half + mov eax, p3_table_hh - KERNEL_VIRTUAL_ADDR + or eax, FLAGS + mov dword [(p4_table - KERNEL_VIRTUAL_ADDR) + 511 * 8], eax + + ; Map the kernel into the higher half (second level) + mov eax, p2_table - KERNEL_VIRTUAL_ADDR + or eax, FLAGS + mov dword[(p3_table_hh - KERNEL_VIRTUAL_ADDR) + 510 * 8], eax + ; Map 8MB of kernel memory (2 page directories) mov ebx, 0 mov eax, p1_tables - KERNEL_VIRTUAL_ADDR @@ -67,7 +67,7 @@ start: cmp ebx, PD_LOOP_LIMIT jne .map_pd_table - ; Fill the page directory with the page tables + ; Fill the page directory with the kernel page tables mov ecx, 0 .map_p2_table: @@ -137,7 +137,7 @@ p3_table_hh: p2_table: resb 4096 p1_tables: - resb 8192 + resb 16384 ; The stack for the kernel diff --git a/kernel/src/asm/multiboot_header.s b/kernel/src/asm/multiboot_header.s index b35c64ea..3bd4441c 100644 --- a/kernel/src/asm/multiboot_header.s +++ b/kernel/src/asm/multiboot_header.s @@ -1,25 +1,23 @@ section .multiboot_header header_start: align 8 - dd 0xe85250d6 ;magic_number - dd 0 ;Protected mode - dd header_end - header_start ;Header length + dd 0xe85250d6 ; Magic number + dd 0 ; Protected mode + dd header_end - header_start ; Header length - ;compute checksum + ; Checksum dd 0x100000000 - (0xe85250d6 + 0 + (header_end - header_start)) framebuffer_tag_start: - dw 0x05 ;Type: framebuffer - dw 0x01 ;Optional tag - dd framebuffer_tag_end - framebuffer_tag_start ;size - dd 0 ;Width - if 0 we let the bootloader decide - dd 0 ;Height - same as above - dd 0 ;Depth - same as above + dw 0x05 ; Tag: Framebuffer + dw 0x01 ; Tag is optional + dd framebuffer_tag_end - framebuffer_tag_start ; Size + dd 0 ; Width - let GRUB pick + dd 0 ; Height - let GRUB pick + dd 0 ; Depth - let GRUB pick framebuffer_tag_end: - ;here ends the required part of the multiboot header - ;The following is the end tag, must be always present - ;end tag + ; End Of multiboot tag align 8 dw 0 ;type dw 0 ;flags diff --git a/kernel/src/common/buffer.cpp b/kernel/src/common/buffer.cpp new file mode 100644 index 00000000..588bdd39 --- /dev/null +++ b/kernel/src/common/buffer.cpp @@ -0,0 +1,356 @@ +// +// Created by 98max on 30/07/2025. +// + +#include + +using namespace MaxOS; +using namespace MaxOS::common; + +/** + * @brief Creates a buffer of the specified size + * + * @param size + */ +Buffer::Buffer(size_t size, bool update_offset) +: m_capacity(size), + update_offset(update_offset) +{ + + // Create the buffer + m_bytes = new uint8_t[size]; + +} + +/** + * @brief Creates a buffer pointing to a source + * + * @param source The source + * @param size The capacity of the source + */ +Buffer::Buffer(void* source, size_t size, bool update_offset) +: update_offset(update_offset) +{ + + m_bytes = (uint8_t*)source; + m_capacity = size; + m_dont_delete = true; + +} + +/** + * @brief Destroy the buffer, freeing the memory + */ +Buffer::~Buffer() { + if(!m_dont_delete && m_bytes) + delete[] m_bytes; +} + +/** + * @brief The raw pointer to the bytes stored in memory, use is not recommended + * @return The address of buffer's storage + */ +uint8_t *Buffer::raw() const{ + + return m_bytes; +} + +/** + * @brief Fulls the buffer with 0's and resets the offset + */ +void Buffer::clear() { + + m_offset = 0; + memset(m_bytes, 0, m_capacity); +} + +/** + * @brief Full the buffer with a specified byte at an offset + * + * @param byte The byte to write + * @param offset Where to start writing (deafults to 0) + * @param amount + */ +void Buffer::full(uint8_t byte, size_t offset, size_t amount) { + + // Prevent writing past the buffer bounds + if (amount == 0) amount = m_capacity - m_offset - offset; + ASSERT(m_offset + offset + amount <= capacity(), "Buffer overflow"); + + memset(m_bytes + m_offset + offset, byte, amount); + +} + +/** + * @brief The max bytes the buffer can currently store. @see Buffer:grow(size_t size) to increase this. + * @return The length of the buffer storage array + */ +size_t Buffer::capacity() const { + return m_capacity; +} + +/** + * @brief Grow the buffer to fit a new size + * + * @param size The new max capacity of the buffer + */ +void Buffer::resize(size_t size) { + + // Create the new buffer + auto* new_buffer = new uint8_t[size]; + + // Copy the old buffer + if(m_bytes) + memcpy(new_buffer, m_bytes, m_capacity); + + // Store the new buffer + if(!m_dont_delete && m_bytes) + delete[] m_bytes; + + m_bytes = new_buffer; + m_capacity = size; + m_dont_delete = true; + +} + +/** + * @brief Set the offset for where operations should begin from + * + * @param offset The new offset + */ +void Buffer::set_offset(size_t offset) { + + if(update_offset) + m_offset = offset; + +} + +/** + * @brief Safely writes a byte to the buffer at the current offset + * + * @param byte The byte to write + */ +void Buffer::write(uint8_t byte) { + m_bytes[m_offset] = byte; + set_offset(m_offset + 1); +} + + +/** + * @brief Safely writes a byte to the buffer at the specified offset + * + * @param offset The offset into the buffer storage array + * @param byte The byte to write + */ +void Buffer::write(size_t offset, uint8_t byte) { + + // Prevent writing past the buffer bounds + ASSERT(m_offset + offset < capacity() && offset >= 0, "Buffer overflow"); + + // Set the byte + m_bytes[m_offset + offset] = byte; + set_offset(m_offset + 1); + +} + +/** + * @brief Safely reads a byte from the buffer at the current offset + * + * @param offset The offset into the buffer storage array + */ +uint8_t Buffer::read() { + set_offset(m_offset + 1); + return m_bytes[m_offset - 1]; +} + +/** + * @brief Safely reads a byte from the buffer at the specified offset + * + * @param offset The offset into the buffer storage array + */ +uint8_t Buffer::read(size_t offset) { + + // Prevent writing past the buffer bounds + ASSERT(m_offset + offset < capacity() && offset >= 0, "Buffer overflow"); + + // Set the byte + set_offset(m_offset + 1); + return m_bytes[(m_offset - 1) + offset]; +} + +/** + * @brief Copies all the bytes from another buffer into this buffer. + * + * @param buffer Where to read from + */ +void Buffer::copy_from(const Buffer* buffer) { + + // Copy the buffer + ASSERT(buffer -> capacity() <= capacity(), "Copy exceeds buffer capacity"); + copy_from(buffer -> raw(), buffer -> capacity() ); + +} + +/** + * @brief Copies a range of bytes from another buffer into this buffer. + * + * @param buffer Where to read from + * @param length How much to read + */ +void Buffer::copy_from(const Buffer *buffer, size_t length) { + + // Copy the buffer + ASSERT(length <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_from(buffer -> raw(), length); + +} + +/** + * @brief Copies a range of bytes from another buffer into this buffer at a specified offset. + * @param source Where to read from + * @param length How much to read + * @param offset Where to start writing into this at + */ +void Buffer::copy_from(const Buffer *buffer, size_t length, size_t offset) { + + // Copy the buffer + ASSERT(length <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_from(buffer -> raw(), length, offset); + +} + +/** + * @brief Copies a range of bytes from another buffer into this buffer at a specified offset for both. + * + * @param source Where to read from + * @param length How much to read + * @param offset Where to start writing into this at + * @param offset Where to start reading from the other buffer + */ +void Buffer::copy_from(const Buffer *buffer, size_t length, size_t offset, size_t offset_other) { + + // Copy the buffer + ASSERT(length + offset_other <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_from(buffer -> raw() + offset_other, length, offset); + +} + +/** + * @brief Copies a range of bytes from a source into this buffer. + * + * @param source Where to read from + * @param length How much to read + */ +void Buffer::copy_from(void const *source, size_t length) { + + // Copy the bytes + ASSERT(length + m_offset <= m_capacity, "Copy exceeds buffer capacity"); + memcpy(m_bytes + m_offset, source, length); + set_offset(m_offset + length); +} + +/** + * @brief Copies a range of bytes from a source into this buffer at a specified offset. + * + * @param source Where to read from + * @param length How much to read + * @param offset Where to start writing at + */ +void Buffer::copy_from(void const *source, size_t length, size_t offset) { + + // Copy the bytes + ASSERT((length + offset + m_offset) <= m_capacity, "Copy exceeds buffer capacity"); + memcpy(m_bytes + offset + m_offset, source, length); + set_offset(m_offset + length); + +} + +/** + * @brief Copies all the bytes from this buffer into another buffer. + * + * @param buffer Where to write to + */ +void Buffer::copy_to(Buffer *buffer) { + + // Copy the buffer + ASSERT(capacity() <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_to(buffer -> raw(), capacity()); + +} + +/** + * @brief Writes a range of bytes from this buffer into another buffer. + * + * @param buffer Where to write to + * @param length How much to write + */ +void Buffer::copy_to(Buffer *buffer, size_t length) { + + // Copy the buffer + ASSERT(length <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_to(buffer -> raw(), length); + +} + +/** + * @brief Copies a range of bytes from this buffer into another buffer at a specified offset. + * + * @param source Where to write to + * @param length How much to write + * @param offset Where to start reading at + */ +void Buffer::copy_to(Buffer *buffer, size_t length, size_t offset) { + + // Copy the bytes + ASSERT((length + offset) <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_to(buffer -> raw(), length, offset); + +} + +/** + * @brief Copies a range of bytes from this buffer into another at an offset for each. + * + * @param destination Where to write to + * @param length How much to write + * @param offset Where to start reading from + * @param offset_other Where to start writing to + */ +void Buffer::copy_to(Buffer *buffer, size_t length, size_t offset, size_t offset_other) { + + // Copy the bytes + ASSERT((length + offset) <= buffer -> capacity(), "Copy exceeds external buffer capacity"); + copy_to(buffer -> raw() + offset_other, length, offset); + +} + + +/** + * @brief Copies a range of bytes from this buffer into a source. + * + * @param destination Where to write to + * @param length How much to write + */ +void Buffer::copy_to(void* destination, size_t length) { + + // Copy the bytes + ASSERT(length + m_offset <= (m_capacity), "Copy exceeds buffer capacity"); + memcpy(destination, m_bytes + m_offset, length); + set_offset(m_offset + length); + +} + +/** + * @brief Copies a range of bytes from this buffer at an offset into a source. + * + * @param destination Where to write to + * @param length How much to write + * @param offset Where to start reading from + */ +void Buffer::copy_to(void *destination, size_t length, size_t offset) { + + // Copy the bytes + ASSERT((length + offset + m_offset) <= m_capacity, "Copy exceeds buffer capacity"); + memcpy(destination, m_bytes + offset + m_offset, length); + set_offset(m_offset + length); + +} \ No newline at end of file diff --git a/kernel/src/common/colour.cpp b/kernel/src/common/colour.cpp index c9511289..765acebf 100644 --- a/kernel/src/common/colour.cpp +++ b/kernel/src/common/colour.cpp @@ -9,34 +9,32 @@ using namespace MaxOS::common; Colour::Colour() = default; Colour::Colour(uint8_t red, uint8_t green, uint8_t blue) -: red(red), - green(green), - blue(blue) -{ + : red(red), + green(green), + blue(blue) { } Colour::Colour(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) -: red(red), - green(green), - blue(blue), - alpha(alpha) -{ + : red(red), + green(green), + blue(blue), + alpha(alpha) { } Colour::~Colour() = default; Colour::Colour(ConsoleColour colour) { - parse_console_colour(colour); + parse_console_colour(colour); } -Colour::Colour(MaxOS::string string) { +Colour::Colour(string string) { - if(string[0] == '#') - parse_hex_string(string); - else - parse_ansi_string(string); + if (string[0] == '#') + parse_hex_string(string); + else + parse_ansi_string(string); } @@ -47,19 +45,19 @@ Colour::Colour(MaxOS::string string) { */ void Colour::parse_hex_string(string hex_string) { - // Check if the string is a valid hex string - if(hex_string.length() != 7 || hex_string.length() != 9) - return; + // Check if the string is a valid hex string + if (hex_string.length() != 7 || hex_string.length() != 9) + return; - // Parse the red, green and blue values - red = (hex_string[1] - '0') * 16 + (hex_string[2] - '0'); - green = (hex_string[3] - '0') * 16 + (hex_string[4] - '0'); - blue = (hex_string[5] - '0') * 16 + (hex_string[6] - '0'); + // Parse the red, green and blue values + red = (hex_string[1] - '0') * 16 + (hex_string[2] - '0'); + green = (hex_string[3] - '0') * 16 + (hex_string[4] - '0'); + blue = (hex_string[5] - '0') * 16 + (hex_string[6] - '0'); - // Parse the alpha value - alpha = 255; - if(hex_string.length() == 9) - alpha = (hex_string[7] - '0') * 16 + (hex_string[8] - '0'); + // Parse the alpha value + alpha = 255; + if (hex_string.length() == 9) + alpha = (hex_string[7] - '0') * 16 + (hex_string[8] - '0'); } @@ -71,52 +69,48 @@ void Colour::parse_hex_string(string hex_string) { */ void Colour::parse_ansi_string(string ansi_string) { - // Check if the string is a valid ANSI string - if(ansi_string.length() != 7 || ansi_string[0] != '\033' || ansi_string[1] != '[' || ansi_string[6] != 'm') - return; + // Check if the string is a valid ANSI string + if (ansi_string.length() != 7 || ansi_string[0] != '\033' || ansi_string[1] != '[' || ansi_string[6] != 'm') + return; - // Parse the colour - uint8_t colour = ansi_string[5] - '0'; + // Parse the colour + uint8_t colour = ansi_string[5] - '0'; + switch (colour) { + case 0: + parse_console_colour(ConsoleColour::Black); + break; - // Set the colour - switch (colour) { + case 1: + parse_console_colour(ConsoleColour::Red); + break; - case 0: - parse_console_colour(ConsoleColour::Black); - break; + case 2: + parse_console_colour(ConsoleColour::Green); + break; - case 1: - parse_console_colour(ConsoleColour::Red); - break; + case 3: + parse_console_colour(ConsoleColour::Yellow); + break; - case 2: - parse_console_colour(ConsoleColour::Green); - break; + case 4: + parse_console_colour(ConsoleColour::Blue); + break; - case 3: - parse_console_colour(ConsoleColour::Yellow); - break; + case 5: + parse_console_colour(ConsoleColour::Magenta); + break; - case 4: - parse_console_colour(ConsoleColour::Blue); - break; + case 6: + parse_console_colour(ConsoleColour::Cyan); + break; - case 5: - parse_console_colour(ConsoleColour::Magenta); - break; + case 7: + parse_console_colour(ConsoleColour::White); + break; - case 6: - parse_console_colour(ConsoleColour::Cyan); - break; - - case 7: - parse_console_colour(ConsoleColour::White); - break; - - - default: - break; - } + default: + break; + } } /** @@ -125,106 +119,106 @@ void Colour::parse_ansi_string(string ansi_string) { * @param colour The console colour */ void Colour::parse_console_colour(ConsoleColour colour) { - switch (colour) { - - case ConsoleColour::Uninitialised: - case ConsoleColour::Black: - red = 0; - green = 0; - blue = 0; - break; - - case ConsoleColour::Blue: - red = 0; - green = 128; - blue = 253; - break; - - case ConsoleColour::Green: - red = 0; - green = 170; - blue = 0; - break; - - case ConsoleColour::Cyan: - red = 0; - green = 170; - blue = 170; - break; - - case ConsoleColour::Red: - red = 170; - green = 0; - blue = 0; - break; - - case ConsoleColour::Magenta: - red = 170; - green = 0; - blue = 170; - break; - - case ConsoleColour::Brown: - red = 170; - green = 85; - blue = 0; - break; - - case ConsoleColour::LightGrey: - red = 170; - green = 170; - blue = 170; - break; - - case ConsoleColour::DarkGrey: - red = 85; - green = 85; - blue = 85; - break; - - case ConsoleColour::LightBlue: - red = 85; - green = 85; - blue = 255; - break; - - case ConsoleColour::LightGreen: - red = 85; - green = 255; - blue = 85; - break; - - case ConsoleColour::LightCyan: - red = 85; - green = 255; - blue = 255; - break; - - case ConsoleColour::LightRed: - red = 255; - green = 85; - blue = 85; - break; - - case ConsoleColour::LightMagenta: - red = 255; - green = 85; - blue = 255; - break; - - // Same as CLION yellow - case ConsoleColour::Yellow: - red = 0x96; - green = 0x82; - blue = 0x0E; - break; - - case ConsoleColour::White: - red = 255; - green = 255; - blue = 255; - break; - } + switch (colour) { + + case ConsoleColour::Uninitialised: + case ConsoleColour::Black: + red = 0; + green = 0; + blue = 0; + break; + + case ConsoleColour::Blue: + red = 0; + green = 128; + blue = 253; + break; + + case ConsoleColour::Green: + red = 0; + green = 170; + blue = 0; + break; + + case ConsoleColour::Cyan: + red = 0; + green = 170; + blue = 170; + break; + + case ConsoleColour::Red: + red = 170; + green = 0; + blue = 0; + break; + + case ConsoleColour::Magenta: + red = 170; + green = 0; + blue = 170; + break; + + case ConsoleColour::Brown: + red = 170; + green = 85; + blue = 0; + break; + + case ConsoleColour::LightGrey: + red = 170; + green = 170; + blue = 170; + break; + + case ConsoleColour::DarkGrey: + red = 85; + green = 85; + blue = 85; + break; + + case ConsoleColour::LightBlue: + red = 85; + green = 85; + blue = 255; + break; + + case ConsoleColour::LightGreen: + red = 85; + green = 255; + blue = 85; + break; + + case ConsoleColour::LightCyan: + red = 85; + green = 255; + blue = 255; + break; + + case ConsoleColour::LightRed: + red = 255; + green = 85; + blue = 85; + break; + + case ConsoleColour::LightMagenta: + red = 255; + green = 85; + blue = 255; + break; + + // Same as CLION yellow + case ConsoleColour::Yellow: + red = 0x96; + green = 0x82; + blue = 0x0E; + break; + + case ConsoleColour::White: + red = 255; + green = 255; + blue = 255; + break; + } } /** @@ -235,54 +229,54 @@ void Colour::parse_console_colour(ConsoleColour colour) { ConsoleColour Colour::to_console_colour() const { - if (red == 0 && green == 0 && blue == 0) - return ConsoleColour::Black; + if (red == 0 && green == 0 && blue == 0) + return ConsoleColour::Black; - if (red == 0 && green == 128 && blue == 253) - return ConsoleColour::Blue; + if (red == 0 && green == 128 && blue == 253) + return ConsoleColour::Blue; - if (red == 0 && green == 170 && blue == 0) - return ConsoleColour::Green; + if (red == 0 && green == 170 && blue == 0) + return ConsoleColour::Green; - if (red == 0 && green == 170 && blue == 170) - return ConsoleColour::Cyan; + if (red == 0 && green == 170 && blue == 170) + return ConsoleColour::Cyan; - if (red == 170 && green == 0 && blue == 0) - return ConsoleColour::Red; + if (red == 170 && green == 0 && blue == 0) + return ConsoleColour::Red; - if (red == 170 && green == 0 && blue == 170) - return ConsoleColour::Magenta; + if (red == 170 && green == 0 && blue == 170) + return ConsoleColour::Magenta; - if (red == 170 && green == 85 && blue == 0) - return ConsoleColour::Brown; + if (red == 170 && green == 85 && blue == 0) + return ConsoleColour::Brown; - if (red == 170 && green == 170 && blue == 170) - return ConsoleColour::LightGrey; + if (red == 170 && green == 170 && blue == 170) + return ConsoleColour::LightGrey; - if (red == 85 && green == 85 && blue == 85) - return ConsoleColour::DarkGrey; + if (red == 85 && green == 85 && blue == 85) + return ConsoleColour::DarkGrey; - if (red == 85 && green == 85 && blue == 255) - return ConsoleColour::LightBlue; + if (red == 85 && green == 85 && blue == 255) + return ConsoleColour::LightBlue; - if (red == 85 && green == 255 && blue == 85) - return ConsoleColour::LightGreen; + if (red == 85 && green == 255 && blue == 85) + return ConsoleColour::LightGreen; - if (red == 85 && green == 255 && blue == 255) - return ConsoleColour::LightCyan; + if (red == 85 && green == 255 && blue == 255) + return ConsoleColour::LightCyan; - if (red == 255 && green == 85 && blue == 85) - return ConsoleColour::LightRed; + if (red == 255 && green == 85 && blue == 85) + return ConsoleColour::LightRed; - if (red == 255 && green == 85 && blue == 255) - return ConsoleColour::LightMagenta; + if (red == 255 && green == 85 && blue == 255) + return ConsoleColour::LightMagenta; - if (red == 0x96 && green == 0x82 && blue == 0x0E) - return ConsoleColour::Yellow; + if (red == 0x96 && green == 0x82 && blue == 0x0E) + return ConsoleColour::Yellow; - if (red == 255 && green == 255 && blue == 255) - return ConsoleColour::White; + if (red == 255 && green == 255 && blue == 255) + return ConsoleColour::White; - // Return a default value in case no match is found - return ConsoleColour::Black; + // Return a default value in case no match is found + return ConsoleColour::Black; } diff --git a/kernel/src/common/graphicsContext.cpp b/kernel/src/common/graphicsContext.cpp index 9febe0b8..8946199d 100644 --- a/kernel/src/common/graphicsContext.cpp +++ b/kernel/src/common/graphicsContext.cpp @@ -6,84 +6,83 @@ using namespace MaxOS::common; -GraphicsContext::GraphicsContext() -{ - - - // VirtualBox VGA palette - m_colour_pallet[0x00] = Colour(0x00,0x00,0x00); // Black - m_colour_pallet[0x01] = Colour(0x00,0x00,0xA8); // Duke Blue - m_colour_pallet[0x02] = Colour(0x00,0xA8,0x00); // Islamic Green - m_colour_pallet[0x03] = Colour(0x00,0xA8,0xA8); // Persian Green - m_colour_pallet[0x04] = Colour(0xA8,0x00,0x00); // Dark Candy Apple Red - m_colour_pallet[0x05] = Colour(0xA8,0x00,0xA8); // Heliotrope Magenta - - m_colour_pallet[0x06] = Colour(0xA8,0xA8,0x00); // Light Gold - m_colour_pallet[0x07] = Colour(0xA8,0xA8,0xA8); // Dark Gray (X11) - m_colour_pallet[0x08] = Colour(0x00,0x00,0x57); // Cetacean Blue - m_colour_pallet[0x09] = Colour(0x00,0x00,0xFF); // Blue - m_colour_pallet[0x0A] = Colour(0x00,0xA8,0x57); // Green (Pigment) - m_colour_pallet[0x0B] = Colour(0x00,0xA8,0xFF); // Vivid Cerulean - m_colour_pallet[0x0C] = Colour(0xA8,0x00,0x57); // Jazz berry Jam - m_colour_pallet[0x0D] = Colour(0xA8,0x00,0x57); // Jazz berry Jam - m_colour_pallet[0x0E] = Colour(0xA8,0xA8,0x57); // Olive Green - m_colour_pallet[0x0F] = Colour(0xA8,0xA8,0xFF); // Maximum Blue Purple - - m_colour_pallet[0x10] = Colour(0x00,0x57,0x00); // Dark Green (X11) - m_colour_pallet[0x11] = Colour(0x00,0x57,0xA8); // Cobalt Blue - m_colour_pallet[0x12] = Colour(0x00,0xFF,0x00); // Electric Green - m_colour_pallet[0x13] = Colour(0x00,0xFF,0xA8); // Medium Spring Green - m_colour_pallet[0x14] = Colour(0xA8,0x57,0x00); // Windsor Tan - m_colour_pallet[0x15] = Colour(0xA8,0x57,0xA8); // Purpureus - m_colour_pallet[0x16] = Colour(0xA8,0xFF,0x00); // Spring Bud - m_colour_pallet[0x17] = Colour(0xA8,0xFF,0xA8); // Mint Green - m_colour_pallet[0x18] = Colour(0x00,0x57,0x57); // Midnight Green (Eagle Green) - m_colour_pallet[0x19] = Colour(0x00,0x57,0xFF); // Blue (RYB) - m_colour_pallet[0x1A] = Colour(0x00,0xFF,0x57); // Malachite - m_colour_pallet[0x1B] = Colour(0x00,0xFF,0xFF); // Aqua - m_colour_pallet[0x1C] = Colour(0xA8,0x57,0x57); // Middle Red Purple - m_colour_pallet[0x1D] = Colour(0xA8,0x57,0xFF); // Lavender Indigo - m_colour_pallet[0x1E] = Colour(0xA8,0xFF,0x57); // Olive Green - m_colour_pallet[0x1F] = Colour(0xA8,0xFF,0xFF); // Celeste - - m_colour_pallet[0x20] = Colour(0x57,0x00,0x00); // Blood Red - m_colour_pallet[0x21] = Colour(0x57,0x00,0xA8); // Metallic Violet - m_colour_pallet[0x22] = Colour(0x57,0xA8,0x00); // Kelly Green - m_colour_pallet[0x23] = Colour(0x57,0xA8,0xA8); // Cadet Blue - m_colour_pallet[0x24] = Colour(0xFF,0x00,0x00); // Red - m_colour_pallet[0x25] = Colour(0xFF,0x00,0xA8); // Fashion Fuchsia - m_colour_pallet[0x26] = Colour(0xFF,0xA8,0x00); // Chrome Yellow - m_colour_pallet[0x27] = Colour(0xFF,0xA8,0xA8); // Light Salmon Pink - m_colour_pallet[0x28] = Colour(0x57,0x00,0x57); // Imperial Purple - m_colour_pallet[0x29] = Colour(0x57,0x00,0xFF); // Electric Indigo - m_colour_pallet[0x2A] = Colour(0x57,0xA8,0x57); // Apple - m_colour_pallet[0x2B] = Colour(0x57,0xA8,0xFF); // Blue Jeans - m_colour_pallet[0x2C] = Colour(0xFF,0x00,0x57); // Folly - m_colour_pallet[0x2D] = Colour(0xFF,0x00,0xFF); // Fuchsia - m_colour_pallet[0x2E] = Colour(0xFF,0xA8,0x57); // Rajah - m_colour_pallet[0x2F] = Colour(0xFF,0xA8,0xFF); // Rich Brilliant Lavender - - m_colour_pallet[0x30] = Colour(0x57,0x57,0x00); // Dark Bronze (Coin) - m_colour_pallet[0x31] = Colour(0x57,0x57,0xA8); // Liberty - m_colour_pallet[0x32] = Colour(0x57,0xFF,0x00); // Chlorophyll Green - m_colour_pallet[0x33] = Colour(0x57,0xFF,0xA8); // Medium Aquamarine - m_colour_pallet[0x34] = Colour(0xFF,0x57,0x00); // Orange (Pantone) - m_colour_pallet[0x35] = Colour(0xFF,0x57,0xA8); // Brilliant Rose - m_colour_pallet[0x36] = Colour(0xFF,0xFF,0x00); // Yellow - m_colour_pallet[0x37] = Colour(0xFF,0xFF,0xA8); // Calamansi - m_colour_pallet[0x38] = Colour(0x57,0x57,0x57); // Davy's Grey - m_colour_pallet[0x39] = Colour(0x57,0x57,0xFF); // Very Light Blue - m_colour_pallet[0x3A] = Colour(0x57,0xFF,0x57); // Screamin' Green - m_colour_pallet[0x3B] = Colour(0x57,0xFF,0xFF); // Electric Blue - m_colour_pallet[0x3C] = Colour(0xFF,0x57,0x57); // Sunset Orange - m_colour_pallet[0x3D] = Colour(0xFF,0x57,0xFF); // Shocking Pink (Crayola) - m_colour_pallet[0x3E] = Colour(0xFF,0xFF,0x57); // Shocking Pink (Crayola) - m_colour_pallet[0x3F] = Colour(0xFF,0xFF,0xFF); // White - - - // Set the rest of the palette to black - for(uint8_t color_code = 255; color_code >= 0x40; --color_code) - m_colour_pallet[color_code] = Colour(0,0,0); +GraphicsContext::GraphicsContext() { + + + // VirtualBox VGA palette + m_colour_pallet[0x00] = Colour(0x00, 0x00, 0x00); // Black + m_colour_pallet[0x01] = Colour(0x00, 0x00, 0xA8); // Duke Blue + m_colour_pallet[0x02] = Colour(0x00, 0xA8, 0x00); // Islamic Green + m_colour_pallet[0x03] = Colour(0x00, 0xA8, 0xA8); // Persian Green + m_colour_pallet[0x04] = Colour(0xA8, 0x00, 0x00); // Dark Candy Apple Red + m_colour_pallet[0x05] = Colour(0xA8, 0x00, 0xA8); // Heliotrope Magenta + + m_colour_pallet[0x06] = Colour(0xA8, 0xA8, 0x00); // Light Gold + m_colour_pallet[0x07] = Colour(0xA8, 0xA8, 0xA8); // Dark Gray (X11) + m_colour_pallet[0x08] = Colour(0x00, 0x00, 0x57); // Cetacean Blue + m_colour_pallet[0x09] = Colour(0x00, 0x00, 0xFF); // Blue + m_colour_pallet[0x0A] = Colour(0x00, 0xA8, 0x57); // Green (Pigment) + m_colour_pallet[0x0B] = Colour(0x00, 0xA8, 0xFF); // Vivid Cerulean + m_colour_pallet[0x0C] = Colour(0xA8, 0x00, 0x57); // Jazz berry Jam + m_colour_pallet[0x0D] = Colour(0xA8, 0x00, 0x57); // Jazz berry Jam + m_colour_pallet[0x0E] = Colour(0xA8, 0xA8, 0x57); // Olive Green + m_colour_pallet[0x0F] = Colour(0xA8, 0xA8, 0xFF); // Maximum Blue Purple + + m_colour_pallet[0x10] = Colour(0x00, 0x57, 0x00); // Dark Green (X11) + m_colour_pallet[0x11] = Colour(0x00, 0x57, 0xA8); // Cobalt Blue + m_colour_pallet[0x12] = Colour(0x00, 0xFF, 0x00); // Electric Green + m_colour_pallet[0x13] = Colour(0x00, 0xFF, 0xA8); // Medium Spring Green + m_colour_pallet[0x14] = Colour(0xA8, 0x57, 0x00); // Windsor Tan + m_colour_pallet[0x15] = Colour(0xA8, 0x57, 0xA8); // Purpureus + m_colour_pallet[0x16] = Colour(0xA8, 0xFF, 0x00); // Spring Bud + m_colour_pallet[0x17] = Colour(0xA8, 0xFF, 0xA8); // Mint Green + m_colour_pallet[0x18] = Colour(0x00, 0x57, 0x57); // Midnight Green (Eagle Green) + m_colour_pallet[0x19] = Colour(0x00, 0x57, 0xFF); // Blue (RYB) + m_colour_pallet[0x1A] = Colour(0x00, 0xFF, 0x57); // Malachite + m_colour_pallet[0x1B] = Colour(0x00, 0xFF, 0xFF); // Aqua + m_colour_pallet[0x1C] = Colour(0xA8, 0x57, 0x57); // Middle Red Purple + m_colour_pallet[0x1D] = Colour(0xA8, 0x57, 0xFF); // Lavender Indigo + m_colour_pallet[0x1E] = Colour(0xA8, 0xFF, 0x57); // Olive Green + m_colour_pallet[0x1F] = Colour(0xA8, 0xFF, 0xFF); // Celeste + + m_colour_pallet[0x20] = Colour(0x57, 0x00, 0x00); // Blood Red + m_colour_pallet[0x21] = Colour(0x57, 0x00, 0xA8); // Metallic Violet + m_colour_pallet[0x22] = Colour(0x57, 0xA8, 0x00); // Kelly Green + m_colour_pallet[0x23] = Colour(0x57, 0xA8, 0xA8); // Cadet Blue + m_colour_pallet[0x24] = Colour(0xFF, 0x00, 0x00); // Red + m_colour_pallet[0x25] = Colour(0xFF, 0x00, 0xA8); // Fashion Fuchsia + m_colour_pallet[0x26] = Colour(0xFF, 0xA8, 0x00); // Chrome Yellow + m_colour_pallet[0x27] = Colour(0xFF, 0xA8, 0xA8); // Light Salmon Pink + m_colour_pallet[0x28] = Colour(0x57, 0x00, 0x57); // Imperial Purple + m_colour_pallet[0x29] = Colour(0x57, 0x00, 0xFF); // Electric Indigo + m_colour_pallet[0x2A] = Colour(0x57, 0xA8, 0x57); // Apple + m_colour_pallet[0x2B] = Colour(0x57, 0xA8, 0xFF); // Blue Jeans + m_colour_pallet[0x2C] = Colour(0xFF, 0x00, 0x57); // Folly + m_colour_pallet[0x2D] = Colour(0xFF, 0x00, 0xFF); // Fuchsia + m_colour_pallet[0x2E] = Colour(0xFF, 0xA8, 0x57); // Rajah + m_colour_pallet[0x2F] = Colour(0xFF, 0xA8, 0xFF); // Rich Brilliant Lavender + + m_colour_pallet[0x30] = Colour(0x57, 0x57, 0x00); // Dark Bronze (Coin) + m_colour_pallet[0x31] = Colour(0x57, 0x57, 0xA8); // Liberty + m_colour_pallet[0x32] = Colour(0x57, 0xFF, 0x00); // Chlorophyll Green + m_colour_pallet[0x33] = Colour(0x57, 0xFF, 0xA8); // Medium Aquamarine + m_colour_pallet[0x34] = Colour(0xFF, 0x57, 0x00); // Orange (Pantone) + m_colour_pallet[0x35] = Colour(0xFF, 0x57, 0xA8); // Brilliant Rose + m_colour_pallet[0x36] = Colour(0xFF, 0xFF, 0x00); // Yellow + m_colour_pallet[0x37] = Colour(0xFF, 0xFF, 0xA8); // Calamansi + m_colour_pallet[0x38] = Colour(0x57, 0x57, 0x57); // Davy's Grey + m_colour_pallet[0x39] = Colour(0x57, 0x57, 0xFF); // Very Light Blue + m_colour_pallet[0x3A] = Colour(0x57, 0xFF, 0x57); // Screamin' Green + m_colour_pallet[0x3B] = Colour(0x57, 0xFF, 0xFF); // Electric Blue + m_colour_pallet[0x3C] = Colour(0xFF, 0x57, 0x57); // Sunset Orange + m_colour_pallet[0x3D] = Colour(0xFF, 0x57, 0xFF); // Shocking Pink (Crayola) + m_colour_pallet[0x3E] = Colour(0xFF, 0xFF, 0x57); // Shocking Pink (Crayola) + m_colour_pallet[0x3F] = Colour(0xFF, 0xFF, 0xFF); // White + + + // Set the rest of the palette to black + for (uint8_t color_code = 255; color_code >= 0x40; --color_code) + m_colour_pallet[color_code] = Colour(0, 0, 0); } @@ -99,21 +98,21 @@ GraphicsContext::~GraphicsContext() = default; */ void GraphicsContext::render_pixel(uint32_t x, uint32_t y, uint32_t colour) { - // Call the correct put_pixel function based on the color depth - switch (m_color_depth) { - case 8: - render_pixel_8_bit(x, y, colour); - break; - case 16: - render_pixel_16_bit(x, y, colour); - break; - case 24: - render_pixel_24_bit(x, y, colour); - break; - case 32: - render_pixel_32_bit(x, y, colour); - break; - } + // Call the correct put_pixel function based on the color depth + switch (m_color_depth) { + case 8: + render_pixel_8_bit(x, y, colour); + break; + case 16: + render_pixel_16_bit(x, y, colour); + break; + case 24: + render_pixel_24_bit(x, y, colour); + break; + case 32: + render_pixel_32_bit(x, y, colour); + break; + } } @@ -170,19 +169,19 @@ void GraphicsContext::render_pixel_32_bit(uint32_t, uint32_t, uint32_t) { * @return The colour of the pixel or white if the pixel is not supported */ uint32_t GraphicsContext::get_rendered_pixel(uint32_t x, uint32_t y) { - // Call the correct get_pixel function based on the color depth - switch (m_color_depth) { - case 8: - return get_rendered_pixel_8_bit(x, y); - case 16: - return get_rendered_pixel_16_bit(x, y); - case 24: - return get_rendered_pixel_24_bit(x, y); - case 32: - return get_rendered_pixel_32_bit(x, y); - } - - return colour_to_int(Colour(0xFF, 0xFF, 0xFF)); + // Call the correct get_pixel function based on the color depth + switch (m_color_depth) { + case 8: + return get_rendered_pixel_8_bit(x, y); + case 16: + return get_rendered_pixel_16_bit(x, y); + case 24: + return get_rendered_pixel_24_bit(x, y); + case 32: + return get_rendered_pixel_32_bit(x, y); + } + + return colour_to_int(Colour(0xFF, 0xFF, 0xFF)); } /** @@ -193,7 +192,7 @@ uint32_t GraphicsContext::get_rendered_pixel(uint32_t x, uint32_t y) { * @return The 8Bit colour of the pixel */ uint8_t GraphicsContext::get_rendered_pixel_8_bit(uint32_t, uint32_t) { - return 0; + return 0; } /** @@ -204,7 +203,7 @@ uint8_t GraphicsContext::get_rendered_pixel_8_bit(uint32_t, uint32_t) { * @return The 16Bit colour of the pixel */ uint16_t GraphicsContext::get_rendered_pixel_16_bit(uint32_t, uint32_t) { - return 0; + return 0; } /** @@ -215,7 +214,7 @@ uint16_t GraphicsContext::get_rendered_pixel_16_bit(uint32_t, uint32_t) { * @return The 24Bit colour of the pixel */ uint32_t GraphicsContext::get_rendered_pixel_24_bit(uint32_t, uint32_t) { - return 0; + return 0; } /** @@ -226,7 +225,7 @@ uint32_t GraphicsContext::get_rendered_pixel_24_bit(uint32_t, uint32_t) { * @return The 32Bit colour of the pixel */ uint32_t GraphicsContext::get_rendered_pixel_32_bit(uint32_t, uint32_t) { - return 0; + return 0; } /** @@ -235,56 +234,49 @@ uint32_t GraphicsContext::get_rendered_pixel_32_bit(uint32_t, uint32_t) { * @param colour The colour class to convert * @return The integer value of the colour */ -uint32_t GraphicsContext::colour_to_int(const Colour& colour) { - - switch(m_color_depth) - { - case 8: - { - uint32_t result = 0; - int mindistance = 0xfffffff; - for(uint32_t i = 0; i <= 255; ++i) - { - Colour* c = &m_colour_pallet[i]; - int distance = - ((int)colour.red-(int)c->red)*((int)colour.red-(int)c->red) - +((int)colour.green-(int)c->green)*((int)colour.green-(int)c->green) - +((int)colour.blue-(int)c->blue)*((int)colour.blue-(int)c->blue); - if(distance < mindistance) - { - mindistance = distance; - result = i; - } - } - return result; - } - case 16: - { - // 16-Bit colours RRRRRGGGGGGBBBBB - return ((uint16_t)(colour.red & 0xF8)) << 8 - | ((uint16_t)(colour.green & 0xFC)) << 3 - | ((uint16_t)(colour.blue & 0xF8) >> 3); - } - case 24: - { - return (uint32_t)colour.red << 16 - | (uint32_t)colour.green << 8 - | (uint32_t)colour.blue; - } - default: - case 32: - { - uint32_t red_hex = ((uint32_t)colour.red & 0xFF) << 16; - uint32_t green_hex = ((uint32_t)colour.green & 0xFF) << 8; - uint32_t blue_hex = (uint32_t)colour.blue & 0xFF; - uint32_t alpha_hex = ((uint32_t)colour.alpha & 0xFF) << 24; - - uint32_t hexValue = red_hex | green_hex | blue_hex | alpha_hex; - - - return hexValue; - } - } +uint32_t GraphicsContext::colour_to_int(const Colour &colour) { + + switch (m_color_depth) { + case 8: { + uint32_t result = 0; + int mindistance = 0xfffffff; + for (uint32_t i = 0; i <= 255; ++i) { + Colour *c = &m_colour_pallet[i]; + int distance = + ((int) colour.red - (int) c->red) * ((int) colour.red - (int) c->red) + + ((int) colour.green - (int) c->green) * ((int) colour.green - (int) c->green) + + ((int) colour.blue - (int) c->blue) * ((int) colour.blue - (int) c->blue); + if (distance < mindistance) { + mindistance = distance; + result = i; + } + } + return result; + } + case 16: { + // 16-Bit colours RRRRRGGGGGGBBBBB + return ((uint16_t) (colour.red & 0xF8)) << 8 + | ((uint16_t) (colour.green & 0xFC)) << 3 + | ((uint16_t) (colour.blue & 0xF8) >> 3); + } + case 24: { + return (uint32_t) colour.red << 16 + | (uint32_t) colour.green << 8 + | (uint32_t) colour.blue; + } + default: + case 32: { + uint32_t red_hex = ((uint32_t) colour.red & 0xFF) << 16; + uint32_t green_hex = ((uint32_t) colour.green & 0xFF) << 8; + uint32_t blue_hex = (uint32_t) colour.blue & 0xFF; + uint32_t alpha_hex = ((uint32_t) colour.alpha & 0xFF) << 24; + + uint32_t hexValue = red_hex | green_hex | blue_hex | alpha_hex; + + + return hexValue; + } + } } /** @@ -294,53 +286,49 @@ uint32_t GraphicsContext::colour_to_int(const Colour& colour) { * @return The colour class of the integer value */ Colour GraphicsContext::int_to_colour(uint32_t colour) { - switch (m_color_depth) { + switch (m_color_depth) { - case 8: - { - // Return the colour from the palette - return m_colour_pallet[colour & 0xFF]; - } + case 8: { + // Return the colour from the palette + return m_colour_pallet[colour & 0xFF]; + } - case 16: - { - // 16-Bit Colour: 5 bits for red, 6 bits for green, 5 bits for blue (RRRRR,GGGGGG,BBBBB) - Colour result; + case 16: { + // 16-Bit Colour: 5 bits for red, 6 bits for green, 5 bits for blue (RRRRR,GGGGGG,BBBBB) + Colour result; - result.red = (colour & 0xF800) >> 8; - result.green = (colour & 0x07E0) >> 3; - result.blue = (colour & 0x001F) << 3; + result.red = (colour & 0xF800) >> 8; + result.green = (colour & 0x07E0) >> 3; + result.blue = (colour & 0x001F) << 3; - return result; - } + return result; + } - case 24: - { - // 24-Bit Colour: 8 bits for red, 8 bits for green, 8 bits for blue (RRRRRRRR,GGGGGGGG,BBBBBBBB) - Colour result; + case 24: { + // 24-Bit Colour: 8 bits for red, 8 bits for green, 8 bits for blue (RRRRRRRR,GGGGGGGG,BBBBBBBB) + Colour result; - result.red = (colour & 0xFF0000) >> 16; - result.green = (colour & 0x00FF00) >> 8; - result.blue = (colour & 0x0000FF); + result.red = (colour & 0xFF0000) >> 16; + result.green = (colour & 0x00FF00) >> 8; + result.blue = (colour & 0x0000FF); - return result; - } + return result; + } - default: - case 32: - { - Colour result; + default: + case 32: { + Colour result; - uint32_t hex_value = colour; - result.red = (hex_value >> 16) & 0xFF; - result.green = (hex_value >> 8) & 0xFF; - result.blue = hex_value & 0xFF; - result.alpha = (hex_value >> 24) & 0xFF; + uint32_t hex_value = colour; + result.red = (hex_value >> 16) & 0xFF; + result.green = (hex_value >> 8) & 0xFF; + result.blue = hex_value & 0xFF; + result.alpha = (hex_value >> 24) & 0xFF; - return result; + return result; - } - } + } + } } /** @@ -349,7 +337,7 @@ Colour GraphicsContext::int_to_colour(uint32_t colour) { * @return The width of the screen */ uint32_t GraphicsContext::width() const { - return m_width; + return m_width; } /** @@ -358,7 +346,7 @@ uint32_t GraphicsContext::width() const { * @return The height of the screen */ uint32_t GraphicsContext::height() const { - return m_height; + return m_height; } /** @@ -366,7 +354,7 @@ uint32_t GraphicsContext::height() const { * @return The color depth */ uint32_t GraphicsContext::color_depth() const { - return m_color_depth; + return m_color_depth; } /** @@ -376,10 +364,10 @@ uint32_t GraphicsContext::color_depth() const { * @param y The y coordinate of the pixel * @param colour The colour of the pixel */ -void GraphicsContext::put_pixel(int32_t x, int32_t y, const Colour& colour) { +void GraphicsContext::put_pixel(int32_t x, int32_t y, const Colour &colour) { - // Convert the colour to an integer and then print it - putPixel(x,y, colour_to_int(colour)); + // Convert the colour to an integer and then print it + putPixel(x, y, colour_to_int(colour)); } /** @@ -391,17 +379,17 @@ void GraphicsContext::put_pixel(int32_t x, int32_t y, const Colour& colour) { */ void GraphicsContext::putPixel(int32_t x, int32_t y, uint32_t colour) { - if (0 > x || (uint32_t)x >= m_width) { - return; - } + if (0 > x || (uint32_t) x >= m_width) { + return; + } - // Check if the pixel is within the m_height of the screen - if (0 > y || (uint32_t) y >= m_height) { - return; - } + // Check if the pixel is within the m_height of the screen + if (0 > y || (uint32_t) y >= m_height) { + return; + } - // Render the pixel - render_pixel(x, mirror_y_axis ? m_height - y - 1 : y, colour); + // Render the pixel + render_pixel(x, mirror_y_axis ? m_height - y - 1 : y, colour); } @@ -414,21 +402,13 @@ void GraphicsContext::putPixel(int32_t x, int32_t y, uint32_t colour) { */ Colour GraphicsContext::get_pixel(int32_t x, int32_t y) { - // Check if the pixel is within the m_width of the screen - if (0 > x || (uint32_t)x >= m_width) { - return {0,0,0}; - } - - // Check if the pixel is within the m_height of the screen - if (0 > y || (uint32_t) y >= m_height) { - return {0,0,0}; - } - - // Get the pixel and convert it to a colour - uint32_t translated_color = get_rendered_pixel(x, mirror_y_axis ? m_height - y - 1 : y); - return int_to_colour(translated_color); - + // Check if the pixel is within the bounds of the screen + if (0 > x || (uint32_t) x >= m_width || 0 > y || (uint32_t) y >= m_height) + return {0, 0, 0}; + // Get the pixel and convert it to a colour + uint32_t translated_color = get_rendered_pixel(x, mirror_y_axis ? m_height - y - 1 : y); + return int_to_colour(translated_color); } /** @@ -438,16 +418,17 @@ Colour GraphicsContext::get_pixel(int32_t x, int32_t y) { * @param y The y coordinate of the pixel */ void GraphicsContext::invert_pixel(int32_t x, int32_t y) { - // Get the pixel - Colour colour = get_pixel(x, y); - // Invert the pixel - colour.red = 255 - colour.red; - colour.green = 255 - colour.green; - colour.blue = 255 - colour.blue; + // Get the pixel + Colour colour = get_pixel(x, y); + + // Invert the pixel + colour.red = 255 - colour.red; + colour.green = 255 - colour.green; + colour.blue = 255 - colour.blue; - // Render the pixel - put_pixel(x, y, colour); + // Render the pixel + put_pixel(x, y, colour); } @@ -460,8 +441,8 @@ void GraphicsContext::invert_pixel(int32_t x, int32_t y) { * @param y1 The y coordinate of the final point * @param colour The colour of the line */ -void GraphicsContext::draw_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour& colour) { - drawLine(x0,y0,x1,y1, colour_to_int(colour)); +void GraphicsContext::draw_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour &colour) { + drawLine(x0, y0, x1, y1, colour_to_int(colour)); } /** @@ -475,87 +456,82 @@ void GraphicsContext::draw_line(int32_t x0, int32_t y0, int32_t x1, int32_t y1, */ void GraphicsContext::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t colour) { - // Store the minimum and maximum y values - bool y_0_is_smaller = y0 < y1; - int32_t y_min = y_0_is_smaller ? y0 : y1; - int32_t y_max = y_0_is_smaller ? y1 : y0; - - //Reverse the points to draw from left to right - if(x1 < x0){ - drawLine(x1,y1,x0,y0,colour); - return; - } - - // Vertical line - if(x1 == x0) - { - // Force the line to be within the screen - if(y_min < 0) y_min = 0; - if(y_max >= m_height) - y_max = m_height - 1; - - // Mirror the Y axis as directly calling put_pixel will not do this - if(mirror_y_axis) - { - int32_t temp = y_max; - y_max = m_height - y_min - 1; - y_min = m_height - temp - 1; - } - - // Check that the line is within the screen - if (0 > x0 || (uint32_t) x0 >= m_width) { - return; - } - - // Draw the line - for(int32_t y = y_min; y <= y_max; ++y) - putPixel(x0, y, colour); - - return; - } - - // Horizontal line - if(y1 == y0) - { - // Ensure the line is within the screen - if(x0 < 0) x0 = 0; - if(x1 >= m_width) x1 = m_width -1; - - // Mirror the Y axis as directly calling put_pixel will not do this - if(mirror_y_axis) - y0 = m_height -y0-1; - - // Check that the line is within the screen - if (0 > y0 || y0 >= m_height) - return; - - // Draw the line - for(int32_t x = x0; x <= x1; ++x) - putPixel(x,y0,colour); - } - - // If the line is not horizontal or vertical then it must be a diagonal line - // Find the slope of the line - float slope = ((float)(y1-y0))/(x1-x0); - - // A slope that is more horizontal should be drawn by incrementing x - if(-1 <= slope && slope <= 1) - { - float y = y0; - for(int32_t x = x0; x <= x1; x++, y+=slope) - putPixel(x, (int32_t)y, colour); - } - - // A slope that is more vertical should be drawn by incrementing y - else - { - // Invert the slope - slope = 1.0f/slope; - - float x = x0; - for(int32_t y = y_min; y <= y_max; x+=slope, y++) - putPixel((int32_t)x, y, colour); - } + // Store the minimum and maximum y values + bool y_0_is_smaller = y0 < y1; + int32_t y_min = y_0_is_smaller ? y0 : y1; + int32_t y_max = y_0_is_smaller ? y1 : y0; + + //Reverse the points to draw from left to right + if (x1 < x0) { + drawLine(x1, y1, x0, y0, colour); + return; + } + + // Vertical line + if (x1 == x0) { + // Force the line to be within the screen + if (y_min < 0) y_min = 0; + if (y_max >= m_height) + y_max = m_height - 1; + + // Mirror the Y axis as directly calling put_pixel will not do this + if (mirror_y_axis) { + int32_t temp = y_max; + y_max = m_height - y_min - 1; + y_min = m_height - temp - 1; + } + + // Check that the line is within the screen + if (0 > x0 || (uint32_t) x0 >= m_width) { + return; + } + + // Draw the line + for (int32_t y = y_min; y <= y_max; ++y) + putPixel(x0, y, colour); + + return; + } + + // Horizontal line + if (y1 == y0) { + // Ensure the line is within the screen + if (x0 < 0) x0 = 0; + if (x1 >= m_width) x1 = m_width - 1; + + // Mirror the Y axis as directly calling put_pixel will not do this + if (mirror_y_axis) + y0 = m_height - y0 - 1; + + // Check that the line is within the screen + if (0 > y0 || y0 >= m_height) + return; + + // Draw the line + for (int32_t x = x0; x <= x1; ++x) + putPixel(x, y0, colour); + } + + // If the line is not horizontal or vertical then it must be a diagonal line + // Find the slope of the line + float slope = ((float) (y1 - y0)) / (x1 - x0); + + // A slope that is more horizontal should be drawn by incrementing x + if (-1 <= slope && slope <= 1) { + float y = y0; + for (int32_t x = x0; x <= x1; x++, y += slope) + putPixel(x, (int32_t) y, colour); + } + + // A slope that is more vertical should be drawn by incrementing y + else { + // Invert the slope + slope = 1.0f / slope; + + float x = x0; + for (int32_t y = y_min; y <= y_max; x += slope, y++) + putPixel((int32_t) x, y, colour); + } } /** @@ -567,8 +543,8 @@ void GraphicsContext::drawLine(int32_t x0, int32_t y0, int32_t x1, int32_t y1, u * @param y1 The y coordinate of the bottom right corner * @param colour The colour of the rectangle */ -void GraphicsContext::draw_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour& colour) { - draw_rectangle(x0, y0, x1, y1, colour_to_int(colour)); +void GraphicsContext::draw_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour &colour) { + draw_rectangle(x0, y0, x1, y1, colour_to_int(colour)); } @@ -583,15 +559,15 @@ void GraphicsContext::draw_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t */ void GraphicsContext::draw_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t colour) { - // Ensure x and y 0 is smaller than x and y 1 - --y0; - --x0; + // Ensure x and y 0 is smaller than x and y 1 + --y0; + --x0; - // Draw the rectangle - drawLine(x0,y0,x1,y0,colour); // Top - drawLine(x0,y1,x1,y1,colour); // Bottom - drawLine(x0,y0,x0,y1,colour); // Left - drawLine(x1,y0,x1,y1,colour); // Right + // Draw the rectangle + drawLine(x0, y0, x1, y0, colour); // Top + drawLine(x0, y1, x1, y1, colour); // Bottom + drawLine(x0, y0, x0, y1, colour); // Left + drawLine(x1, y0, x1, y1, colour); // Right } @@ -604,8 +580,8 @@ void GraphicsContext::draw_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t * @param y1 The y coordinate of the bottom right corner * @param colour The colour of the rectangle */ -void GraphicsContext::fill_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour& colour) { - fill_rectangle(x0, y0, x1, y1, colour_to_int(colour)); +void GraphicsContext::fill_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, const Colour &colour) { + fill_rectangle(x0, y0, x1, y1, colour_to_int(colour)); } /** @@ -619,39 +595,38 @@ void GraphicsContext::fill_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t */ void GraphicsContext::fill_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint32_t colour) { - // Draw from left to right - if(y1 < y0){ - fill_rectangle(x1, y1, x0, y0, colour); - return; - } - - // Make sure the rectangle is within the height of the screen - if(y0 < 0) y0 = 0; - if(y1 > m_height) y1 = m_height; - - // Make sure the rectangle is within the width of the screen - bool x_0_is_smaller = x0 < x1; - int32_t x_min = x_0_is_smaller ? x0 : x1; - int32_t x_max = x_0_is_smaller ? x1 : x0; - - if(x_min < 0) x_min = 0; - if(x_max > m_width) - x_max = m_width; - - // Mirror the Y axis as directly calling put_pixel will not do this - if(mirror_y_axis) - { - int32_t temp = y1; - y1 = m_height - y0 - 1; - y0 = m_height - temp - 1; - } - - // Draw the rectangle - for(int32_t y = y0; y < y1; ++y){ - for (int32_t x = x_min; x < x_max; ++x) { - putPixel(x, y, colour); - } - } + // Draw from left to right + if (y1 < y0) { + fill_rectangle(x1, y1, x0, y0, colour); + return; + } + + // Make sure the rectangle is within the height of the screen + if (y0 < 0) y0 = 0; + if (y1 > m_height) y1 = m_height; + + // Make sure the rectangle is within the width of the screen + bool x_0_is_smaller = x0 < x1; + int32_t x_min = x_0_is_smaller ? x0 : x1; + int32_t x_max = x_0_is_smaller ? x1 : x0; + + if (x_min < 0) x_min = 0; + if (x_max > m_width) + x_max = m_width; + + // Mirror the Y axis as directly calling put_pixel will not do this + if (mirror_y_axis) { + int32_t temp = y1; + y1 = m_height - y0 - 1; + y0 = m_height - temp - 1; + } + + // Draw the rectangle + for (int32_t y = y0; y < y1; ++y) { + for (int32_t x = x_min; x < x_max; ++x) { + putPixel(x, y, colour); + } + } } @@ -663,8 +638,8 @@ void GraphicsContext::fill_rectangle(int32_t x0, int32_t y0, int32_t x1, int32_t * @param radius The radius of the circle * @param colour The colour of the circle */ -void GraphicsContext::draw_circle(int32_t x0, int32_t y0, int32_t radius, const Colour& colour){ - draw_circle(x0, y0, radius, colour_to_int(colour)); +void GraphicsContext::draw_circle(int32_t x0, int32_t y0, int32_t radius, const Colour &colour) { + draw_circle(x0, y0, radius, colour_to_int(colour)); } /** @@ -677,28 +652,28 @@ void GraphicsContext::draw_circle(int32_t x0, int32_t y0, int32_t radius, const */ void GraphicsContext::draw_circle(int32_t x0, int32_t y0, int32_t radius, uint32_t colour) { - // Make sure the circle is with in the width and height of the screen - if(x0 < 0) x0 = 0; - if(x0 > m_width) x0 = m_width; - if(y0 < 0) y0 = 0; - if(y0 > m_height) y0 = m_height; + // Make sure the circle is with in the width and height of the screen + if (x0 < 0) x0 = 0; + if (x0 > m_width) x0 = m_width; + if (y0 < 0) y0 = 0; + if (y0 > m_height) y0 = m_height; - // Mirror the Y axis as directly calling put_pixel will not do this - if(mirror_y_axis) - y0 = m_height -y0-1; + // Mirror the Y axis as directly calling put_pixel will not do this + if (mirror_y_axis) + y0 = m_height - y0 - 1; - // Begin drawing at the left most point of the circle and draw a line to the right most point of the circle - for(int32_t x = -radius; x <= radius; ++x){ + // Begin drawing at the left most point of the circle and draw a line to the right most point of the circle + for (int32_t x = -radius; x <= radius; ++x) { - // Draw a line from the top most point of the circle to the bottom most point of the circle - for(int32_t y = -radius; y <= radius; ++y){ + // Draw a line from the top most point of the circle to the bottom most point of the circle + for (int32_t y = -radius; y <= radius; ++y) { - // If the point is within the circle, draw it but make sure it is only part of the outline - if(x*x + y*y <= radius*radius && x*x + y*y >= (radius-1)*(radius-1)) - putPixel(x0+x,y0+y,colour); - } - } + // If the point is within the circle, draw it but make sure it is only part of the outline + if (x * x + y * y <= radius * radius && x * x + y * y >= (radius - 1) * (radius - 1)) + putPixel(x0 + x, y0 + y, colour); + } + } } @@ -711,8 +686,8 @@ void GraphicsContext::draw_circle(int32_t x0, int32_t y0, int32_t radius, uint32 * @param radius The radius of the circle * @param colour The colour of the circle */ -void GraphicsContext::fill_circle(int32_t x0, int32_t y0, int32_t radius, const Colour& colour) { - fillCircle(x0,y0,radius, colour_to_int(colour)); +void GraphicsContext::fill_circle(int32_t x0, int32_t y0, int32_t radius, const Colour &colour) { + fillCircle(x0, y0, radius, colour_to_int(colour)); } @@ -726,29 +701,29 @@ void GraphicsContext::fill_circle(int32_t x0, int32_t y0, int32_t radius, const */ void GraphicsContext::fillCircle(int32_t x0, int32_t y0, int32_t radius, uint32_t colour) { - // Make sure the circle is with in the width and height of the screen - if(x0 < 0) x0 = 0; - if(x0 > m_width) x0 = m_width; - if(y0 < 0) y0 = 0; - if(y0 > m_height) y0 = m_height; + // Make sure the circle is with in the width and height of the screen + if (x0 < 0) x0 = 0; + if (x0 > m_width) x0 = m_width; + if (y0 < 0) y0 = 0; + if (y0 > m_height) y0 = m_height; - // Mirror the Y axis as directly calling put_pixel will not do this - if(mirror_y_axis) - y0 = m_height -y0-1; + // Mirror the Y axis as directly calling put_pixel will not do this + if (mirror_y_axis) + y0 = m_height - y0 - 1; - // Draw the circle + // Draw the circle - // Begin drawing at the left most point of the circle and draw a line to the right most point of the circle - for(int32_t x = -radius; x <= radius; ++x){ + // Begin drawing at the left most point of the circle and draw a line to the right most point of the circle + for (int32_t x = -radius; x <= radius; ++x) { - // Draw a line from the top most point of the circle to the bottom most point of the circle - for(int32_t y = -radius; y <= radius; ++y){ + // Draw a line from the top most point of the circle to the bottom most point of the circle + for (int32_t y = -radius; y <= radius; ++y) { - // Only draw the pixel if it is within the circle - if(x*x + y*y <= radius*radius) - putPixel(x0+x,y0+y,colour); - } - } + // Only draw the pixel if it is within the circle + if (x * x + y * y <= radius * radius) + putPixel(x0 + x, y0 + y, colour); + } + } } /** @@ -756,6 +731,6 @@ void GraphicsContext::fillCircle(int32_t x0, int32_t y0, int32_t radius, uint32_ * * @return The framebuffer address */ -uint64_t* GraphicsContext::framebuffer_address() { - return m_framebuffer_address; +uint64_t *GraphicsContext::framebuffer_address() { + return m_framebuffer_address; } diff --git a/kernel/src/common/logger.cpp b/kernel/src/common/logger.cpp index 52f1c421..59bb0004 100644 --- a/kernel/src/common/logger.cpp +++ b/kernel/src/common/logger.cpp @@ -4,18 +4,21 @@ #include #include #include +#include +#include using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::drivers::console; - Logger::Logger() -:m_log_writers() +: m_log_writers() { - // Set the logger to this - s_active_logger = this; + s_active_logger = this; + + // The following line is generated automatically by the MaxOS build system. + s_progress_total = 22; } @@ -30,20 +33,20 @@ Logger::~Logger() { * * @param output_stream The output stream to add */ -void Logger::add_log_writer(OutputStream* log_writer) { +void Logger::add_log_writer(OutputStream *log_writer) { - // If the list is not empty - if(m_log_writer_count >= m_max_log_writers) - return; + // If the list is not empty + if (m_log_writer_count >= m_max_log_writers) + return; - // Add the output stream to the list - m_log_writers[m_log_writer_count] = log_writer; - m_log_writers_enabled[m_log_writer_count] = true; - m_log_writer_count++; + // Add the output stream to the list + m_log_writers[m_log_writer_count] = log_writer; + m_log_writers_enabled[m_log_writer_count] = true; + m_log_writer_count++; - // Print the setup info - *this << LogLevel::INFO << "Logger setup: " << m_log_writer_count << "\n"; - *this << LogLevel::HEADER << "MaxOS v" << VERSION_STRING << " [ build " << BUILD_NUMBER << " ]\n"; + // Print the setup info + *this << LogLevel::INFO << "Logger setup: " << m_log_writer_count << "\n"; + *this << LogLevel::HEADER << "MaxOS v" << VERSION_STRING << " [ build " << BUILD_NUMBER << " ]\n"; } @@ -52,16 +55,16 @@ void Logger::add_log_writer(OutputStream* log_writer) { * * @param output_stream The output stream to remove */ -void Logger::disable_log_writer(OutputStream* log_writer) { +void Logger::disable_log_writer(OutputStream *log_writer) { - // If the list is empty - if(m_log_writer_count == 0) - return; + // If the list is empty + if (m_log_writer_count == 0) + return; - // Find the output stream in the list - for(int i = 0; i < m_log_writer_count; i++) - if(m_log_writers[i] == log_writer) - m_log_writers_enabled[i] = false; + // Find the output stream in the list + for (int i = 0; i < m_log_writer_count; i++) + if (m_log_writers[i] == log_writer) + m_log_writers_enabled[i] = false; } /** @@ -71,38 +74,39 @@ void Logger::disable_log_writer(OutputStream* log_writer) { */ void Logger::set_log_level(LogLevel log_level) { - // Set the log level - m_log_level = log_level; + // Set the log level + m_log_level = log_level; - // Update the progress bar - if(log_level == LogLevel::INFO){ - VESABootConsole::update_progress_bar((m_progress_current * 100) / s_progress_total); - m_progress_current ++; - } + // Update the progress bar + if (log_level == LogLevel::INFO) { + VESABootConsole::update_progress_bar((m_progress_current * 100) / s_progress_total); + m_progress_current++; + } - // Print the header - switch (log_level) { + // Print the header + switch (log_level) { - case LogLevel::HEADER: - *this << ANSI_COLOURS[ANSIColour::FG_Blue] << "[ BOOT ] "; - break; + case LogLevel::HEADER: + *this << ANSI_COLOURS[ANSIColour::FG_Blue] << "[ BOOT ] "; + break; - case LogLevel::INFO: - *this << ANSI_COLOURS[ANSIColour::FG_Cyan] << "[ INFO ]" << ANSI_COLOURS[ANSIColour::FG_White] << " "; - break; + case LogLevel::INFO: + *this << ANSI_COLOURS[ANSIColour::FG_Cyan] << "[ INFO ]" << ANSI_COLOURS[ANSIColour::FG_White] << " "; + break; - case LogLevel::DEBUG: - *this << ANSI_COLOURS[ANSIColour::FG_Yellow] << "[ DEBUG ]" << ANSI_COLOURS[ANSIColour::Reset] << " "; - break; + case LogLevel::DEBUG: + *this << ANSI_COLOURS[ANSIColour::FG_Yellow] << "[ DEBUG ]" << ANSI_COLOURS[ANSIColour::Reset] << " "; + break; - case LogLevel::WARNING: - *this << ANSI_COLOURS[ANSIColour::FG_Yellow] << ANSI_COLOURS[FG_White] << "[ WARNING ]" << ANSI_COLOURS[ANSIColour::Reset] << " "; - break; + case LogLevel::WARNING: + *this << ANSI_COLOURS[ANSIColour::BG_Yellow] << ANSI_COLOURS[FG_White] << "[ WARNING ]" + << ANSI_COLOURS[ANSIColour::Reset] << " "; + break; - case LogLevel::ERROR: - *this << ANSI_COLOURS[ANSIColour::BG_Red] << "[ ERROR ]" << ANSI_COLOURS[ANSIColour::Reset] << " "; - break; - } + case LogLevel::ERROR: + *this << ANSI_COLOURS[ANSIColour::BG_Red] << "[ ERROR ]" << ANSI_COLOURS[ANSIColour::Reset] << " "; + break; + } } @@ -113,78 +117,70 @@ void Logger::set_log_level(LogLevel log_level) { */ void Logger::write_char(char c) { - // Ensure logging at this level is enabled - if(m_log_level > s_max_log_level) - return; + // Ensure logging at this level is enabled + if (m_log_level > s_max_log_level) + return; - // Write the character to all output streams - for(int i = 0; i < m_log_writer_count; i++) - if(m_log_writers_enabled[i] || m_log_level == LogLevel::ERROR) - m_log_writers[i]->write_char(c); + // Write the character to all output streams + for (int i = 0; i < m_log_writer_count; i++) + if (m_log_writers_enabled[i] || m_log_level == LogLevel::ERROR) + m_log_writers[i]->write_char(c); } /** * @brief Gets the active logger + * * @return The active logger */ -Logger& Logger::Out() { - return *active_logger(); +Logger &Logger::Out() { + return *active_logger(); } /** * @brief Gets active logger set to task level + * * @return The task logger */ -Logger Logger::HEADER(){ - - // Set the log level to task - s_active_logger->set_log_level(LogLevel::HEADER); +Logger Logger::HEADER() { - // Return the logger - return Out(); + s_active_logger->set_log_level(LogLevel::HEADER); + return Out(); } /** * @brief Gets active logger set to info level + * * @return The info logger */ Logger Logger::INFO() { - // Set the log level to info - s_active_logger->set_log_level(LogLevel::INFO); - - // Return the logger - return Out(); - + s_active_logger->set_log_level(LogLevel::INFO); + return Out(); } /** * @brief Gets active logger set to DEBUG level + * * @return The debug logger */ Logger Logger::DEBUG() { - // Set the log level to debug - s_active_logger->set_log_level(LogLevel::DEBUG); - - // Return the logger - return Out(); + s_active_logger->set_log_level(LogLevel::DEBUG); + return Out(); } /** * @brief Gets active logger set to WARNING level + * * @return The warning logger */ Logger Logger::WARNING() { - // Set the log level to warning - s_active_logger->set_log_level(LogLevel::WARNING); - - // Return the logger - return Out(); + s_active_logger->set_log_level(LogLevel::WARNING); + return Out(); } /** @@ -194,11 +190,8 @@ Logger Logger::WARNING() { */ Logger Logger::ERROR() { - // Set the log level to error - s_active_logger->set_log_level(LogLevel::ERROR); - - // Return the logger - return Out(); + s_active_logger->set_log_level(LogLevel::ERROR); + return Out(); } @@ -208,7 +201,7 @@ Logger Logger::ERROR() { * @return The active logger */ Logger *Logger::active_logger() { - return s_active_logger; + return s_active_logger; } /** @@ -219,65 +212,62 @@ Logger *Logger::active_logger() { */ void Logger::printf(char const *format, ...) { - // Create a pointer to the data - va_list parameters; - va_start(parameters, format); - - - // Loop through the format string - for (; *format != '\0'; format++) - { - - // If it is not a %, print the character - if (*format != '%') - { - write_char(*format); - continue; - } - - // Move to the next character - format++; - - switch (*format) - { - case 'd': - { - // Print a decimal - int number = va_arg (parameters, int); - write_int(number); - break; - } - case 'x': - { - // Print a hex - uint64_t number = va_arg (parameters, uint64_t ); - write_hex(number); - break; - } - case 's': - { - // Print a string - char* str = va_arg (parameters, char*); - write(str); - break; - } - } - } + // Create a pointer to the data + va_list parameters; + va_start(parameters, format); + + // Loop through the format string + for (; *format != '\0'; format++) { + + // If it is not a %, print the character + if (*format != '%') { + write_char(*format); + continue; + } + + // Move to the next character + format++; + switch (*format) { + case 'd': { + // Print a decimal + int number = va_arg (parameters, int); + write_int(number); + break; + } + case 'x': { + // Print a hex + uint64_t number = va_arg (parameters, uint64_t); + write_hex(number); + break; + } + case 's': { + // Print a string + char *str = va_arg (parameters, char*); + write(str); + break; + } + } + } } -#include +/** + * @brief Puts the system into a panic state if the condition is false, printing the message first + * + * @param condition The condition to check if is met + * @param message The message to print if the condition fails + */ void Logger::ASSERT(bool condition, char const *message, ...) { - // If the condition is met then everything is ok - if(condition) - return; + // If the condition is met then everything is ok + if (condition) + return; - // Print the message - s_active_logger -> set_log_level(LogLevel::ERROR); - s_active_logger -> printf(message); + // Print the message + s_active_logger->set_log_level(LogLevel::ERROR); + s_active_logger->printf(message); - // Hang the system - system::CPU::PANIC("Check previous logs for more information"); + // Hang the system + system::CPU::PANIC("Check previous logs for more information"); } /** @@ -286,29 +276,9 @@ void Logger::ASSERT(bool condition, char const *message, ...) { * @param log_level The log level to set * @return This logger */ -Logger& Logger::operator<<(LogLevel log_level) { +Logger &Logger::operator<<(LogLevel log_level) { - // Set the log level - set_log_level(log_level); - - // Return this logger - return *this; - -} + set_log_level(log_level); + return *this; -/** - * @brief Handles a line feed for different log levels - */ -void Logger::lineFeed() { - - switch (s_active_logger -> m_log_level) { - case LogLevel::HEADER: - case LogLevel::INFO: - case LogLevel::DEBUG: - case LogLevel::WARNING: - case LogLevel::ERROR: - write_char('\n'); - - } - -} +} \ No newline at end of file diff --git a/kernel/src/common/outputStream.cpp b/kernel/src/common/outputStream.cpp index 214242dd..e8f0cfa3 100644 --- a/kernel/src/common/outputStream.cpp +++ b/kernel/src/common/outputStream.cpp @@ -20,8 +20,7 @@ OutputStream::~OutputStream() = default; */ void OutputStream::lineFeed() { - // write the text representation of a newline to the output stream. - write_char('\n'); + write_char('\n'); } @@ -30,8 +29,8 @@ void OutputStream::lineFeed() { */ void OutputStream::carriageReturn() { - // write the text representation of a carriage return to the output stream. - write_char('\r'); + // write the text representation of a carriage return to the output stream. + write_char('\r'); } @@ -49,7 +48,7 @@ void OutputStream::clear() { */ void OutputStream::write(string string_to_write) { - write(string_to_write.c_str()); + write(string_to_write.c_str()); } /** @@ -57,44 +56,31 @@ void OutputStream::write(string string_to_write) { * * @param string_to_write The string to write to the output stream. */ -void OutputStream::write(const char* string_to_write){ +void OutputStream::write(const char *string_to_write) { - // Loop through the string - int i = 0; - while (string_to_write[i] != '\0') { + int i = 0; + while (string_to_write[i] != '\0') { - // Switch on the current character - switch (string_to_write[i]) { + switch (string_to_write[i]) { - // If the current character is a newline - case '\n': + case '\n': + lineFeed(); + break; - // write a newline to the output stream - lineFeed(); - break; + case '\r': + carriageReturn(); + break; - // If the current character is a carriage return - case '\r': + case '\0': + return; - // write a carriage return to the output stream - carriageReturn(); - break; + default: + write_char(string_to_write[i]); + break; - - // If the current character is a null terminator - case '\0': - return; - - // If the current character is any other character - default: - - // write the current character to the output stream - write_char(string_to_write[i]); - break; - - } - i++; - } + } + i++; + } } /** @@ -113,7 +99,7 @@ void OutputStream::write_char(char) { */ void OutputStream::write_int(int int_to_write) { - write(itoa(10, int_to_write)); + write(itoa(10, int_to_write)); } @@ -124,23 +110,20 @@ void OutputStream::write_int(int int_to_write) { */ void OutputStream::write_hex(uint64_t hex_to_write) { - write(htoa(hex_to_write)); + write(htoa(hex_to_write)); } /** - * @brief Writes a interger to the output stream. + * @brief Writes a integer to the output stream. * * @param int_to_write The integer to write to the output stream. * @return The output stream. */ -OutputStream &OutputStream::operator << (int int_to_write) { - - // Call the writeInt function to write the integer to the output stream - write_int(int_to_write); +OutputStream &OutputStream::operator<<(int int_to_write) { - // Return the output stream - return *this; + write_int(int_to_write); + return *this; } /** @@ -149,13 +132,10 @@ OutputStream &OutputStream::operator << (int int_to_write) { * @param hex_to_write The hex to write to the output stream. * @return The output stream. */ -OutputStream &OutputStream::operator << (uint64_t hex_to_write) { - - // Call the write_hex function to write the hex to the output stream - write_hex(hex_to_write); +OutputStream &OutputStream::operator<<(uint64_t hex_to_write) { - // Return the output stream - return *this; + write_hex(hex_to_write); + return *this; } /** @@ -164,13 +144,10 @@ OutputStream &OutputStream::operator << (uint64_t hex_to_write) { * @param string_to_write The string to write to the output stream. * @return The output stream. */ -OutputStream &OutputStream::operator << (string string_to_write) { +OutputStream &OutputStream::operator<<(string string_to_write) { - // Call the write function to write the string to the output stream - write(string_to_write); - - // Return the output stream - return *this; + write(string_to_write); + return *this; } /** @@ -181,11 +158,8 @@ OutputStream &OutputStream::operator << (string string_to_write) { */ OutputStream &OutputStream::operator<<(char char_to_write) { - // Call the writeChar function to write the character to the output stream - write_char(char_to_write); - - // Return the output stream - return *this; + write_char(char_to_write); + return *this; } /** @@ -196,10 +170,6 @@ OutputStream &OutputStream::operator<<(char char_to_write) { */ OutputStream &OutputStream::operator<<(char const *string_to_write) { - // Call the write function to write the string to the output stream - write(string_to_write); - - // Return the output stream - return *this; -} - + write(string_to_write); + return *this; +} \ No newline at end of file diff --git a/kernel/src/common/spinlock.cpp b/kernel/src/common/spinlock.cpp index 660d6e8d..f8aecafb 100644 --- a/kernel/src/common/spinlock.cpp +++ b/kernel/src/common/spinlock.cpp @@ -21,13 +21,8 @@ Spinlock::~Spinlock() = default; */ void Spinlock::lock() { - // Wait for the lock to be available - acquire(); - - // Set the lock to be locked - m_locked = true; - - + acquire(); + m_locked = true; } /** @@ -35,12 +30,8 @@ void Spinlock::lock() { */ void Spinlock::unlock() { - // Set the lock to be unlocked - m_locked = false; - - // Release the lock - release(); - + m_locked = false; + release(); } /** @@ -49,7 +40,7 @@ void Spinlock::unlock() { * @return True if the spinlock is locked, false otherwise */ bool Spinlock::is_locked() const { - return m_locked; + return m_locked; } @@ -57,20 +48,18 @@ bool Spinlock::is_locked() const { * @brief Acquire the spinlock: wait until the lock is available, yielding if desired until that happens. */ void Spinlock::acquire() { - while (__atomic_test_and_set(&m_locked, __ATOMIC_ACQUIRE)) { + while (__atomic_test_and_set(&m_locked, __ATOMIC_ACQUIRE)) { - // Wait for the lock to be available - if(m_should_yield) - Scheduler::system_scheduler()->yield(); + // Wait for the lock to be available + if (m_should_yield) + Scheduler::system_scheduler()->yield(); - // don't optimise this loop - asm("nop"); - } + } } /** * @brief Release the spinlock */ void Spinlock::release() { - __atomic_clear(&m_locked, __ATOMIC_RELEASE); + __atomic_clear(&m_locked, __ATOMIC_RELEASE); } diff --git a/kernel/src/common/string.cpp b/kernel/src/common/string.cpp index b3f5428b..b898bf8c 100644 --- a/kernel/src/common/string.cpp +++ b/kernel/src/common/string.cpp @@ -5,70 +5,129 @@ using namespace MaxOS; -String::String() -{ +String::String() { - // String that only contains the null terminator - m_string = new char[1]; - m_string[0] = '\0'; - m_length = 1; + // String that only contains the null terminator + allocate_self(); + m_string[0] = '\0'; + m_length = 0; } -String::String(char const *string) -{ +String::String(char c) { - // Get the length of the string, prevent longer than 10000 because this should mean somethings gone wrong - m_length = 0; - while (string[m_length] != '\0' && m_length <= 10000) - m_length++; + // Create the memory + m_length = 1; + allocate_self(); + // Store the char + m_string[0] = c; + m_string[m_length] = '\0'; - // Allocate memory for the string (and null terminator) - m_string = new char[m_length + 1]; - // Copy the string - for (int i = 0; i < m_length; i++) - m_string[i] = string[i]; +} + +String::String(char const *string) { + + // Get the length of the string, prevent longer than 10000 because this should mean something's gone wrong + m_length = 0; + while (string[m_length] != '\0' && m_length <= 10000) + m_length++; + allocate_self(); - // If the length is more than 10,000 Replace the end with a warning incase future use actually requires that - const char* warning = "MAXOS: String length exceeded 10000 - might be a bug"; - if(m_length > 10000) - for (int i = 0; i < 52; i++) - m_string[m_length - 52 + i] = warning[i]; + // Copy the string + for (int i = 0; i < m_length; i++) + m_string[i] = string[i]; + // If the length is more than 10,000 Replace the end with a warning incase future use actually requires that + const char *warning = "MAXOS: String length exceeded 10000 - might be a bug"; + if (m_length > 10000) + for (int i = 0; i < 52; i++) + m_string[m_length - 52 + i] = warning[i]; - // Write the null terminator - m_string[m_length] = '\0'; + m_string[m_length] = '\0'; } +String::String(uint8_t const *string, int length) { + // Allocate memory for the string (and null terminator) + m_length = length; + allocate_self(); -String::String(int value) { + // Copy the string + for (int i = 0; i < length; i++) + m_string[i] = string[i]; + // Write the null terminator + m_string[length] = '\0'; } +String::String(int value) { + + // Convert to a string + const char *str = itoa(10, value); + m_length = strlen(str); + // Create space to store + allocate_self(); + + // Store the string + for (int i = 0; i < m_length; i++) + m_string[i] = str[i]; + m_string[m_length] = '\0'; + +} + +/** + * @brief Constructs a string from a hex value (Excludes 0x____) + * + * @param value + */ String::String(uint64_t value) { + // Convert to a string + const char *str = htoa(value); + m_length = strlen(str); + + // Create space to store + allocate_self(); + + // Store the string + for (int i = 0; i < m_length; i++) + m_string[i] = str[i]; + m_string[m_length] = '\0'; } String::String(float value) { + // Convert to a string + const char *str = ftoa(value); + m_length = strlen(str); + // Create space to store + allocate_self(); -} + // Store the string + for (int i = 0; i < m_length; i++) + m_string[i] = str[i]; + m_string[m_length] = '\0'; +} +/** + * @brief Copy constructor for the string + * + * @param other String to copy from + */ String::String(String const &other) { - // Copy the other string - copy(other); + copy(other); } String::~String() { - // Free the memory - delete[] m_string; + // Free the memory + if (!m_using_small) + delete[] m_string; } @@ -79,18 +138,16 @@ String::~String() { */ void String::copy(String const &other) { - // Get the length of the string - m_length = other.length(); - - // Allocate memory for the string (and null terminator) - m_string = new char[m_length + 1]; + // Allocate memory for the string (and null terminator) + m_length = other.length(); + allocate_self(); - // Copy the string - for (int i = 0; i < m_length; i++) - m_string[i] = other[i]; + // Copy the string + for (int i = 0; i < m_length; i++) + m_string[i] = other[i]; - // Write the null terminator - m_string[m_length] = '\0'; + // Write the null terminator + m_string[m_length] = '\0'; } @@ -102,41 +159,45 @@ void String::copy(String const &other) { */ int String::lex_value(String const &string) { - // The sum of the ascii values of the characters in the string - int sum = 0; + // Sum the ascii values of the characters in the string + int sum = 0; + for (int i = 0; i < string.length(); i++) + sum += string[i]; - // Add the ascii values of the characters in the string - for (int i = 0; i < string.length(); i++) - sum += string[i]; + return sum; +} - // Return the sum - return sum; +/** + * @brief Allocates memory for the string + */ +void String::allocate_self() { -} + // Clear the old buffer if in use + if (m_string && !m_using_small) + delete[] m_string; + + // Try to use the small string buffer + m_using_small = m_length + 1 <= s_small_storage; + m_string = m_using_small ? m_small_string : new char[m_length + 1]; +} /** * @brief Sets the string to the other string * * @param other The string for this one to be updated to - * @return String& The string + * @return String The string */ -String &String::operator = (String const &other) { - - // Self assignment check - if (this == &other) - return *this; - - // Free the old memory - delete[] m_string; +String &String::operator=(String const &other) { - // Copy the other string - copy(other); - - // Return the string - return *this; + // Self assignment check + if (this == &other) + return *this; + // Copy the other string + copy(other); + return *this; } /** @@ -144,11 +205,9 @@ String &String::operator = (String const &other) { * * @return The char* string */ -char* String::c_str() { - - // Return the string - return m_string; +char *String::c_str() { + return m_string; } /** @@ -156,11 +215,98 @@ char* String::c_str() { * * @return The string as an array of characters */ -const char* String::c_str() const { +const char *String::c_str() const { - // Return the string - return m_string; + return m_string; +} + +/** + * @brief Checks if the string starts with the other string (must contain the same characters in the same order) + * + * @param other The other string + * @return True if the string starts with the other string, false otherwise + */ +bool String::starts_with(String const &other) { + + // Must at least be able to fit the other string + if (m_length < other.length()) + return false; + + // Check if the string starts with the other string + for (int i = 0; i < other.length(); i++) + if (m_string[i] != other[i]) + return false; + // No string left over to check so it must contain other + return true; +} + +/** + * @brief Get a section of the string + * + * @param start The start of the substring + * @param length The length of the substring + * @return The substring or empty string if out of bounds + */ +String String::substring(int start, int length) const { + + // Ensure the start is within bounds + if (start < 0 || start >= m_length) + return {}; + + // Ensure the length is within bounds + if (length < 0 || start + length > m_length) + return {}; + + // Allocate memory for the substring (and null terminator) + String substring; + substring.m_length = length; + substring.allocate_self(); + + // Copy the substring + for (int i = 0; i < length; i++) + substring.m_string[i] = m_string[start + i]; + + // Write the null terminator + substring.m_string[length] = '\0'; + + return substring; +} + +/** + * @brief Splits the string by the delimiter + * + * @param delimiter What to split the string by + * @return A vector of strings that were split by the delimiter + */ +common::Vector String::split(String const &delimiter) const { + common::Vector strings; + + // Go through the string and split it by the delimiter + int start = 0; + for (int i = 0; i <= m_length - delimiter.length(); i++) { + + // Check if matches at this position + bool matches = true; + for (int j = 0; j < delimiter.length(); j++) + if (m_string[i + j] != delimiter[j]) { + matches = false; + break; + } + + if(!matches) + continue; + + // Add the splice of the string + strings.push_back(substring(start, i - start)); + start = i + delimiter.length(); + i += delimiter.length() - 1; + } + + // Add the last string to the vector + strings.push_back(substring(start, m_length - start)); + + return strings; } /** @@ -171,29 +317,27 @@ const char* String::c_str() const { */ int String::length(bool count_ansi) const { - // If ansi characters are not to be counted - if (count_ansi) - return m_length; + // If ansi characters are not to be counted + if (count_ansi) + return m_length; + // Calculate the length of the string without ansi characters + int total_length = 0; + int clean_length = 0; + while (m_string[total_length] != '\0') { - // Calculate the length of the string without ansi characters - int total_length = 0; - int clean_length = 0; - while (m_string[total_length] != '\0'){ + // If the character is an ansi character, skip it + if (m_string[total_length] == '\033') + while (m_string[total_length] != 'm') + total_length++; - // If the character is an ansi character, skip it - if (m_string[total_length] == '\033'){ - while (m_string[total_length] != 'm') - total_length++; - } + // Increment the length + clean_length++; + total_length++; + } - // Increment the length - clean_length++; - total_length++; - } - - // Return the length - return clean_length; + // Return the length + return clean_length; } /** @@ -204,17 +348,17 @@ int String::length(bool count_ansi) const { */ bool String::equals(String const &other) const { - // Check if the lengths are equal - if (m_length != other.length()) - return false; + // Check if the lengths are equal + if (m_length != other.length()) + return false; - // Check if the characters are equal - for (int i = 0; i < m_length; i++) - if (m_string[i] != other[i]) - return false; + // Check if the characters are equal + for (int i = 0; i < m_length; i++) + if (m_string[i] != other[i]) + return false; - // The strings are equal - return true; + // The strings are equal + return true; } @@ -224,11 +368,10 @@ bool String::equals(String const &other) const { * @param other The other string * @return True if the strings are equal, false otherwise */ -bool String::operator == (String const &other) const { - - // Check if the strings are equal - return equals(other); +bool String::operator==(String const &other) const { + // Check if the strings are equal + return equals(other); } /** @@ -237,15 +380,13 @@ bool String::operator == (String const &other) const { * @param other The other string * @return True if the strings are not equal, false otherwise */ -bool String::operator != (String const &other) const { - - // If the strings are equal, return false - if (*this == other) - return false; +bool String::operator!=(String const &other) const { - // The strings are not equal - return true; + // Self assignment check + if (*this == other) + return false; + return !equals(other); } /** @@ -254,10 +395,9 @@ bool String::operator != (String const &other) const { * @param other The other string * @return True if the string is less than the other, false otherwise */ -bool String::operator < (String const &other) const { +bool String::operator<(String const &other) const { - // If the sum of this is less than the sum of the other, return true - return lex_value(*this) < lex_value(other); + return lex_value(*this) < lex_value(other); } @@ -267,10 +407,9 @@ bool String::operator < (String const &other) const { * @param other The other string * @return True if the string is greater than the other, false otherwise */ -bool String::operator > (String const &other) const { +bool String::operator>(String const &other) const { - // If the sum of this is greater than the sum of the other, return true - return lex_value(*this) > lex_value(other); + return lex_value(*this) > lex_value(other); } @@ -280,10 +419,9 @@ bool String::operator > (String const &other) const { * @param other The other string * @return True if the string is less than or equal to the other, false otherwise */ -bool String::operator <= (String const &other) const { +bool String::operator<=(String const &other) const { - // If the sum of this is less than or equal to the sum of the other, return true - return lex_value(*this) <= lex_value(other); + return lex_value(*this) <= lex_value(other); } @@ -293,10 +431,9 @@ bool String::operator <= (String const &other) const { * @param other The other string * @return True if the string is greater than or equal to the other, false otherwise */ -bool String::operator >= (String const &other) const { +bool String::operator>=(String const &other) const { - // If the sum of this is greater than or equal to the sum of the other, return true - return lex_value(*this) >= lex_value(other); + return lex_value(*this) >= lex_value(other); } @@ -306,31 +443,26 @@ bool String::operator >= (String const &other) const { * @param other The other string * @return The concatenated string */ -String String::operator + (String const &other) const { - - // The concatenated string - String concatenated; - - // The length of the concatenated string - int length = m_length + other.length(); - concatenated.m_length = length; +String String::operator+(String const &other) const { - // Allocate memory for the concatenated string (and null terminator) - concatenated.m_string = new char[length + 1]; + // The concatenated string + String concatenated; + concatenated.m_length = m_length + other.length(); + concatenated.allocate_self(); - // Copy the first string - for (int i = 0; i < m_length; i++) - concatenated.m_string[i] = m_string[i]; + // Copy the first string + for (int i = 0; i < m_length; i++) + concatenated.m_string[i] = m_string[i]; - // Copy the second string - for(int i = 0; i < other.length(); i++) - concatenated.m_string[m_length + i] = other[i]; + // Copy the second string + for (int i = 0; i < other.length(); i++) + concatenated.m_string[m_length + i] = other[i]; - // Write the null terminator - concatenated.m_string[length] = '\0'; + // Write the null terminator + concatenated.m_string[concatenated.m_length] = '\0'; - // Return the concatenated string - return concatenated; + // Return the concatenated string + return concatenated; } /** @@ -339,37 +471,12 @@ String String::operator + (String const &other) const { * @param other The other string * @return The concatenated string */ -String &String::operator += (String const &other) { - - // The concatenated string - String concatenated; - - // The length of the concatenated string - int length = m_length + other.length(); - concatenated.m_length = length; - - // Allocate memory for the concatenated string (and null terminator) - concatenated.m_string = new char[length + 1]; - - // Copy the first string - for (int i = 0; i < m_length; i++) - concatenated.m_string[i] = m_string[i]; - - // Copy the second string - for (int i = 0; i < other.length(); i++) - concatenated.m_string[m_length + i] = other[i]; +String &String::operator+=(String const &other) { - // Write the null terminator - concatenated.m_string[length] = '\0'; - - // Free the old memory - delete[] m_string; - - // Copy the concatenated string - copy(concatenated); - - // Return the concatenated string - return *this; + // Add the other string to this string + String concatenated = *this + other; + copy(concatenated); + return *this; } @@ -379,8 +486,8 @@ String &String::operator += (String const &other) { * @param index The index of the character * @return The character at the specified index */ -char& String::operator[](int index) { - return m_string[index]; +char &String::operator[](int index) { + return m_string[index]; } @@ -390,8 +497,8 @@ char& String::operator[](int index) { * @param index The index of the character * @return The character at the specified index */ -char& String::operator[](int index) const { - return m_string[index]; +char &String::operator[](int index) const { + return m_string[index]; } /** @@ -402,26 +509,21 @@ char& String::operator[](int index) const { */ String String::operator*(int times) const { - // The repeated string - String repeated; - - // The length of the repeated string - int length = m_length * times; - repeated.m_length = length; - - // Allocate memory for the repeated string (and null terminator) - repeated.m_string = new char[length + 1]; + // The repeated string + String repeated; + repeated.m_length = m_length * times; + repeated.allocate_self(); - // Copy the string - for (int i = 0; i < times; i++) - for (int j = 0; j < m_length; j++) - repeated.m_string[i * m_length + j] = m_string[j]; + // Copy the string + for (int i = 0; i < times; i++) + for (int j = 0; j < m_length; j++) + repeated.m_string[i * m_length + j] = m_string[j]; - // Write the null terminator - repeated.m_string[length] = '\0'; + // Write the null terminator + repeated.m_string[repeated.m_length] = '\0'; - // Return the repeated string - return repeated; + // Return the repeated string + return repeated; } @@ -434,39 +536,55 @@ String String::operator*(int times) const { */ String String::center(int width, char fill) const { - // The centered string - String centered; + // The number of characters to add + int add = (width - m_length) / 2; - // The length of the string - int length = m_length; + // The centered string + String centered; + centered.m_length = width; + centered.allocate_self(); - // The number of characters to add - int add = (width - length) / 2; + // Fill the right side (before) + for (int i = 0; i < add; i++) + centered.m_string[i] = fill; - // The length of the centered string - centered.m_length = width; + // Copy the string (middle) + for (int i = 0; i < m_length; i++) + centered.m_string[add + i] = m_string[i]; - // Allocate memory for the centered string (and null terminator) - centered.m_string = new char[width + 1]; + // Fill the left side (after) + for (int i = add + m_length; i < width; i++) + centered.m_string[i] = fill; - // Fill the string with the fill character - for (int i = 0; i < add; i++) - centered.m_string[i] = fill; + // Write the null terminator + centered.m_string[width] = '\0'; - // Copy the string - for (int i = 0; i < length; i++) - centered.m_string[add + i] = m_string[i]; + return centered; +} + +/** + * @brief Strips the string of whitespace + * + * @param strip_char The character to strip (default = ' ') + * @return The stripped string (new string) + */ +String String::strip(char strip_char) const { - // Fill the string with the fill character - for (int i = add + length; i < width; i++) - centered.m_string[i] = fill; + // The stripped string + String stripped; + stripped.copy(*this); - // Write the null terminator - centered.m_string[width] = '\0'; + // Search from the back for the earliest non-whitespace character + int end = m_length - 1; + while (end >= 0 && (m_string[end] == strip_char || m_string[end] == '\n' || m_string[end] == '\t')) + end--; - // Return the centered string - return centered; + // Make sure there is something to strip + if (end < 0) + return stripped; + // Split the string to remove the end + return stripped.substring(0, end + 1); } /** @@ -475,11 +593,10 @@ String String::center(int width, char fill) const { * @param str The string to get the length of * @return The length of the string */ -int strlen(const char* str) -{ - int len = 0; - for (; str[len] != '\0'; len++); - return len; +int strlen(const char *str) { + int len = 0; + for (; str[len] != '\0'; len++); + return len; } /** @@ -491,32 +608,29 @@ int strlen(const char* str) * * @return The converted string */ -char* itoa(int base, int64_t number) -{ +char *itoa(int base, int64_t number) { - // If there is no buffer use a default buffer - static char buffer[50] = {0}; + // If there is no buffer use a default buffer + static char buffer[50] = {0}; - int i = 49; - bool isNegative = number < 0; + int i = 49; + bool isNegative = number < 0; - if (number == 0) - { - buffer[i] = '0'; - return &buffer[i]; - } + if (number == 0) { + buffer[i] = '0'; + return &buffer[i]; + } - for (; number && i; --i, number /= base) - buffer[i] = "0123456789ABCDEF"[number % base]; + for (; number && i; --i, number /= base) + buffer[i] = "0123456789ABCDEF"[number % base]; - if (isNegative) - { - buffer[i] = '-'; - return &buffer[i]; - } + if (isNegative) { + buffer[i] = '-'; + return &buffer[i]; + } - return &buffer[i + 1]; + return &buffer[i + 1]; } /** @@ -526,22 +640,20 @@ char* itoa(int base, int64_t number) * @param buffer The buffer to store the converted string * @return The converted string */ -char* htoa(uint64_t number) -{ - // If there is no buffer use a default buffer - static char buffer[50] = {0}; - int i = 49; +char *htoa(uint64_t number) { + // If there is no buffer use a default buffer + static char buffer[50] = {0}; + int i = 49; - if (number == 0) - { - buffer[i] = '0'; - return &buffer[i]; - } + if (number == 0) { + buffer[i] = '0'; + return &buffer[i]; + } - for (; number && i; --i, number /= 16) - buffer[i] = "0123456789ABCDEF"[number % 16]; + for (; number && i; --i, number /= 16) + buffer[i] = "0123456789ABCDEF"[number % 16]; - return &buffer[i + 1]; + return &buffer[i + 1]; } /** @@ -551,65 +663,65 @@ char* htoa(uint64_t number) * @param buffer The buffer to store the converted string * @return The converted string */ -char* ftoa(float number) { +char *ftoa(float number) { - static char buffer[50]; - char* ptr = buffer; + static char buffer[50]; + char *ptr = buffer; - // Handle negative numbers. - if (number < 0) { - *ptr++ = '-'; - number = -number; - } + // Handle negative numbers. + if (number < 0) { + *ptr++ = '-'; + number = -number; + } - // Separate integer and fractional parts. - int64_t intPart = (int64_t)number; - float fraction = number - (float)intPart; + // Separate integer and fractional parts. + int64_t intPart = (int64_t) number; + float fraction = number - (float) intPart; - // Convert integer part to string using itoa. - char* intStr = itoa(10, intPart); - while (*intStr) { - *ptr++ = *intStr++; - } + // Convert integer part to string using itoa. + char *intStr = itoa(10, intPart); + while (*intStr) { + *ptr++ = *intStr++; + } - // Add the decimal point. - *ptr++ = '.'; + // Add the decimal point. + *ptr++ = '.'; - // Define the desired precision for the fractional part. - const int precision = 6; + // Define the desired precision for the fractional part. + const int precision = 6; - // Multiply the fraction to shift the decimal digits into integer range. - float fracValue = fraction; - for (int i = 0; i < precision; i++) { - fracValue *= 10.0f; - } + // Multiply the fraction to shift the decimal digits into integer range. + float fracValue = fraction; + for (int i = 0; i < precision; i++) { + fracValue *= 10.0f; + } - // Optionally, round the value. - auto fracInt = (int64_t)(fracValue + 0.5f); + // Optionally, round the value. + auto fracInt = (int64_t) (fracValue + 0.5f); - // Convert the fractional part to string. - char fracBuffer[50]; - char* fracStr = itoa(10, fracInt); + // Convert the fractional part to string. + char fracBuffer[50]; + char *fracStr = itoa(10, fracInt); - // Ensure we have leading zeros if the fractional part doesn't produce enough digits. - // Calculate length of the converted fractional string. - int len = 0; - for (char* p = fracStr; *p; p++) { - len++; - } - for (int i = 0; i < precision - len; i++) { - *ptr++ = '0'; - } + // Ensure we have leading zeros if the fractional part doesn't produce enough digits. + // Calculate length of the converted fractional string. + int len = 0; + for (char *p = fracStr; *p; p++) { + len++; + } + for (int i = 0; i < precision - len; i++) { + *ptr++ = '0'; + } - // Copy the fractional digits. - while (*fracStr) { - *ptr++ = *fracStr++; - } + // Copy the fractional digits. + while (*fracStr) { + *ptr++ = *fracStr++; + } - // Null-terminate the string. - *ptr = '\0'; + // Null-terminate the string. + *ptr = '\0'; - return buffer; + return buffer; } /** @@ -621,13 +733,13 @@ char* ftoa(float number) { */ bool strcmp(char const *str1, char const *str2) { - // Check if the strings are equal - for (int i = 0; str1[i] != '\0' || str2[i] != '\0'; i++) - if (str1[i] != str2[i]) - return false; + // Check if the strings are equal + for (int i = 0; str1[i] != '\0' || str2[i] != '\0'; i++) + if (str1[i] != str2[i]) + return false; - // The strings are equal - return true; + // The strings are equal + return true; } @@ -640,8 +752,8 @@ bool strcmp(char const *str1, char const *str2) { */ bool strcmp(char const *str1, String const &str2) { - // Use the other strcmp function - return strcmp(str1, str2.c_str()); + // Use the other strcmp function + return strcmp(str1, str2.c_str()); } @@ -654,8 +766,8 @@ bool strcmp(char const *str1, String const &str2) { */ bool strcmp(String const &str1, char const *str2) { - // Use the other strcmp function - return strcmp(str1.c_str(), str2); + // Use the other strcmp function + return strcmp(str1.c_str(), str2); } /** @@ -667,8 +779,8 @@ bool strcmp(String const &str1, char const *str2) { */ bool strcmp(String const &str1, String const &str2) { - // Use the other strcmp function - return strcmp(str1.c_str(), str2.c_str()); + // Use the other strcmp function + return strcmp(str1.c_str(), str2.c_str()); } @@ -682,13 +794,13 @@ bool strcmp(String const &str1, String const &str2) { */ bool strncmp(char const *str1, char const *str2, int length) { - // Check if the strings are equal - for (int i = 0; i < length; i++) - if (str1[i] != str2[i]) - return false; + // Check if the strings are equal + for (int i = 0; i < length; i++) + if (str1[i] != str2[i]) + return false; - // Strings are equal - return true; + // Strings are equal + return true; } @@ -702,8 +814,8 @@ bool strncmp(char const *str1, char const *str2, int length) { */ bool strncmp(char const *str1, String const &str2, int length) { - // Use the other strncmp function - return strncmp(str1, str2.c_str(), length); + // Use the other strncmp function + return strncmp(str1, str2.c_str(), length); } @@ -717,8 +829,8 @@ bool strncmp(char const *str1, String const &str2, int length) { */ bool strncmp(String const &str1, char const *str2, int length) { - // Use the other strncmp function - return strncmp(str1.c_str(), str2, length); + // Use the other strncmp function + return strncmp(str1.c_str(), str2, length); } @@ -732,6 +844,6 @@ bool strncmp(String const &str1, char const *str2, int length) { */ bool strncmp(String const &str1, String const &str2, int length) { - // Use the other strncmp function - return strncmp(str1.c_str(), str2.c_str(), length); + // Use the other strncmp function + return strncmp(str1.c_str(), str2.c_str(), length); } diff --git a/kernel/src/drivers/clock/clock.cpp b/kernel/src/drivers/clock/clock.cpp index 9aa8acaa..32e3e2a6 100644 --- a/kernel/src/drivers/clock/clock.cpp +++ b/kernel/src/drivers/clock/clock.cpp @@ -10,7 +10,6 @@ using namespace MaxOS::hardwarecommunication; using namespace MaxOS::drivers; using namespace MaxOS::drivers::clock; -///__Handler__ ClockEventHandler::ClockEventHandler() = default; @@ -31,22 +30,19 @@ void ClockEventHandler::on_time(common::Time const &) { * @param event The event being fired * @return The event (may have been modified by the handler) */ -Event* ClockEventHandler::on_event(Event* event) { - - switch (event -> type) { - case ClockEvents::TIME: - on_time(*((TimeEvent *)event)->time); - break; - - default: - break; - } - - // Return the event - return event; -} +Event *ClockEventHandler::on_event(Event *event) { + + switch (event->type) { + case ClockEvents::TIME: + on_time(*((TimeEvent *) event)->time); + break; -///__Clock__ + default: + break; + } + + return event; +} /** * @brief Constructor for the Clock class @@ -54,13 +50,12 @@ Event* ClockEventHandler::on_event(Event* event) { * @param interrupt_manager The interrupt manager * @param time_between_events The time between events in 10ths of a second */ -Clock::Clock(AdvancedProgrammableInterruptController* apic, uint16_t time_between_events) -: Driver(), - InterruptHandler(0x20), +Clock::Clock(AdvancedProgrammableInterruptController *apic, uint16_t time_between_events) +: InterruptHandler(0x20), m_apic(apic), m_ticks_between_events(time_between_events) { - Logger::INFO() << "Setting up Clock \n"; + Logger::INFO() << "Setting up Clock \n"; } Clock::~Clock() = default; @@ -71,20 +66,20 @@ Clock::~Clock() = default; */ void Clock::handle_interrupt() { - // Increment the number of ticks and decrement the number of ticks until the next event - m_ticks++; - m_ticks_until_next_event--; + // Clock has ticked + m_ticks++; + m_ticks_until_next_event--; - // If the number of ticks until the next event is not 0 then return - if(m_ticks_until_next_event != 0) - return; + // Dont raise events until needed + if (m_ticks_until_next_event != 0) + return; - // Otherwise, reset the number of ticks until the next event - m_ticks_until_next_event = m_ticks_between_events; - - // Raise the time event + // Raise the time event // Time time = get_time(); // raise_event(new TimeEvent(&time)); + + // Reset + m_ticks_until_next_event = m_ticks_between_events; } @@ -94,13 +89,10 @@ void Clock::handle_interrupt() { * @param address The address of the register to read from * @return The value of the register */ -uint8_t Clock::read_hardware_clock(uint8_t address) -{ - // Send the address to the hardware clock - m_command_port.write(address); +uint8_t Clock::read_hardware_clock(uint8_t address) { - // read the value from the hardware clock - return m_data_port.read(); + m_command_port.write(address); + return m_data_port.read(); } /** @@ -111,13 +103,12 @@ uint8_t Clock::read_hardware_clock(uint8_t address) */ uint8_t Clock::binary_representation(uint8_t number) const { - // If the binary coded decimal representation is not used, return the number - if(m_binary) - return number; - - // Otherwise, return the binary representation - return ((number / 16) * 10) + (number & 0x0f); + // Check if the conversion needed + if (m_binary) + return number; + // Convert to the binary representation + return ((number / 16) * 10) + (number & 0x0f); } /** @@ -125,16 +116,16 @@ uint8_t Clock::binary_representation(uint8_t number) const { */ void Clock::activate() { - // read the status register - uint8_t status = read_hardware_clock(0xB); + s_active_clock = this; - // Set the clock information - m_24_hour_clock = status & 0x02; - m_binary = status & 0x04; + // Get the stats from the clock + uint8_t status = read_hardware_clock(0xB); + // Store the clock status + m_24_hour_clock = status & 0x02; + m_binary = status & 0x04; } - /** * @brief Delays the program for a specified number of milliseconds * (rounded up to the nearest degree of accuracy - ensured the delay is at least the specified number of milliseconds). @@ -147,16 +138,13 @@ void Clock::activate() { */ void Clock::delay(uint32_t milliseconds) const { + // Round the number of milliseconds UP to the nearest clock accuracy + uint64_t rounded_milliseconds = (milliseconds + clock_accuracy - 1) / clock_accuracy; - // Round the number of milliseconds UP to the nearest clock accuracy - uint64_t rounded_milliseconds = (milliseconds + s_clock_accuracy - 1) / s_clock_accuracy; - - // Calculate the number of ticks until the delay is over - uint64_t ticks_until_delay_is_over = m_ticks + rounded_milliseconds; - - // Wait until the number of ticks is equal to the number of ticks until the delay is over - while(m_ticks < ticks_until_delay_is_over) - asm volatile("nop"); + // Wait until the time has passed + uint64_t ticks_until_delay_is_over = m_ticks + rounded_milliseconds; + while (m_ticks < ticks_until_delay_is_over) + asm volatile("nop"); } /** @@ -165,7 +153,7 @@ void Clock::delay(uint32_t milliseconds) const { * @return The name of the vendor */ string Clock::vendor_name() { - return "Generic"; + return "Generic"; } /** @@ -174,7 +162,7 @@ string Clock::vendor_name() { * @return The name of the device */ string Clock::device_name() { - return "Clock"; + return "Clock"; } /** @@ -184,27 +172,25 @@ string Clock::device_name() { */ void Clock::calibrate(uint64_t ms_per_tick) { - Logger::INFO() << "Calibrating Clock \n"; + Logger::INFO() << "Calibrating Clock \n"; + clock_accuracy = ms_per_tick; - // Update the clock accuracy - s_clock_accuracy = ms_per_tick; + // Get the ticks per ms + PIT pit(m_apic); + uint32_t ticks_per_ms = pit.ticks_per_ms(); - // Get the ticks per ms - PIT pit(m_apic); - uint32_t ticks_per_ms = pit.ticks_per_ms(); + // Configure the clock to periodic mode + uint32_t lvt = 0x20 | (1 << 17); + m_apic->local_apic()->write(0x320, lvt); - // Set the timer vector to 0x20 and configure it for periodic mode - uint32_t lvt = 0x20 | (1 << 17); - m_apic -> local_apic() -> write(0x320, lvt); + // Set the initial count + m_apic->local_apic()->write(0x380, ms_per_tick * ticks_per_ms); - // Set the initial count - m_apic -> local_apic() -> write(0x380, ms_per_tick * ticks_per_ms); + // Clear the interrupt mask for the clock + lvt &= ~(1 << 16); + m_apic->local_apic()->write(0x380, lvt); - // Clear the mask bit - lvt &= ~(1 << 16); - m_apic -> local_apic() -> write(0x380, lvt); - - Logger::DEBUG() << "Clock: Calibrated to " << ms_per_tick << "ms per tick\n"; + Logger::DEBUG() << "Clock: Calibrated to " << ms_per_tick << "ms per tick\n"; } /** @@ -214,34 +200,33 @@ void Clock::calibrate(uint64_t ms_per_tick) { */ common::Time Clock::get_time() { - // Wait for the clock to be ready - while(read_hardware_clock(0xA) & 0x80) - asm volatile("nop"); - - // Create a time object - Time time{}; - - // read the time from the hardware clock - time.year = binary_representation(read_hardware_clock(0x9)) + 2000; - time.month = binary_representation(read_hardware_clock(0x8)); - time.day = binary_representation(read_hardware_clock(0x7)); - time.hour = binary_representation(read_hardware_clock(0x4)); - time.minute = binary_representation(read_hardware_clock(0x2)); - time.second = binary_representation(read_hardware_clock(0x0)); + // Wait for the clock to be ready + while (read_hardware_clock(0xA) & 0x80) + asm volatile("nop"); - // If the clock is using 12hr format and PM is set then add 12 to the hour - if(!m_24_hour_clock && (time.hour & 0x80) != 0) - time.hour = ((time.hour & 0x7F) + 12) % 24; + // Read the time from the clock + Time time{}; + time.year = binary_representation(read_hardware_clock(0x9)) + 2000; + time.month = binary_representation(read_hardware_clock(0x8)); + time.day = binary_representation(read_hardware_clock(0x7)); + time.hour = binary_representation(read_hardware_clock(0x4)); + time.minute = binary_representation(read_hardware_clock(0x2)); + time.second = binary_representation(read_hardware_clock(0x0)); + // If the clock is using 12hr format and PM is set then add 12 to the hour + if (!m_24_hour_clock && (time.hour & 0x80) != 0) + time.hour = ((time.hour & 0x7F) + 12) % 24; - //Raise the clock event - return time; + return time; } +Clock *Clock::active_clock() { + return s_active_clock; +} -TimeEvent::TimeEvent(Time* time) -:Event(ClockEvents::TIME), -time(time) +TimeEvent::TimeEvent(Time *time) +: Event(ClockEvents::TIME), + time(time) { } @@ -252,8 +237,8 @@ PIT::PIT(AdvancedProgrammableInterruptController *apic) : InterruptHandler(0x22), m_data_port(0x40), m_command_port(0x43), - m_local_apic(apic -> local_apic()), - m_io_apic(apic -> io_apic()) + m_local_apic(apic->local_apic()), + m_io_apic(apic->io_apic()) { } @@ -264,7 +249,7 @@ PIT::~PIT() = default; * @brief Tick on each interrupt */ void PIT::handle_interrupt() { - m_ticks++; + m_ticks++; } /** @@ -274,56 +259,55 @@ void PIT::handle_interrupt() { */ uint32_t PIT::ticks_per_ms() { - // Set the redirect for the timer interrupt - interrupt_redirect_t redirect = { - .type = 0x2, - .index = 0x14, - .interrupt = 0x22, - .destination = 0x00, - .flags = 0x00, - .mask = true, - }; - m_io_apic -> set_redirect(&redirect); - - // Configure the PIT clock - pit_command_t command = { - .bcd_mode = (uint8_t)BCDMode::BINARY, - .operating_mode = (uint8_t)OperatingMode::RATE_GENERATOR, - .access_mode = (uint8_t)AccessMode::LOW_HIGH_BYTE, - .channel = (uint8_t)Channel::INTERRUPT - }; - m_command_port.write(*(uint8_t *)&command); - - // Set the clock rate to 1 ms; - uint16_t rate = 1193182 / 1000; - m_data_port.write(rate & 0xFF); - m_data_port.write(rate >> 8); - - // Stop the clock (write 0 as the initial count) - m_local_apic -> write(0x380, 0x00); - - // Set the divisor to 2 - m_local_apic -> write(0x3E0, 0x0); - - // Unmask the PIT interrupt - m_io_apic ->set_redirect_mask(redirect.index, false); - - // Calculate the number of ticks in 1 ms - auto max = (uint32_t) - 1; - m_local_apic -> write(0x380, max); - - while (m_ticks < s_calibrate_ticks) - asm volatile("nop"); - - uint32_t elapsed = max - (m_local_apic -> read(0x390)); - uint32_t ticks_per_ms = elapsed / s_calibrate_ticks; - - Logger::DEBUG() << "Ticks per ms: " << (int)ticks_per_ms << "\n"; - - // Disable the PIT interrupt again - m_local_apic -> write(0x380, 0x00); - m_io_apic -> set_redirect_mask(redirect.index, true); - - return ticks_per_ms; - + // Set the redirect for the timer interrupt + interrupt_redirect_t redirect = { + .type = 0x2, + .index = 0x14, + .interrupt = 0x22, + .destination = 0x00, + .flags = 0x00, + .mask = true, + }; + m_io_apic->set_redirect(&redirect); + + // Configure the PIT clock + pit_command_t command = { + .bcd_mode = (uint8_t) BCDMode::BINARY, + .operating_mode = (uint8_t) OperatingMode::RATE_GENERATOR, + .access_mode = (uint8_t) AccessMode::LOW_HIGH_BYTE, + .channel = (uint8_t) Channel::INTERRUPT + }; + m_command_port.write(*(uint8_t *) &command); + + // Set the clock rate to 1 ms; + uint16_t rate = 1193182 / 1000; + m_data_port.write(rate & 0xFF); + m_data_port.write(rate >> 8); + + // Stop the clock + m_local_apic->write(0x380, 0x00); + + // Set the divisor to 2 + m_local_apic->write(0x3E0, 0x0); + + // Unmask the PIT interrupt + m_io_apic->set_redirect_mask(redirect.index, false); + + // Calculate the number of ticks in 1 ms + auto max = (uint32_t) -1; + m_local_apic->write(0x380, max); + + while (m_ticks < s_calibrate_ticks) + asm volatile("nop"); + + uint32_t elapsed = max - (m_local_apic->read(0x390)); + uint32_t ticks_per_ms = elapsed / s_calibrate_ticks; + + Logger::DEBUG() << "Ticks per ms: " << (int) ticks_per_ms << "\n"; + + // Disable the PIT interrupt again + m_local_apic->write(0x380, 0x00); + m_io_apic->set_redirect_mask(redirect.index, true); + + return ticks_per_ms; } diff --git a/kernel/src/drivers/console/console.cpp b/kernel/src/drivers/console/console.cpp index ada5e196..43a2dbed 100644 --- a/kernel/src/drivers/console/console.cpp +++ b/kernel/src/drivers/console/console.cpp @@ -9,8 +9,6 @@ using namespace MaxOS::common; using namespace MaxOS::drivers; using namespace MaxOS::drivers::console; -///____ Console ____ - Console::Console() = default; Console::~Console() = default; @@ -131,17 +129,15 @@ void Console::put_string(uint16_t x, uint16_t y, string string, ConsoleColour fo // Print each character on the screen for(int i = 0; i < string.length(); i++) - put_character(x + i, y, string[i], foreground, background); + put_character(x + i, y, string[i], foreground, background); } /** - * @brief Scroll the console up by 1 line + * @brief Scroll the entire console up by 1 line */ void Console::scroll_up() { - // Scroll the console up by 1 line scroll_up(0, 0, width(), height()); - } /** @@ -157,32 +153,22 @@ void Console::scroll_up() { */ void Console::scroll_up(uint16_t left, uint16_t top, uint16_t width, uint16_t height, ConsoleColour foreground, ConsoleColour background, char fill) { - // For each line in the area to scroll (except the last line) - for(uint32_t y = top; y < top+height-1; y++){ - - // For each character in the line - for(uint32_t x = left; x < left+width; x++) { + // Shift everything but the last line by getting what is below it + for(uint32_t y = top; y < top+height-1; y++) + for(uint32_t x = left; x < left+width; x++) + put_character(x, y, get_character(x, y + 1), get_foreground_color(x, y + 1), get_background_color(x, y + 1)); - // Put the character from the line below - put_character(x, y, get_character(x, y + 1), - get_foreground_color(x, y + 1), - get_background_color(x, y + 1)); - - } - } - - // Fill the last line with the fill character + // Fill the last line for(uint16_t x = left; x < left+width; x++) put_character(x, top + height - 1, fill, foreground, background); } /** - * Clear the console + * Clear the entire console */ void Console::clear() { - // Clear the console clear(0, 0, width(), height()); } @@ -200,11 +186,14 @@ void Console::clear() { */ void Console::clear(uint16_t left, uint16_t top, uint16_t width, uint16_t height, ConsoleColour foreground, ConsoleColour background, char fill) { - // Put the fill character in the areas - for(uint16_t y = top; y < top+height; y++) - for(uint16_t x = left; x < left+width; x++){ + // Check bounds + if ( left > this -> width() || top > this -> height() || width > this -> width() || height > this -> height() ) + return; + + // Fill the area with the character + for(uint16_t y = top; y < top + height; y++) + for(uint16_t x = left; x < left + width; x++) put_character(x, y, fill, foreground, background); - } } @@ -225,10 +214,6 @@ void Console::invert_colors(uint16_t x, uint16_t y) { set_background_color(x, y, foreground); } - -///____ Console Area ____/// - - ConsoleArea::ConsoleArea(Console *console, uint16_t left, uint16_t top, uint16_t width, uint16_t height) : m_console(console), m_left(left), @@ -247,7 +232,7 @@ ConsoleArea::ConsoleArea(Console *console, uint16_t left, uint16_t top, uint16_t m_height(height) { - // Loop through the area setting the colors + // Store the colours of the console for(uint16_t y = top; y < top+height; y++) for(uint16_t x = left; x < left+width; x++){ console->set_foreground_color(x, y, foreground); @@ -256,7 +241,6 @@ ConsoleArea::ConsoleArea(Console *console, uint16_t left, uint16_t top, uint16_t } - ConsoleArea::~ConsoleArea() = default; /** @@ -286,13 +270,12 @@ uint16_t ConsoleArea::height() { */ void ConsoleArea::put_character(uint16_t x, uint16_t y, char c) { - // Make sure the coordinates are within the console area + // Check bounds if(x >= m_width || y >= m_height) return; - // Put the character on the console + // Write to the console m_console->put_character(m_left + x, m_top + y, c); - } @@ -389,9 +372,7 @@ ConsoleColour ConsoleArea::get_background_color(uint16_t x, uint16_t y) { */ void ConsoleArea::scroll_up() { - // Get the console m_console->scroll_up(m_left, m_top, m_width, m_height); - } /** @@ -413,7 +394,6 @@ void ConsoleArea::scroll_up(uint16_t left, uint16_t top, uint16_t width, } -///____ Console Stream ____/// ConsoleStream::ConsoleStream(Console *console) : m_console(console) { @@ -446,13 +426,11 @@ void ConsoleStream::write_char(char c) { switch (c) { // New line case '\n': - // Increment the y coordinate but if it is more than the height of the console scroll the console + // Go to the next line, if it is more then what the console can fit then scroll if(++m_cursor_y >= m_console->height()){ - - // Scroll the console m_console->scroll_up(); - // Decrement the y coordinate + // Ensure there is space at the bottom to write m_cursor_y = m_console->height()-1; } @@ -461,23 +439,22 @@ void ConsoleStream::write_char(char c) { // Carriage return case '\r': - // Go to the start of the next line m_cursor_x = 0; break; - // Null Terminator + // End of string case '\0': break; // Backspace case '\b': - // Decrement the x coordinate m_cursor_x--; break; // Tab case '\t': - // Figure out how many spaces to the next tab stop + + // Pad with spaces until a tab is reached spaces = 8 - (m_cursor_x % 8); for(uint16_t i = 0; i < spaces; i++) write_char(' '); @@ -487,7 +464,7 @@ void ConsoleStream::write_char(char c) { // Put the character on the console m_console->put_character(m_cursor_x, m_cursor_y, c); - // Increment the x coordinate + // Increment the x coordinate (ANSI aren't rendered) if(!is_ansi) m_cursor_x++; diff --git a/kernel/src/drivers/console/serial.cpp b/kernel/src/drivers/console/serial.cpp index ffd36cd7..c0013b1d 100644 --- a/kernel/src/drivers/console/serial.cpp +++ b/kernel/src/drivers/console/serial.cpp @@ -7,7 +7,7 @@ using namespace MaxOS; using namespace MaxOS::drivers; -SerialConsole::SerialConsole(Logger* logger) +SerialConsole::SerialConsole(Logger *logger) : m_data_port(0x3F8), m_interrupt_enable_port(0x3F9), m_fifo_control_port(0x3FA), @@ -16,44 +16,39 @@ SerialConsole::SerialConsole(Logger* logger) m_line_status_port(0x3FD) { - // Disable all interrupts - m_interrupt_enable_port.write(0x00); + // Disable all interrupts + m_interrupt_enable_port.write(0x00); - // Enable DLAB (set baud rate divisor) - m_line_control_port.write(0x80); + // Enable DLAB (set baud rate divisor) + m_line_control_port.write(0x80); - // Set divisor to 3 - m_data_port.write(0x03); - m_interrupt_enable_port.write(0x00); + // Set divisor to 3 + m_data_port.write(0x03); + m_interrupt_enable_port.write(0x00); - // 8 bits, no parity, one stop bit - m_line_control_port.write(0x03); + // 8 bits, no parity, one stop bit + m_line_control_port.write(0x03); - // Enable FIFO, clear them, with 14-byte threshold - m_fifo_control_port.write(0xC7); + // Enable FIFO, clear them, with 14-byte threshold + m_fifo_control_port.write(0xC7); - // IRQs enabled, RTS/DSR set - m_modem_control_port.write(0x0B); + // IRQs enabled, RTS/DSR set + m_modem_control_port.write(0x0B); - // Test serial chip - m_modem_control_port.write(0x1E); - m_data_port.write(0xAE); - if (m_data_port.read() != 0xAE) - return; - - // Enable serial chip - m_modem_control_port.write(0x0F); - - // Set the active serial console - logger->add_log_writer(this); + // Test serial chip + m_modem_control_port.write(0x1E); + m_data_port.write(0xAE); + if (m_data_port.read() != 0xAE) + return; + // Enable serial chip + m_modem_control_port.write(0x0F); + // Set the active serial console + logger->add_log_writer(this); } -SerialConsole::~SerialConsole() { - - -} +SerialConsole::~SerialConsole() = default; /** * @brief Waits for the serial port to be ready, then writes a character to it @@ -62,13 +57,14 @@ SerialConsole::~SerialConsole() { */ void SerialConsole::put_character(char c) { - // Wait for the serial port to be ready - while (0 == (m_line_status_port.read() & 0x20)); + // Wait for the serial port to be ready + while (0 == (m_line_status_port.read() & 0x20)); - // Write the character - m_data_port.write(c); + // Write the character + m_data_port.write(c); } + void SerialConsole::write_char(char c) { - put_character(c); -} + put_character(c); +} \ No newline at end of file diff --git a/kernel/src/drivers/console/textmode.cpp b/kernel/src/drivers/console/textmode.cpp index 0855e1a8..56fce4f4 100644 --- a/kernel/src/drivers/console/textmode.cpp +++ b/kernel/src/drivers/console/textmode.cpp @@ -9,12 +9,7 @@ using namespace MaxOS::common; using namespace MaxOS::drivers; using namespace MaxOS::drivers::console; -TextModeConsole::TextModeConsole() -: Driver(), - Console() -{ - -} +TextModeConsole::TextModeConsole() = default; TextModeConsole::~TextModeConsole() = default; @@ -23,9 +18,8 @@ TextModeConsole::~TextModeConsole() = default; * * @return The width of the console in characters */ -uint16_t TextModeConsole::width() -{ - return 80; +uint16_t TextModeConsole::width() { + return 80; } /** @@ -33,9 +27,8 @@ uint16_t TextModeConsole::width() * * @return The height of the console in characters */ -uint16_t TextModeConsole::height() -{ - return 25; +uint16_t TextModeConsole::height() { + return 25; } /** @@ -47,16 +40,15 @@ uint16_t TextModeConsole::height() */ void TextModeConsole::put_character(uint16_t x, uint16_t y, char c) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; + // Check bounds + if (x >= width() || y >= height()) + return; - // Calculate the offset - int offset = (y*width() + x); - - // Set the character at the offset, by masking the character with the current character (last 8 bits) - m_video_memory[offset] = (m_video_memory[offset] & 0xFF00) | (uint16_t)c; + // Calculate the offset + int offset = (y * width() + x); + // Set the character at the offset, mask away the colour information + m_video_memory[offset] = (m_video_memory[offset] & 0xFF00) | (uint16_t) c; } /** @@ -68,15 +60,15 @@ void TextModeConsole::put_character(uint16_t x, uint16_t y, char c) { */ void TextModeConsole::set_foreground_color(uint16_t x, uint16_t y, ConsoleColour foreground) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; + // Check bounds + if (x >= width() || y >= height()) + return; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Set the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) - m_video_memory[offset] = (m_video_memory[offset] & 0xF0FF) | ((uint16_t)foreground << 8); + // Set the foreground color at the offset, mask to get the foreground bits + m_video_memory[offset] = (m_video_memory[offset] & 0xF0FF) | ((uint16_t) foreground << 8); } /** @@ -88,16 +80,15 @@ void TextModeConsole::set_foreground_color(uint16_t x, uint16_t y, ConsoleColour */ void TextModeConsole::set_background_color(uint16_t x, uint16_t y, ConsoleColour background) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; - - // Calculate the offset - int offset = (y* width() + x); + // Check bounds + if (x >= width() || y >= height()) + return; - // Set the background color at the offset, by masking the background color with the current background color (bits 12-15) - m_video_memory[offset] = (m_video_memory[offset] & 0x0FFF) | ((uint16_t)background << 12); + // Calculate the offset + int offset = (y * width() + x); + // Set the background color at the offset, mask to get the backgroun bits + m_video_memory[offset] = (m_video_memory[offset] & 0x0FFF) | ((uint16_t) background << 12); } /** @@ -109,15 +100,15 @@ void TextModeConsole::set_background_color(uint16_t x, uint16_t y, ConsoleColour */ char TextModeConsole::get_character(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ' '; + // Check bounds + if (x >= width() || y >= height()) + return ' '; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the character at the offset, by masking the character with the current character (last 8 bits) - return (char)(m_video_memory[offset] & 0x00FF); + // Return the character at the offset, mask away the colour information + return (char) (m_video_memory[offset] & 0x00FF); } /** @@ -129,15 +120,15 @@ char TextModeConsole::get_character(uint16_t x, uint16_t y) { */ ConsoleColour TextModeConsole::get_foreground_color(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ConsoleColour::White; + // Check bounds + if (x >= width() || y >= height()) + return ConsoleColour::White; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) - return (ConsoleColour)((m_video_memory[offset] & 0x0F00) >> 8); + // Return the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) + return (ConsoleColour) ((m_video_memory[offset] & 0x0F00) >> 8); } /** @@ -149,13 +140,13 @@ ConsoleColour TextModeConsole::get_foreground_color(uint16_t x, uint16_t y) { */ ConsoleColour TextModeConsole::get_background_color(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ConsoleColour::Black; + // Check bounds + if (x >= width() || y >= height()) + return ConsoleColour::Black; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the background color at the offset, by masking the background color with the current background color (bits 12-15) - return (ConsoleColour)((m_video_memory[offset] & 0xF000) >> 12); + // Return the background color at the offset, by masking the background color with the current background color (bits 12-15) + return (ConsoleColour) ((m_video_memory[offset] & 0xF000) >> 12); } \ No newline at end of file diff --git a/kernel/src/drivers/console/vesaboot.cpp b/kernel/src/drivers/console/vesaboot.cpp index 10159dcd..a7a063ff 100644 --- a/kernel/src/drivers/console/vesaboot.cpp +++ b/kernel/src/drivers/console/vesaboot.cpp @@ -15,28 +15,28 @@ using namespace MaxOS::drivers::console; using namespace MaxOS::system; VESABootConsole::VESABootConsole(GraphicsContext *graphics_context) -: m_font((uint8_t*)AMIGA_FONT) +: m_font((uint8_t *)AMIGA_FONT) { - // Set up - Logger::INFO() << "Setting up VESA console\n"; - s_graphics_context = graphics_context; - m_video_memory_meta = (uint16_t*)MemoryManager::kmalloc(width() * height() * sizeof(uint16_t)); - - // Prepare the console - VESABootConsole::clear(); - print_logo(); - - // Connect to the loggers - m_console_area = new ConsoleArea(this, 0, 0, width() / 2 - 25, height(), ConsoleColour::DarkGrey, ConsoleColour::Black); - cout = new ConsoleStream(m_console_area); - - #ifdef TARGET_DEBUG - Logger::active_logger() -> add_log_writer(cout); - Logger::INFO() << "Console Stream set up \n"; - #endif - - update_progress_bar(0); + // Set up + Logger::INFO() << "Setting up VESA console\n"; + s_graphics_context = graphics_context; + m_video_memory_meta = (uint16_t *) MemoryManager::kmalloc(width() * height() * sizeof(uint16_t)); + + // Prepare the console + VESABootConsole::clear(); + print_logo(); + m_console_area = new ConsoleArea(this, 0, 0, width() / 2 - 25, height(), ConsoleColour::DarkGrey, + ConsoleColour::Black); + cout = new ConsoleStream(m_console_area); + + // Only log to the screen when debugging + #ifdef TARGET_DEBUG + Logger::active_logger()->add_log_writer(cout); + Logger::INFO() << "Console Stream set up \n"; + #endif + + update_progress_bar(0); } VESABootConsole::~VESABootConsole() = default; @@ -46,9 +46,8 @@ VESABootConsole::~VESABootConsole() = default; * * @return The width of the console in characters */ -uint16_t VESABootConsole::width() -{ - return s_graphics_context->width() / 8; // 8 pixels per character +uint16_t VESABootConsole::width() { + return s_graphics_context->width() / 8; // 8 pixels per character } /** @@ -56,9 +55,8 @@ uint16_t VESABootConsole::width() * * @return The height of the console in characters */ -uint16_t VESABootConsole::height() -{ - return s_graphics_context->height() / Font::font_height; +uint16_t VESABootConsole::height() { + return s_graphics_context->height() / Font::font_height; } /** @@ -70,71 +68,68 @@ uint16_t VESABootConsole::height() */ void VESABootConsole::put_character(uint16_t x, uint16_t y, char c) { - // Parse any ansi codes - if (c == '\033') { - - // Store the character - ansi_code_length = 0; - ansi_code[ansi_code_length++] = c; + // Parse any ansi codes + if (c == '\033') { - // Do not draw the escape character - return; + // Store the character + ansi_code_length = 0; + ansi_code[ansi_code_length++] = c; - } else if (ansi_code_length < 8) { + // Do not draw the escape character + return; + } - // Add the character to the ANSI code - ansi_code[ansi_code_length++] = c; + if (ansi_code_length < 8) { - // If the ANSI code is complete - if (c == 'm') { - ansi_code[ansi_code_length] = '\0'; - ansi_code_length = -1; + // Add the character to the ANSI code + ansi_code[ansi_code_length++] = c; - if(strcmp("\033[0m", ansi_code) != 0) { - m_foreground_color = ConsoleColour::Uninitialised; - m_background_color = ConsoleColour::Uninitialised; - return; - } + // If the ANSI code is complete + if (c == 'm') { + ansi_code[ansi_code_length] = '\0'; + ansi_code_length = -1; - // Get the colour from the ANSI code - auto* colour = new Colour(ansi_code); + if (strcmp("\033[0m", ansi_code) != 0) { + m_foreground_color = ConsoleColour::Uninitialised; + m_background_color = ConsoleColour::Uninitialised; + return; + } - // Set the colour - bool foreground = ansi_code[4] == '3'; - if (foreground) - m_foreground_color = colour->to_console_colour(); - else - m_background_color = colour->to_console_colour(); + // Get the colour from the ANSI code + const Colour colour(ansi_code); - // Delete the colour - delete colour; + // Set the colour + bool foreground = ansi_code[4] == '3'; + if (foreground) + m_foreground_color = colour.to_console_colour(); + else + m_background_color = colour.to_console_colour(); - } + } - // Do not draw the escape character - return; - } + // Do not draw the escape character + return; + } - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Set the character at the offset, by masking the character with the current character (last 8 bits) - m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0xFF00) | (uint16_t)c; + // Set the character at the offset, by masking the character with the current character (last 8 bits) + m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0xFF00) | (uint16_t) c; - // Convert the char into a string - char s[] = " "; - s[0] = c; + // Convert the char into a string + char s[] = " "; + s[0] = c; - Colour foreground = m_foreground_color == ConsoleColour::Uninitialised ? get_foreground_color(x, y) : Colour(m_foreground_color); - Colour background = m_background_color == ConsoleColour::Uninitialised ? get_background_color(x, y) : Colour(m_background_color); - - // Use the m_font to draw the character - m_font.draw_text(x * 8, y * Font::font_height, foreground, background, s_graphics_context, s); + Colour foreground = m_foreground_color == ConsoleColour::Uninitialised ? get_foreground_color(x, y) : Colour(m_foreground_color); + Colour background = m_background_color == ConsoleColour::Uninitialised ? get_background_color(x, y) : Colour(m_background_color); + // Use the m_font to draw the character + m_font.draw_text(x * 8, y * Font::font_height, foreground, background, s_graphics_context, s); } /** @@ -146,15 +141,15 @@ void VESABootConsole::put_character(uint16_t x, uint16_t y, char c) { */ void VESABootConsole::set_foreground_color(uint16_t x, uint16_t y, ConsoleColour foreground) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Set the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) - m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0xF0FF) | ((uint16_t)foreground << 8); + // Set the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) + m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0xF0FF) | ((uint16_t) foreground << 8); } /** @@ -166,16 +161,15 @@ void VESABootConsole::set_foreground_color(uint16_t x, uint16_t y, ConsoleColour */ void VESABootConsole::set_background_color(uint16_t x, uint16_t y, ConsoleColour background) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return; - - // Calculate the offset - int offset = (y* width() + x); + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return; - // Set the background color at the offset, by masking the background color with the current background color (bits 12-15) - m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0x0FFF) | ((uint16_t)background << 12); + // Calculate the offset + int offset = (y * width() + x); + // Set the background color at the offset, by masking the background color with the current background color (bits 12-15) + m_video_memory_meta[offset] = (m_video_memory_meta[offset] & 0x0FFF) | ((uint16_t) background << 12); } /** @@ -187,15 +181,15 @@ void VESABootConsole::set_background_color(uint16_t x, uint16_t y, ConsoleColour */ char VESABootConsole::get_character(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ' '; + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return ' '; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the character at the offset, by masking the character with the current character (last 8 bits) - return (char)(m_video_memory_meta[offset] & 0x00FF); + // Return the character at the offset, by masking the character with the current character (last 8 bits) + return (char) (m_video_memory_meta[offset] & 0x00FF); } /** @@ -207,15 +201,15 @@ char VESABootConsole::get_character(uint16_t x, uint16_t y) { */ ConsoleColour VESABootConsole::get_foreground_color(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ConsoleColour::White; + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return ConsoleColour::White; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) - return (ConsoleColour)((m_video_memory_meta[offset] & 0x0F00) >> 8); + // Return the foreground color at the offset, by masking the foreground color with the current foreground color (bits 8-11) + return (ConsoleColour) ((m_video_memory_meta[offset] & 0x0F00) >> 8); } /** @@ -227,15 +221,15 @@ ConsoleColour VESABootConsole::get_foreground_color(uint16_t x, uint16_t y) { */ ConsoleColour VESABootConsole::get_background_color(uint16_t x, uint16_t y) { - // If the coordinates are out of bounds, return - if(x >= width() || y >= height()) - return ConsoleColour::Black; + // If the coordinates are out of bounds, return + if (x >= width() || y >= height()) + return ConsoleColour::Black; - // Calculate the offset - int offset = (y* width() + x); + // Calculate the offset + int offset = (y * width() + x); - // Return the background color at the offset, by masking the background color with the current background color (bits 12-15) - return (ConsoleColour)((m_video_memory_meta[offset] & 0xF000) >> 12); + // Return the background color at the offset, by masking the background color with the current background color (bits 12-15) + return (ConsoleColour) ((m_video_memory_meta[offset] & 0xF000) >> 12); } /** @@ -243,29 +237,29 @@ ConsoleColour VESABootConsole::get_background_color(uint16_t x, uint16_t y) { */ void VESABootConsole::print_logo() { - // Load the logo - const char* logo = header_data; + // Load the logo + const char *logo = header_data; - // Find the center of the screen - uint32_t center_x = s_graphics_context->width()/2; - uint32_t center_y = s_graphics_context->height()/2 - 80; + // Find the center of the screen + uint32_t center_x = s_graphics_context->width() / 2; + uint32_t center_y = s_graphics_context->height() / 2 - 80; - // Draw the logo - for (uint32_t logoY = 0; logoY < logo_height; ++logoY) { - for (uint32_t logoX = 0; logoX < logo_width; ++logoX) { + // Draw the logo + for (uint32_t logoY = 0; logoY < logo_height; ++logoY) { + for (uint32_t logoX = 0; logoX < logo_width; ++logoX) { - // Store the pixel in the logo - uint8_t pixel[3] = {0}; + // Store the pixel in the logo + uint8_t pixel[3] = {0}; - // Get the pixel from the logo - LOGO_HEADER_PIXEL(logo, pixel) + // Get the pixel from the logo + LOGO_HEADER_PIXEL(logo, pixel) - // Draw the pixel - s_graphics_context->put_pixel(center_x - logo_width / 2 + logoX, - center_y - logo_height / 2 + logoY, - common::Colour(pixel[0], pixel[1], pixel[2])); - } - } + // Draw the pixel + s_graphics_context->put_pixel(center_x - logo_width / 2 + logoX, + center_y - logo_height / 2 + logoY, + common::Colour(pixel[0], pixel[1], pixel[2])); + } + } } @@ -281,57 +275,55 @@ void VESABootConsole::print_logo() { * @param fill The character to fill the new line with */ void VESABootConsole::scroll_up(uint16_t left, uint16_t top, uint16_t width, - uint16_t height, - common::ConsoleColour foreground, - common::ConsoleColour background, char) { - - - // Get the framebuffer info - auto* framebuffer_address = (uint8_t*)s_graphics_context->framebuffer_address(); - uint64_t framebuffer_width = s_graphics_context->width(); - uint64_t framebuffer_height = s_graphics_context->height(); - uint64_t framebuffer_bpp = s_graphics_context->color_depth(); // in bits per pixel - uint64_t bytes_per_pixel = framebuffer_bpp / 8; - uint64_t framebuffer_pitch = framebuffer_width * bytes_per_pixel; - - // Calculate the number of pixels per line - uint16_t line_height = Font::font_height; - - // Calculate the number of pixels in the region - uint16_t region_pixel_y = top * line_height; - uint16_t region_pixel_height = height * line_height; - uint16_t region_pixel_left = left * Font::font_width; - uint16_t region_pixel_width = width * Font::font_width; - size_t row_bytes = region_pixel_width * bytes_per_pixel; - - // Decide the colour of the pixel - ConsoleColour to_set_foreground = CPU::is_panicking ? ConsoleColour::White : get_foreground_color(left, top + height - 1); - ConsoleColour to_set_background = CPU::is_panicking ? ConsoleColour::Red : get_background_color(left, top + height - 1); - Colour fill_colour = Colour(to_set_background); - uint32_t fill_value = s_graphics_context->colour_to_int(to_set_background); - - // Scroll the region upward by one text line - for (uint16_t row = 0; row < region_pixel_height - line_height; row++) { - uint8_t* src = framebuffer_address + (region_pixel_y + row + line_height) * framebuffer_pitch + region_pixel_left * bytes_per_pixel; - uint8_t* dest = framebuffer_address + (region_pixel_y + row) * framebuffer_pitch + region_pixel_left * bytes_per_pixel; - memmove(dest, src, row_bytes); - } - - // Clear the last line of the region - uint16_t clear_start_y = region_pixel_y + region_pixel_height - line_height; - for (uint16_t row = 0; row < line_height; row++) { - auto row_add = (uint32_t*)(framebuffer_address + (clear_start_y + row) * framebuffer_pitch + region_pixel_left * 4); - for (uint16_t col = 0; col < region_pixel_width; col++) { - row_add[col] = fill_value; - } - } - - //Update any per-pixel colour metadata - uint16_t text_row = top + height - 1; - for (uint16_t x = left; x < left + width; x++) { - set_foreground_color(x, text_row, to_set_foreground); - set_background_color(x, text_row, to_set_background); - } + uint16_t height, + common::ConsoleColour foreground, + common::ConsoleColour background, char fill) { + + + // Get the framebuffer info + auto *framebuffer_address = (uint8_t *) s_graphics_context->framebuffer_address(); + uint64_t framebuffer_width = s_graphics_context->width(); + uint64_t framebuffer_bpp = s_graphics_context->color_depth(); // in bits per pixel + uint64_t bytes_per_pixel = framebuffer_bpp / 8; + uint64_t framebuffer_pitch = framebuffer_width * bytes_per_pixel; + + uint16_t line_height = Font::font_height; + + // Region conversions + uint16_t region_pixel_y = top * line_height; + uint16_t region_pixel_height = height * line_height; + uint16_t region_pixel_left = left * Font::font_width; + uint16_t region_pixel_width = width * Font::font_width; + size_t row_bytes = region_pixel_width * bytes_per_pixel; + + // Decide the colour of the pixel + ConsoleColour to_set_foreground = CPU::is_panicking ? ConsoleColour::White : get_foreground_color(left, top + height - 1); + ConsoleColour to_set_background = CPU::is_panicking ? ConsoleColour::Red : get_background_color(left, top + height - 1); + Colour fill_colour = Colour(to_set_background); + uint32_t fill_value = s_graphics_context->colour_to_int(to_set_background); + + // Scroll the region upward by one text line + for (uint16_t row = 0; row < region_pixel_height - line_height; row++) { + uint8_t *src = framebuffer_address + (region_pixel_y + row + line_height) * framebuffer_pitch + region_pixel_left * bytes_per_pixel; + uint8_t *dest = framebuffer_address + (region_pixel_y + row) * framebuffer_pitch + region_pixel_left * bytes_per_pixel; + memmove(dest, src, row_bytes); + } + + // Clear the last line of the region + uint16_t clear_start_y = region_pixel_y + region_pixel_height - line_height; + for (uint16_t row = 0; row < line_height; row++) { + auto row_add = (uint32_t *) (framebuffer_address + (clear_start_y + row) * framebuffer_pitch + region_pixel_left * 4); + for (uint16_t col = 0; col < region_pixel_width; col++) { + row_add[col] = fill_value; + } + } + + //Update any per-pixel colour metadata + uint16_t text_row = top + height - 1; + for (uint16_t x = left; x < left + width; x++) { + set_foreground_color(x, text_row, to_set_foreground); + set_background_color(x, text_row, to_set_background); + } } /** @@ -339,27 +331,27 @@ void VESABootConsole::scroll_up(uint16_t left, uint16_t top, uint16_t width, */ void VESABootConsole::print_logo_kernel_panic() { - // Load the logo - const char* logo = header_data_kp; + // Load the logo + const char *logo = header_data_kp; - // Find the bottom right of the screen - uint32_t right_x = s_graphics_context->width() - kp_width - 10; - uint32_t bottom_y = s_graphics_context->height() - kp_height - 10; + // Find the bottom right of the screen + uint32_t right_x = s_graphics_context->width() - kp_width - 10; + uint32_t bottom_y = s_graphics_context->height() - kp_height - 10; - // Draw the logo - for (uint32_t logoY = 0; logoY < kp_height; ++logoY) { - for (uint32_t logoX = 0; logoX < kp_width; ++logoX) { + // Draw the logo + for (uint32_t logoY = 0; logoY < kp_height; ++logoY) { + for (uint32_t logoX = 0; logoX < kp_width; ++logoX) { - // Store the pixel in the logo - uint8_t pixel[3] = {0}; + // Store the pixel in the logo + uint8_t pixel[3] = {0}; - // Get the pixel from the logo - LOGO_HEADER_PIXEL(logo, pixel) + // Get the pixel from the logo + LOGO_HEADER_PIXEL(logo, pixel) - // Draw the pixel - s_graphics_context->put_pixel(right_x + logoX, bottom_y + logoY, common::Colour(pixel[0], pixel[1], pixel[2])); - } - } + // Draw the pixel + s_graphics_context->put_pixel(right_x + logoX, bottom_y + logoY, common::Colour(pixel[0], pixel[1], pixel[2])); + } + } } @@ -368,15 +360,14 @@ void VESABootConsole::print_logo_kernel_panic() { */ void VESABootConsole::finish() { - // Done - Logger::INFO() << "MaxOS Kernel Successfully Booted\n"; - - // Move COUT to the bottom of the screen - cout->set_cursor(width(), height()); + // Done + Logger::HEADER() << "MaxOS Kernel Successfully Booted\n"; - // Disable the logger - Logger::active_logger()->disable_log_writer(cout); + // CPU::PANIC will override a disabled logger so the console should scroll itself into view as it is unknown what + // will be on the screen now and that may mess with the presentation of the text (ie white text on a white background) + cout->set_cursor(width(), height()); + Logger::active_logger()->disable_log_writer(cout); } /** @@ -386,48 +377,45 @@ void VESABootConsole::finish() { */ void VESABootConsole::update_progress_bar(uint8_t percentage) { - // Must be within bounds - if(percentage > 100) - percentage = 100; + // Check bounds + if (percentage > 100) + percentage = 100; - // Must have a valid graphics context - if(s_graphics_context == nullptr) - return; + // Must have a valid graphics context + if (s_graphics_context == nullptr) + return; - uint8_t progress_height = 15; - uint8_t progress_spacing = 20; - uint8_t progress_width_cull = 40; + uint8_t progress_height = 15; + uint8_t progress_spacing = 20; + uint8_t progress_width_cull = 40; - // Find the center of the screen - uint32_t right_x = (s_graphics_context->width()/2) - logo_width / 2; - uint32_t bottom_y = (s_graphics_context->height()/2 - 80) - logo_height / 2; + // Find the center of the screen + uint32_t right_x = (s_graphics_context->width() / 2) - logo_width / 2; + uint32_t bottom_y = (s_graphics_context->height() / 2 - 80) - logo_height / 2; - // Find the bounds - uint32_t start_x = progress_width_cull; - uint32_t start_y = logo_height + progress_spacing; - uint32_t end_x = logo_width - progress_width_cull; - uint32_t end_y = logo_height + progress_height + progress_spacing; + // Find the bounds + uint32_t start_x = progress_width_cull; + uint32_t start_y = logo_height + progress_spacing; + uint32_t end_x = logo_width - progress_width_cull; + uint32_t end_y = logo_height + progress_height + progress_spacing; - // Draw the progress bar - for (uint32_t progress_y = start_y; progress_y < end_y; ++progress_y) { - for (uint32_t progress_x = start_x; progress_x < end_x; ++progress_x) { + // Draw the progress bar + for (uint32_t progress_y = start_y; progress_y < end_y; ++progress_y) { + for (uint32_t progress_x = start_x; progress_x < end_x; ++progress_x) { - // Check if drawing border - bool is_border = (progress_y == start_y) || (progress_y == end_y - 1) || - (progress_x == start_x) || (progress_x == end_x - 1); + // Check if drawing border + bool is_border = (progress_y == start_y) || (progress_y == end_y - 1) || + (progress_x == start_x) || (progress_x == end_x - 1); - // Only draw the border if it is the first time drawing it - is_border = is_border && percentage == 0; + // Only draw the border if it is the first time drawing it + is_border = is_border && percentage == 0; - // If it is not within the percentage, skip it - if (progress_x > logo_width * percentage / 100 && !is_border) - continue; + // If it is not within the percentage, skip it + if (progress_x > logo_width * percentage / 100 && !is_border) + continue; + s_graphics_context->put_pixel(right_x + progress_x, bottom_y + progress_y, Colour(0xFF, 0xFF, 0xFF)); - // Draw the pixel - s_graphics_context->put_pixel(right_x + progress_x, bottom_y + progress_y, Colour(0xFF, 0xFF, 0xFF)); - - } - } - -} + } + } +} \ No newline at end of file diff --git a/kernel/src/drivers/disk/ata.cpp b/kernel/src/drivers/disk/ata.cpp new file mode 100644 index 00000000..9365bcb3 --- /dev/null +++ b/kernel/src/drivers/disk/ata.cpp @@ -0,0 +1,247 @@ +// +// Created by 98max on 24/10/2022. +// + +#include + +using namespace MaxOS; +using namespace MaxOS::common; +using namespace MaxOS::hardwarecommunication; +using namespace MaxOS::drivers; +using namespace MaxOS::drivers::disk; + +AdvancedTechnologyAttachment::AdvancedTechnologyAttachment(uint16_t port_base, bool master) +: m_data_port(port_base), + m_error_port(port_base + 1), + m_sector_count_port(port_base + 2), + m_LBA_low_port(port_base + 3), + m_LBA_mid_port(port_base + 4), + m_LBA_high_Port(port_base + 5), + m_device_port(port_base + 6), + m_command_port(port_base + 7), + m_control_port(port_base + 0x206), + m_is_master(master) +{ + +} + +AdvancedTechnologyAttachment::~AdvancedTechnologyAttachment() = default; + +/** + * @brief Identify the ATA device + * + * @return True if the device is present, false otherwise + */ +bool AdvancedTechnologyAttachment::identify() { + + // Select the device (master or slave) + m_device_port.write(m_is_master ? 0xA0 : 0xB0); + + // Reset the High Order Byte + m_control_port.write(0); + + // Check if the master is present + m_device_port.write(0xA0); + uint8_t status = m_command_port.read(); + if (status == 0xFF) { + Logger::WARNING() << "ATA Device: Invalid status"; + return false; + } + + // Select the device (master or slave) + m_device_port.write(m_is_master ? 0xA0 : 0xB0); + + // Clear the ports + m_sector_count_port.write(0); + m_LBA_low_port.write(0); + m_LBA_mid_port.write(0); + m_LBA_high_Port.write(0); + + // Check if the device is present + m_command_port.write(0x0EC); + status = m_command_port.read(); + if (status == 0x00) + return false; + + // Wait for the device to be ready or for an error to occur + while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) + status = m_command_port.read(); + + //Check for any errors + if (status & 0x01) { + Logger::WARNING() << "ATA Device: Error reading status\n"; + return false; + } + + // Read the rest of the data as a whole sector needs to be read + for (uint16_t i = 0; i < 256; ++i) + uint16_t data = m_data_port.read(); + + // Device is present and ready + return true; +} + +/** + * @brief Read a sector from the ATA device + * + * @param sector The sector to read + * @param data_buffer The data to read into + * @param amount The amount of bytes to read from that sector + */ +void AdvancedTechnologyAttachment::read(uint32_t sector, buffer_t *data_buffer, size_t amount) { + + // Don't allow reading more than a sector + if (sector & 0xF0000000 || amount > m_bytes_per_sector) + return; + + // Select the device (master or slave) + m_device_port.write((m_is_master ? 0xE0 : 0xF0) | ((sector & 0x0F000000) >> 24)); + + // Device is busy (TODO: YIELD) + while ((m_command_port.read() & 0x80) != 0); + + // Reset the device + m_error_port.write(0); + m_sector_count_port.write(1); + + // Split the sector into the ports + m_LBA_low_port.write(sector & 0x000000FF); + m_LBA_mid_port.write((sector & 0x0000FF00) >> 8); + m_LBA_high_Port.write((sector & 0x00FF0000) >> 16); + + // Tell the device to prepare for reading + m_command_port.write(0x20); + + // Make sure the device is there + uint8_t status = m_command_port.read(); + if (status == 0x00) + return; + + // Wait for the device to be ready or for an error to occur TODO: Userspace block here + while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) + status = m_command_port.read(); + + //Check for any errors + if (status & 0x01) + return; + + for (size_t i = 0; i < amount; i += 2) { + + // Read from the disk (2 bytes) and store the first byte + uint16_t read_data = m_data_port.read(); + data_buffer->write(read_data & 0x00FF); + + // Place the second byte in the array if there is one + if (i + 1 < amount) + data_buffer->write((read_data >> 8) & 0x00FF); + } + + // Read the remaining bytes as a full sector has to be read + for (uint16_t i = amount + (amount % 2); i < m_bytes_per_sector; i += 2) + m_data_port.read(); +} + +/** + * @brief write to a sector on the ATA device + * + * @param sector The sector to write to + * @param data The data to write + * @param count The amount of data to write to that sector + */ +void AdvancedTechnologyAttachment::write(uint32_t sector, const buffer_t *data, size_t count) { + + // Don't allow writing more than a sector + if (sector > 0x0FFFFFFF || count > m_bytes_per_sector) + return; + + // Select the device (master or slave) + m_device_port.write(m_is_master ? 0xE0 : 0xF0 | ((sector & 0x0F000000) >> 24)); + + // Device is busy (TODO: YIELD) + while ((m_command_port.read() & 0x80) != 0); + + // Reset the device + m_error_port.write(0); + m_sector_count_port.write(1); + + // Split the sector into the ports + m_LBA_low_port.write(sector & 0x000000FF); + m_LBA_mid_port.write((sector & 0x0000FF00) >> 8); + m_LBA_high_Port.write((sector & 0x00FF0000) >> 16); + + // Send the write command + m_command_port.write(0x30); + + // Wait for the device be ready writing (TODO: YIELD) + uint8_t status = m_command_port.read(); + while ((status & 0x80) != 0 || (status & 0x08) == 0) + status = m_command_port.read(); + + // Write the data to the device + for (uint16_t i = 0; i < m_bytes_per_sector; i += 2) { + + uint16_t writeData = data->read(); + + // Place the next byte in the array if there is one + if (i + 1 < count) + writeData |= (uint16_t) (data->read()) << 8; + + m_data_port.write(writeData); + } + + // Write the remaining bytes as a full sector has to be written + for (int i = count + (count % 2); i < m_bytes_per_sector; i += 2) + m_data_port.write(0x0000); + + // Wait for the device to finish writing (TODO: YIELD) + status = m_command_port.read(); + while ((status & 0x80) != 0 || (status & 0x08) != 0) + status = m_command_port.read(); + + flush(); +} + +/** + * @brief Flush the cache of the ATA device + */ +void AdvancedTechnologyAttachment::flush() { + + // Select the device (master or slave) + m_device_port.write(m_is_master ? 0xE0 : 0xF0); + + // Send the flush command + m_command_port.write(0xE7); + + // Make sure the device is there + uint8_t status = m_command_port.read(); + if (status == 0x00) + return; + + // Wait for the device to be ready or for an error to occur + while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) + status = m_command_port.read(); + + // Check for an error + if (status & 0x01) + return; + + // ... +} + +/** + * @brief Get the device name + * + * @return The name of the device + */ +string AdvancedTechnologyAttachment::device_name() { + return "Advanced Technology Attachment"; +} + +/** + * @brief Get the vendor name + * + * @return The name of the vendor + */ +string AdvancedTechnologyAttachment::vendor_name() { + return "IDE"; +} diff --git a/kernel/src/drivers/disk/disk.cpp b/kernel/src/drivers/disk/disk.cpp new file mode 100644 index 00000000..ed97099a --- /dev/null +++ b/kernel/src/drivers/disk/disk.cpp @@ -0,0 +1,88 @@ +// +// Created by Max Tyson on 18/04/2025. +// + +#include + +using namespace MaxOS; +using namespace MaxOS::common; +using namespace MaxOS::drivers; +using namespace MaxOS::drivers::disk; + +Disk::Disk() = default; + +Disk::~Disk() = default; + +/** + * @brief Read data from the disk into a buffer (max capacity 512 bytes) + * + * @param sector The sector to read from + * @param data_buffer The buffer to read the data into + */ +void Disk::read(uint32_t sector, common::buffer_t* data_buffer) { + + size_t amount = (data_buffer->capacity() > 512) ? 512 : data_buffer->capacity(); + read(sector, data_buffer, amount); + +} + +/** + * @brief Read data from the disk + * + * @param sector The sector to read from + * @param data_buffer The buffer to read the data into + * @param amount The amount of data to read + */ +void Disk::read(uint32_t sector, buffer_t *data_buffer, size_t amount) { + +} + +void Disk::write(uint32_t sector, common::buffer_t const *data) { + + size_t amount = (data->capacity() > 512) ? 512 : data->capacity(); + write(sector, data, amount); + +} + +/** + * @brief Write data to the disk + * + * @param sector The sector to write to + * @param data_buffer The buffer to write the data into + * @param amount The amount of data to write + */ +void Disk::write(uint32_t sector, const buffer_t *data, size_t count) { +} + +/** + * @brief Flush the disk cache + * + * This function is used to flush the disk cache to ensure that all data is written to the disk. + */ +void Disk::flush() { +} + +/** + * @brief Activate the disk driver + */ +void Disk::activate() { + Driver::activate(); +} + +/** + * @brief Get the device name + * + * @return The name of the device + */ +string Disk::device_name() { + return "Disk"; +} + +/** + * @brief Get the vendor name + * + * @return The name of the vendor + */ +string Disk::vendor_name() { + return "Generic"; +} \ No newline at end of file diff --git a/kernel/src/drivers/disk/disk/ata.cpp b/kernel/src/drivers/disk/disk/ata.cpp deleted file mode 100644 index 768dadc0..00000000 --- a/kernel/src/drivers/disk/disk/ata.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// -// Created by 98max on 24/10/2022. -// - -#include - -using namespace MaxOS; -using namespace MaxOS::common; -using namespace MaxOS::hardwarecommunication; -using namespace MaxOS::drivers; -using namespace MaxOS::drivers::disk; - -AdvancedTechnologyAttachment::AdvancedTechnologyAttachment(uint16_t port_base, bool master, OutputStream*output_stream) -: Driver(output_stream), - m_data_port(port_base), - m_error_port(port_base + 1), - m_sector_count_port(port_base + 2), - m_LBA_low_port(port_base + 3), - m_LBA_mid_port(port_base + 4), - m_LBA_high_Port(port_base + 5), - m_device_port(port_base + 6), - m_command_port(port_base + 7), - m_control_port(port_base + 0x206), - m_is_master(master), - ata_message_stream(output_stream) -{ - -} - -AdvancedTechnologyAttachment::~AdvancedTechnologyAttachment() = default; - -/** - * @brief Identify the ATA device - */ -void AdvancedTechnologyAttachment::identify() { - - // Select the device (master or slave) - m_device_port.write(m_is_master ? 0xA0 : 0xB0); - - // Reset the HOB (High Order Byte) - m_control_port.write(0); - - // Check if the master is present - m_device_port.write(0xA0); - uint8_t status = m_command_port.read(); - if(status == 0xFF){ - ata_message_stream-> write("Invalid Status"); - return; - } - - // Select the device (master or slave) - m_device_port.write(m_is_master ? 0xA0 : 0xB0); - - // Clear the ports - m_sector_count_port.write(0); - m_LBA_low_port.write(0); - m_LBA_mid_port.write(0); - m_LBA_high_Port.write(0); - - // Send the identify command - m_command_port.write(0x0EC); - - // Check if the device is present - status = m_command_port.read(); - if(status == 0x00) - return; - - // Wait for the device to be ready or for an error to occur - while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) - status = m_command_port.read(); - - //Check for any errors - if(status & 0x01){ - ata_message_stream-> write("ERROR"); - return; - } - - // read the data and print it - for (uint16_t i = 0; i < 256; ++i) { - uint16_t data = m_data_port.read(); - ata_message_stream-> write(" 0x"); - ata_message_stream-> write_hex(data); - } -} - -/** - * @brief read a sector from the ATA device - * - * @param sector The sector to read - * @param data The data to read into - * @param count The amount of data to read from that sector - */ -void AdvancedTechnologyAttachment::read_28(uint32_t sector, uint8_t* data, int count) -{ - // Don't allow reading more than a sector - if(sector & 0xF0000000 || count > m_bytes_per_sector) - return; - - // Select the device (master or slave) and reset it - m_device_port.write((m_is_master ? 0xE0 : 0xF0) | - ((sector & 0x0F000000) >> 24)); - m_error_port.write(0); - m_sector_count_port.write(1); - - // Split the sector into the ports - m_LBA_low_port.write(sector & 0x000000FF); - m_LBA_mid_port.write((sector & 0x0000FF00) >> 8); - m_LBA_high_Port.write((sector & 0x00FF0000) >> 16); - - // Send the read command - m_command_port.write(0x20); - - // Make sure the device is there - uint8_t status = m_command_port.read(); - if(status == 0x00) - return; - - // Wait for the device to be ready or for an error to occur - while(((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) - status = m_command_port.read(); - - //Check for any errors - if(status & 0x01) - return; - - // read the data and store it in the array - for(int i = 0; i < count; i+= 2) - { - uint16_t read_data = m_data_port.read(); - - data[i] = read_data & 0x00FF; - - // Place the next byte in the array if there is one - if(i+1 < count) - data[i+1] = (read_data >> 8) & 0x00FF; - } - - // read the remaining bytes - for(uint16_t i = count + (count % 2); i < m_bytes_per_sector; i+= 2) - m_data_port.read(); -} - -/** - * @brief write to a sector on the ATA device - * - * @param sector The sector to write to - * @param count The amount of data to write to that sector - */ -void AdvancedTechnologyAttachment::write_28(uint32_t sector, const uint8_t* data, int count){ - - // Don't allow writing more han a sector - if(sector > 0x0FFFFFFF || count > m_bytes_per_sector) - return; - - // Select the device (master or slave) and reset it - m_device_port.write(m_is_master ? 0xE0 - : 0xF0 | ((sector & 0x0F000000) >> 24)); - m_error_port.write(0); - m_sector_count_port.write(1); - - // Split the sector into the ports - m_LBA_low_port.write(sector & 0x000000FF); - m_LBA_mid_port.write((sector & 0x0000FF00) >> 8); - m_LBA_high_Port.write((sector & 0x00FF0000) >> 16); - - // Send the write command - m_command_port.write(0x30); - - // write the data to the device - for (uint16_t i = 0; i < m_bytes_per_sector; i+= 2) { - - uint16_t writeData = data[i]; - - // Place the next byte in the array if there is one - if(i+1 < count) - writeData |= ((uint16_t)data[i+1]) << 8; - - m_data_port.write(writeData); - } - - // write the remaining bytes - for(int i = count + (count%2); i < m_bytes_per_sector; i += 2) - m_data_port.write(0x0000); -} -/** - * @brief Flush the cache of the ATA device - */ -void AdvancedTechnologyAttachment::flush() { - - // Select the device (master or slave) - m_device_port.write(m_is_master ? 0xE0 : 0xF0); - - // Send the flush command - m_command_port.write(0xE7); - - // Make sure the device is there - uint8_t status = m_command_port.read(); - if(status == 0x00) - return; - - - // Wait for the device to be ready or for an error to occur - while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01)) - status = m_command_port.read(); - - if(status & 0x01) - return; - -} - -/** - * @brief Activate the ATA device - */ -void AdvancedTechnologyAttachment::activate() { - Driver::activate(); -} - -/** - * @brief Get the device name - * - * @return The name of the device - */ -string AdvancedTechnologyAttachment::device_name() { - - return "Advanced Technology Attachment"; - -} - -/** - * @brief Get the vendor name - * - * @return The name of the vendor - */ -string AdvancedTechnologyAttachment::vendor_name() { - - return "IDE"; -} diff --git a/kernel/src/drivers/disk/ide.cpp b/kernel/src/drivers/disk/ide.cpp new file mode 100644 index 00000000..404ee9be --- /dev/null +++ b/kernel/src/drivers/disk/ide.cpp @@ -0,0 +1,94 @@ +// +// Created by Max Tyson on 18/04/2025. +// +#include + + +using namespace MaxOS; +using namespace MaxOS::hardwarecommunication; +using namespace MaxOS::drivers; +using namespace MaxOS::drivers::disk; +using namespace MaxOS::filesystem; +using namespace MaxOS::filesystem::partition; + +IntegratedDriveElectronicsController::IntegratedDriveElectronicsController( +PeripheralComponentInterconnectDeviceDescriptor *device_descriptor) +{ + // TODO: Use the device descriptor to get the port base and add the devices dynamically + + // Primary + auto primary_maser = new AdvancedTechnologyAttachment(0x1F0, true); + auto primary_slave = new AdvancedTechnologyAttachment(0x1F0, false); + devices.insert(primary_maser, true); + devices.insert(primary_slave, false); + + // Secondary + auto secondary_maser = new AdvancedTechnologyAttachment(0x170, true); + auto secondary_slave = new AdvancedTechnologyAttachment(0x170, false); + devices.insert(secondary_maser, true); + devices.insert(secondary_slave, false); + +} + +IntegratedDriveElectronicsController::~IntegratedDriveElectronicsController() = default; + +/** + * @brief Initialise the IDE controller by identifying the devices + */ +void IntegratedDriveElectronicsController::initialise() { + + // Loop through the devices and identify them + for (auto &device: devices) { + + // Check if the device is present + auto ata_device = device.first; + if (ata_device == nullptr) + continue; + + // Identify the device + bool exists = ata_device->identify(); + + // Remove the device if it does not exist + if (!exists) { + devices.erase(ata_device); + delete ata_device; + continue; + } + } + + // Log the init done + Logger::DEBUG() << "IDE Controller: Initialised " << devices.size() << " devices\n"; +} + +/** + * @brief Activate the IDE controller by mounting the devices to the virtual file system + */ +void IntegratedDriveElectronicsController::activate() { + + // Loop through the devices and load the partitions + for (auto &device: devices) { + + // Ensure there is a device and that it is the master + if (device.first == nullptr || !device.second) + continue; + + // Mount the device + MSDOSPartition::mount_partitions(device.first); + + } +} + + +/** + * @brief Get the vendor name + */ +string IntegratedDriveElectronicsController::vendor_name() { + return "Intel"; +} + +/** + * @brief Get the device name + */ +string IntegratedDriveElectronicsController::device_name() { + return "PIIX4"; +} \ No newline at end of file diff --git a/kernel/src/drivers/driver.cpp b/kernel/src/drivers/driver.cpp index 0a9c2261..619054a1 100644 --- a/kernel/src/drivers/driver.cpp +++ b/kernel/src/drivers/driver.cpp @@ -4,32 +4,28 @@ #include #include + using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::drivers; using namespace MaxOS::memory; using namespace MaxOS::hardwarecommunication; -Driver::Driver(OutputStream* driverMessageStream) -: m_driver_message_stream(driverMessageStream) { +Driver::Driver() = default; -} - -Driver::~Driver(){ - this ->m_driver_message_stream = nullptr; -} +Driver::~Driver() = default; /** * @brief activate the driver */ -void Driver::activate(){ +void Driver::activate() { } /** * @brief deactivate the driver */ -void Driver::deactivate(){ +void Driver::deactivate() { } @@ -45,70 +41,17 @@ void Driver::initialise() { * * @return How long in milliseconds it took to reset the driver */ -uint32_t Driver::reset(){ - return 0; +uint32_t Driver::reset() { + return 0; } /** - * @brief write a message to the driver message stream if it is not null - * - * @param message The message to write - */ -void Driver::error_message(const string& message) const { - - // If there is a driver message stream write the message to it - if(m_driver_message_stream != nullptr) - m_driver_message_stream-> write(message); - -} - -/** - * @brief write a character to the driver message stream if it is not null - * - * @param char_to_write The character to write - */ -void Driver::error_message(char char_to_write) const { - - // If there is a driver message stream write the character to it - if(m_driver_message_stream != nullptr) - m_driver_message_stream-> write_char(char_to_write); - -} - - -/** - * @brief write an integer to the driver message stream if it is not null - * - * @param int_to_write The integer to write - */ -void Driver::error_message(int int_to_write) const { - - // If there is a driver message stream write the integer to it - if(m_driver_message_stream != nullptr) - m_driver_message_stream-> write_int(int_to_write); -} - -/** - * @brief write a hex to the driver message stream if it is not null - * - * @param hex_to_write The hex to write - */ -void Driver::error_message(uint32_t hex_to_write) const { - - // If there is a driver message stream write the hex to it - if(m_driver_message_stream != nullptr) - m_driver_message_stream->write_hex(hex_to_write); - -} - -/** - * @brief Get the vendor name of the driver + * @brief Get who created the device * * @return The vendor name of the driver */ -string Driver::vendor_name() -{ - return "Generic"; +string Driver::vendor_name() { + return "Generic"; } /** @@ -116,57 +59,49 @@ string Driver::vendor_name() * * @return The device name of the driver */ -string Driver::device_name() -{ - return "Unknown Driver"; +string Driver::device_name() { + return "Unknown Driver"; } -DriverSelectorEventHandler::DriverSelectorEventHandler() -= default; +DriverSelectorEventHandler::DriverSelectorEventHandler() = default; -DriverSelectorEventHandler::~DriverSelectorEventHandler() -= default; +DriverSelectorEventHandler::~DriverSelectorEventHandler() = default; /** * @brief This function is called when a driver is selected * * @param driver The driver that was selected */ -void DriverSelectorEventHandler::on_driver_selected(Driver*) -{ +void DriverSelectorEventHandler::on_driver_selected(Driver *) { } -DriverSelector::DriverSelector() -= default; +DriverSelector::DriverSelector() = default; -DriverSelector::~DriverSelector() -= default; +DriverSelector::~DriverSelector() = default; /** * @brief Select the drivers */ -void DriverSelector::select_drivers(DriverSelectorEventHandler*) -{ +void DriverSelector::select_drivers(DriverSelectorEventHandler *) { } -DriverManager::DriverManager() -{ +DriverManager::DriverManager() { - Logger::INFO() << "Setting up Driver Manager \n"; - add_driver_selector(new PeripheralComponentInterconnectController); - // add_driver_selector(new UniversalSerialBusController); + Logger::INFO() << "Setting up Driver Manager \n"; + add_driver_selector(new PeripheralComponentInterconnectController); + // add_driver_selector(new UniversalSerialBusController); } DriverManager::~DriverManager() { - // Remove any drivers that are still attached - while (!m_drivers.empty()) - remove_driver(*m_drivers.begin()); + // Remove any drivers that are still attached + while (!m_drivers.empty()) + remove_driver(*m_drivers.begin()); - // Free the driver selectors - for(auto & driver_selector : m_driver_selectors) - delete driver_selector; + // Free the driver selectors + for (auto &driver_selector: m_driver_selectors) + delete driver_selector; } @@ -175,8 +110,8 @@ DriverManager::~DriverManager() { * * @param driver The driver to add */ -void DriverManager::add_driver(Driver* driver){ - m_drivers.push_back(driver); +void DriverManager::add_driver(Driver *driver) { + m_drivers.push_back(driver); } /** @@ -184,40 +119,35 @@ void DriverManager::add_driver(Driver* driver){ * * @param driver The driver to remove */ -void DriverManager::remove_driver(Driver* driver) { - - // deactivate the driver - driver -> deactivate(); +void DriverManager::remove_driver(Driver *driver) { - // Remove the driver - m_drivers.erase(driver); + driver->deactivate(); + m_drivers.erase(driver); } /** * @brief When a driver is selected add it to the manager */ -void DriverManager::on_driver_selected(Driver* driver) { - add_driver(driver); +void DriverManager::on_driver_selected(Driver *driver) { + add_driver(driver); } /** * @brief Add a driver selector to the manager */ -void DriverManager::add_driver_selector(DriverSelector* driver_selector) { +void DriverManager::add_driver_selector(DriverSelector *driver_selector) { - // Add the driver selector - m_driver_selectors.push_back(driver_selector); + m_driver_selectors.push_back(driver_selector); } /** * @brief Remove a driver selector from the manager */ -void DriverManager::remove_driver_selector(DriverSelector* driver_selector) { +void DriverManager::remove_driver_selector(DriverSelector *driver_selector) { - // Remove the driver selector - m_driver_selectors.erase(driver_selector); + m_driver_selectors.erase(driver_selector); } @@ -226,11 +156,11 @@ void DriverManager::remove_driver_selector(DriverSelector* driver_selector) { */ void DriverManager::find_drivers() { - Logger::INFO() << "Finding Drivers \n"; + Logger::INFO() << "Finding Drivers \n"; - // Select the drivers - for(auto & driver_selector : m_driver_selectors) - driver_selector -> select_drivers(this); + // Select the drivers + for (auto &driver_selector: m_driver_selectors) + driver_selector->select_drivers(this); } /** @@ -240,20 +170,19 @@ void DriverManager::find_drivers() { */ uint32_t DriverManager::reset_devices() { - Logger::INFO() << "Resetting Devices \n"; + Logger::INFO() << "Resetting Devices \n"; - uint32_t resetWaitTime = 0; - for(auto & driver : m_drivers) - { - // Reset the driver - uint32_t waitTime = driver->reset(); + uint32_t resetWaitTime = 0; + for (auto &driver: m_drivers) { + // Reset the driver + uint32_t waitTime = driver->reset(); - // If the wait time is longer than the current longest wait time, set it as the new longest wait time - if(waitTime > resetWaitTime) - resetWaitTime = waitTime; - } + // If the wait time is longer than the current longest wait time, set it as the new longest wait time + if (waitTime > resetWaitTime) + resetWaitTime = waitTime; + } - return resetWaitTime; + return resetWaitTime; } /** @@ -261,11 +190,11 @@ uint32_t DriverManager::reset_devices() { */ void DriverManager::initialise_drivers() { - Logger::INFO() << "Initialising Drivers \n"; + Logger::INFO() << "Initialising Drivers \n"; - // Initialise the drivers - for(auto& driver : m_drivers) - driver->initialise(); + // Initialise the drivers + for (auto &driver: m_drivers) + driver->initialise(); } @@ -274,9 +203,9 @@ void DriverManager::initialise_drivers() { */ void DriverManager::deactivate_drivers() { - // Deactivate the drivers - for(auto & driver : m_drivers) - driver->deactivate(); + // Deactivate the drivers + for (auto &driver: m_drivers) + driver->deactivate(); } @@ -286,10 +215,10 @@ void DriverManager::deactivate_drivers() { void DriverManager::activate_drivers() { - Logger::INFO() << "Activating Drivers \n"; + Logger::INFO() << "Activating Drivers \n"; - // Activate the drivers - for(auto & driver : m_drivers) - driver->activate(); + // Activate the drivers + for (auto &driver: m_drivers) + driver->activate(); } diff --git a/kernel/src/drivers/ethernet/amd_am79c973.cpp b/kernel/src/drivers/ethernet/amd_am79c973.cpp index 578b2327..01962c7c 100644 --- a/kernel/src/drivers/ethernet/amd_am79c973.cpp +++ b/kernel/src/drivers/ethernet/amd_am79c973.cpp @@ -11,9 +11,12 @@ using namespace MaxOS::drivers; using namespace MaxOS::drivers::ethernet; using namespace MaxOS::hardwarecommunication; -AMD_AM79C973::AMD_AM79C973(PeripheralComponentInterconnectDeviceDescriptor *dev, OutputStream *amdNetMessageStream) -: EthernetDriver(amdNetMessageStream), - InterruptHandler(0x20 + dev -> interrupt), +/// MAX OS NET CODE: +/// All the old (this) networking code poorly written and not used, this will be moved to userspace in the future +/// but is kept here as a reference for now. + +AMD_AM79C973::AMD_AM79C973(PeripheralComponentInterconnectDeviceDescriptor *dev) +: InterruptHandler(0x20 + dev -> interrupt), MACAddress0Port(dev ->port_base), MACAddress2Port(dev ->port_base + 0x02), MACAddress4Port(dev ->port_base + 0x04), @@ -169,13 +172,13 @@ void AMD_AM79C973::handle_interrupt() { // Errors if((temp & 0x8000) == 0x8000) - error_message("AMD am79c973 ERROR: "); + Logger::WARNING() << "AMD am79c973 ERROR: "; if((temp & 0x2000) == 0x2000) - error_message("COLLISION ERROR\n"); + Logger::WARNING() << "COLLISION ERROR\n"; if((temp & 0x1000) == 0x1000) - error_message("MISSED FRAME\n"); + Logger::WARNING() << "MISSED FRAME\n"; if((temp & 0x0800) == 0x0800) - error_message("MEMORY ERROR\n"); + Logger::WARNING() << "MEMORY ERROR\n"; // Responses diff --git a/kernel/src/drivers/ethernet/ethernet.cpp b/kernel/src/drivers/ethernet/ethernet.cpp index 0773fcff..7cfb102d 100644 --- a/kernel/src/drivers/ethernet/ethernet.cpp +++ b/kernel/src/drivers/ethernet/ethernet.cpp @@ -59,13 +59,8 @@ Event* EthernetDriverEventHandler::on_event(Event write("Sending: "); - - int displayType = 34; //What header to hide (Ethernet Header = 14, IP Header = 34, UDP = 42, TCP Header = 54, ARP = 42) - for(uint32_t i = displayType; i < size; i++) - { - m_driver_message_stream->write_hex(buffer[i]); - m_driver_message_stream-> write(" "); - } - m_driver_message_stream-> write("\n"); // Raise the event raise_event(new BeforeSendEvent(buffer, size)); @@ -116,15 +102,6 @@ void EthernetDriver::DoSend(uint8_t*, uint32_t) */ void EthernetDriver::FireDataReceived(uint8_t* buffer, uint32_t size) { - m_driver_message_stream-> write("Receiving: "); - //size = 64; - int displayType = 34; //What header to hide (Ethernet Header = 14, IP Header = 34, UDP = 42, TCP Header = 54, ARP = 42) - for(uint32_t i = displayType; i < size; i++) - { - m_driver_message_stream->write_hex(buffer[i]); - m_driver_message_stream-> write(" "); - } - m_driver_message_stream-> write("\n"); // Raise the event Vector*> values = @@ -134,17 +111,14 @@ void EthernetDriver::FireDataReceived(uint8_t* buffer, uint32_t size) for(auto & value : values) { switch (value->type) { case EthernetDriverEvents::DATA_RECEIVED: - if(value->return_value.boolValue){ - m_driver_message_stream-> write("Sending back... \n"); + if(value->return_value.boolValue) Send(buffer, size); - } break; default: break; } } - m_driver_message_stream-> write("DATA HANDLED\n"); } /** diff --git a/kernel/src/drivers/ethernet/intel_i217.cpp b/kernel/src/drivers/ethernet/intel_i217.cpp index b295a26f..d9832764 100644 --- a/kernel/src/drivers/ethernet/intel_i217.cpp +++ b/kernel/src/drivers/ethernet/intel_i217.cpp @@ -11,6 +11,12 @@ using namespace MaxOS::drivers::ethernet; using namespace MaxOS::hardwarecommunication; using namespace memory; +/// MAX OS NET CODE: +/// All the old (this) networking code poorly written and not used, this will be moved to userspace in the future +/// but is kept here as a reference for now. +/// +/// See OSDEV wiki for the credit for this driver + // Buffer Sizes #define buffer256 (3 << 16) #define buffer512 (2 << 16) @@ -23,8 +29,7 @@ using namespace memory; ///__DRIVER___ intel_i217::intel_i217(PeripheralComponentInterconnectDeviceDescriptor *deviceDescriptor) -: EthernetDriver(nullptr), - InterruptHandler(0x20 + deviceDescriptor->interrupt) +: InterruptHandler(0x20 + deviceDescriptor->interrupt) { //Set the registers @@ -63,13 +68,10 @@ intel_i217::intel_i217(PeripheralComponentInterconnectDeviceDescriptor *deviceDe detectEEProm (); if (readMACAddress()){ - ownMAC = CreateMediaAccessControlAddress(macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]); }else{ - - error_message("ERROR, INIT FAILED, MAC ADDRESS NOT FOUND"); - while (true); + ASSERT(false, "ERROR, INIT FAILED, MAC ADDRESS NOT FOUND"); } for(int i = 0; i < 0x80; i++) //Loop through all the registers @@ -286,7 +288,6 @@ void intel_i217::sendInit() { void intel_i217::activate() { - m_driver_message_stream-> write("Activating Intel i217\n"); //Enable interrupts Write(interruptMaskRegister ,0x1F6DC); //Enable all interrupts @@ -300,7 +301,6 @@ void intel_i217::activate() { sendInit(); active = true; // Set active to true - m_driver_message_stream-> write("Intel i217 INIT DONE\n"); } @@ -309,18 +309,17 @@ void intel_i217::handle_interrupt() { Write(interruptMaskRegister, 0x1); //Clear the interrupt or it will hang uint32_t temp = Read(0xc0); //read the interrupt status register - m_driver_message_stream-> write("Interrupt from INTEL i217"); + // if(temp & 0x04) + // m_driver_message_stream-> write("INTEL i217 START LINK");//initDone = true; + // + // if(temp & 0x10) + // m_driver_message_stream-> write("INTEL i217 GOOD THRESHOLD"); - if(temp & 0x04) - m_driver_message_stream-> write("INTEL i217 START LINK");//initDone = true; - if(temp & 0x10) - m_driver_message_stream-> write("INTEL i217 GOOD THRESHOLD"); if(temp & 0x80) FetchDataReceived(); } void intel_i217::FetchDataReceived() { - m_driver_message_stream-> write("Fetching data... "); uint16_t old_cur; @@ -348,7 +347,6 @@ void intel_i217::FetchDataReceived() { void intel_i217::DoSend(uint8_t* buffer, uint32_t size) { - m_driver_message_stream-> write("Sending package... "); while(!active); //Put params into send buffer @@ -369,12 +367,10 @@ void intel_i217::DoSend(uint8_t* buffer, uint32_t size) { //Wait for the packet to be sent while(!(sendDsrctrs[old_cur]->status & 0xff)); - m_driver_message_stream-> write(" Done\n"); } uint64_t intel_i217::GetMediaAccessControlAddress() { - m_driver_message_stream-> write("Getting MAC address... "); while(ownMAC == 0); return ownMAC; diff --git a/kernel/src/drivers/peripherals/keyboard.cpp b/kernel/src/drivers/peripherals/keyboard.cpp index d1b4ff78..8ff75780 100644 --- a/kernel/src/drivers/peripherals/keyboard.cpp +++ b/kernel/src/drivers/peripherals/keyboard.cpp @@ -4,18 +4,13 @@ #include - - using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::drivers; using namespace MaxOS::drivers::peripherals; using namespace MaxOS::hardwarecommunication; - -///___Handler___ - -KeyboardEventHandler::KeyboardEventHandler()= default; +KeyboardEventHandler::KeyboardEventHandler() = default; KeyboardEventHandler::~KeyboardEventHandler() = default; @@ -25,8 +20,7 @@ KeyboardEventHandler::~KeyboardEventHandler() = default; * @param key_down_code The keycode of the key that was pressed * @param key_down_state The state of the keyboard when the key was pressed */ -void KeyboardEventHandler::on_key_down(KeyCode, KeyboardState) -{ +void KeyboardEventHandler::on_key_down(KeyCode, KeyboardState) { } /** @@ -35,8 +29,7 @@ void KeyboardEventHandler::on_key_down(KeyCode, KeyboardState) * @param key_up_code The keycode of the key that was released * @param key_up_state The state of the keyboard when the key was released */ -void KeyboardEventHandler::on_key_up(KeyCode, KeyboardState) -{ +void KeyboardEventHandler::on_key_up(KeyCode, KeyboardState) { } /** @@ -45,31 +38,25 @@ void KeyboardEventHandler::on_key_up(KeyCode, KeyboardState) * @param event The event to handle * @return The event that was passed with the data modified */ -Event* KeyboardEventHandler::on_event(Event *event) { +Event *KeyboardEventHandler::on_event(Event *event) { - switch (event -> type) { + switch (event->type) { - case KeyboardEvents::KEYDOWN: - this->on_key_down(((KeyDownEvent *)event)->key_code, - ((KeyDownEvent *)event)->keyboard_state); - break; + case KeyboardEvents::KEYDOWN: + this->on_key_down(((KeyDownEvent *) event)->key_code, ((KeyDownEvent *) event)->keyboard_state); + break; - case KeyboardEvents::KEYUP: - this->on_key_up(((KeyUpEvent *)event)->key_code, - ((KeyUpEvent *)event)->keyboard_state); - break; + case KeyboardEvents::KEYUP: + this->on_key_up(((KeyUpEvent *) event)->key_code, ((KeyUpEvent *) event)->keyboard_state); + break; - default: - break; - } + default: + break; + } - return event; + return event; } - - -///___Driver___ - KeyboardDriver::KeyboardDriver() : InterruptHandler(0x21, 0x1, 0x12), m_data_port(0x60), @@ -79,44 +66,41 @@ KeyboardDriver::KeyboardDriver() } -KeyboardDriver::~KeyboardDriver()= default; +KeyboardDriver::~KeyboardDriver() = default; /** * @brief activate the keyboard driver */ void KeyboardDriver::activate() { - // Wait for user to stop pressing key (this is for the start-up key e.g. hold 'F12' for boot menu or hold 'del' for bios ) - while (m_command_port.read() & 0x1) - m_data_port.read(); + // Wait for user to stop pressing key (this is for the start-up key e.g. hold 'F12' for boot menu or hold 'del' for bios ) + while (m_command_port.read() & 0x1) + m_data_port.read(); - // Enable keyboard interrupts - m_command_port.write(0xAE); + // Enable keyboard interrupts + m_command_port.write(0xAE); - // Get the current state of the keyboard - m_command_port.write(0x20); - uint8_t status = (m_data_port.read() | 1) & ~ 0x10; + // Get the current state of the keyboard + m_command_port.write(0x20); + uint8_t status = (m_data_port.read() | 1) & ~0x10; - // Reset the keyboard - m_command_port.write(0x60); - m_data_port.write(status); + // Reset the keyboard + m_command_port.write(0x60); + m_data_port.write(status); - // activate the keyboard - m_data_port.write(0xF4); + // Activate the keyboard + m_data_port.write(0xF4); } /** * @brief deactivate the keyboard driver */ -void KeyboardDriver::handle_interrupt(){ - - // read the scancode from the keyboard - uint8_t key = m_data_port.read(); +void KeyboardDriver::handle_interrupt() { - // Pass the scan code to the m_handlers - for(auto& handler : this -> m_input_stream_event_handlers){ - handler -> on_stream_read(key); - } + // Pass the scan code to the m_handlers + uint8_t key = m_data_port.read(); + for (auto &handler: m_input_stream_event_handlers) + handler->on_stream_read(key); } /** @@ -124,18 +108,13 @@ void KeyboardDriver::handle_interrupt(){ * @return The device name */ string KeyboardDriver::device_name() { - return "Keyboard"; + return "Keyboard"; } -///___State___ - - KeyboardState::KeyboardState() = default; KeyboardState::~KeyboardState() = default; -///___Interpreter___ - KeyboardInterpreter::KeyboardInterpreter() : InputStreamEventHandler() { @@ -144,18 +123,16 @@ KeyboardInterpreter::KeyboardInterpreter() KeyboardInterpreter::~KeyboardInterpreter() = default; -void KeyboardInterpreter::on_key_read(bool released, const KeyboardState& state, KeyCode key_code) { +void KeyboardInterpreter::on_key_read(bool released, const KeyboardState &state, KeyCode key_code) { - // Pass the key event to the handlers - if(released) - raise_event(new KeyUpEvent(key_code, state)); - else - raise_event(new KeyDownEvent(key_code, state)); + // Pass the key event to the handlers + if (released) + raise_event(new KeyUpEvent(key_code, state)); + else + raise_event(new KeyDownEvent(key_code, state)); } -///___Interpreter EN_US___ - KeyboardInterpreterEN_US::KeyboardInterpreterEN_US() : KeyboardInterpreter() { @@ -171,543 +148,491 @@ KeyboardInterpreterEN_US::~KeyboardInterpreterEN_US() = default; */ void KeyboardInterpreterEN_US::on_stream_read(uint8_t scan_code) { - // 0 is a regular key, 1 is an extended code, 2 is an extended code with e1CodeBuffer - int keyType = 0; - - // Check if the key was released - bool released = (scan_code & 0x80) && (m_current_extended_code_1 || (scan_code != 0xe1)) && (m_next_is_extended_code_0 || (scan_code != 0xe0)); - - // Clear the released bit - if (released) - scan_code &= ~0x80; - - // Set the e0Code flag to true - if (scan_code == 0xe0) - { - m_next_is_extended_code_0 = true; - return; - } - - // If e0Code is true, set keyType to 1 and reset e0Code - if (m_next_is_extended_code_0) - { - keyType = 1; - m_next_is_extended_code_0 = false; - - // Check if the scan_code represents a shift key and return (fake shift) - if ((KeyboardInterpreterEN_US::KeyCodeEN_US)scan_code == KeyCodeEN_US::leftShift || (KeyboardInterpreterEN_US::KeyCodeEN_US)scan_code == KeyCodeEN_US::rightShift) - return; - } - - // If the scan_code is 0xe1, set the e1Code flag to 1 and return - if (scan_code == 0xe1) - { - m_current_extended_code_1 = 1; - return; - } - - // If e1Code is 1, set e1Code to 2, store the scan_code in e1CodeBuffer, and return - if (m_current_extended_code_1 == 1) - { - m_current_extended_code_1 = 2; - m_extended_code_1_buffer = scan_code; - return; - } - - // If e1Code is 2, set keyType to 2, reset e1Code, and update e1CodeBuffer - if (m_current_extended_code_1 == 2) - { - keyType = 2; - m_current_extended_code_1 = 0; - m_extended_code_1_buffer |= (((uint16_t)scan_code) << 8); - } - - bool is_shifting = this ->m_keyboard_state.left_shift || this ->m_keyboard_state.right_shift; - bool should_be_upper_case = is_shifting != this ->m_keyboard_state.caps_lock; - - - // TODO: Probably a better way to do this (investigate when adding more keyboard layouts) - if(keyType == 0) - switch ((KeyCodeEN_US)scan_code) { - - // First row - case KeyCodeEN_US::escape: - on_key_read(released, this ->m_keyboard_state, KeyCode::escape); - break; - - case KeyCodeEN_US::f1: - on_key_read(released, this ->m_keyboard_state, KeyCode::f1); - break; - - case KeyCodeEN_US::f2: - on_key_read(released, this ->m_keyboard_state, KeyCode::f2); - break; - - case KeyCodeEN_US::f3: - on_key_read(released, this ->m_keyboard_state, KeyCode::f3); - break; - - case KeyCodeEN_US::f4: - on_key_read(released, this ->m_keyboard_state, KeyCode::f4); - break; - - case KeyCodeEN_US::f5: - on_key_read(released, this ->m_keyboard_state, KeyCode::f5); - break; - - case KeyCodeEN_US::f6: - on_key_read(released, this ->m_keyboard_state, KeyCode::f6); - break; - - case KeyCodeEN_US::f7: - on_key_read(released, this ->m_keyboard_state, KeyCode::f7); - break; - - case KeyCodeEN_US::f8: - on_key_read(released, this ->m_keyboard_state, KeyCode::f8); - break; - - case KeyCodeEN_US::f9: - on_key_read(released, this ->m_keyboard_state, KeyCode::f9); - break; - - case KeyCodeEN_US::f10: - on_key_read(released, this ->m_keyboard_state, KeyCode::f10); - break; - - case KeyCodeEN_US::f11: - on_key_read(released, this ->m_keyboard_state, KeyCode::f11); - break; - - case KeyCodeEN_US::f12: - on_key_read(released, this ->m_keyboard_state, KeyCode::f12); - break; - - case KeyCodeEN_US::printScreen: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadMultiply : KeyCode::printScreen); - break; - - case KeyCodeEN_US::scrollLock: - on_key_read(released, this ->m_keyboard_state, KeyCode::scrollLock); - break; - - // Second row - case KeyCodeEN_US::squigglyLine: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::squigglyLine : KeyCode::slantedApostrophe); - break; - - case KeyCodeEN_US::one: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::exclamationMark : KeyCode::one); - break; - - case KeyCodeEN_US::two: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::atSign: KeyCode::two); - break; - - case KeyCodeEN_US::three: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::hash : KeyCode::three); - break; - - case KeyCodeEN_US::four: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::dollarSign : KeyCode::four); - break; - - case KeyCodeEN_US::five: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::percentSign : KeyCode::five); - break; - - case KeyCodeEN_US::six: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::powerSign : KeyCode::six); - break; - - case KeyCodeEN_US::seven: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::andSign : KeyCode::seven); - break; - - case KeyCodeEN_US::eight: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::multiply : KeyCode::eight); - break; - - case KeyCodeEN_US::nine: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::openBracket : KeyCode::nine); - break; - - case KeyCodeEN_US::zero: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::closeBracket : KeyCode::zero); - break; - - case KeyCodeEN_US::minus: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::underscore : KeyCode::minus); - break; - - case KeyCodeEN_US::equals: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::plus : KeyCode::equals); - break; - - case KeyCodeEN_US::backspace: - on_key_read(released, this ->m_keyboard_state, KeyCode::backspace); - break; - - case KeyCodeEN_US::insert: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadZero : KeyCode::insert); - break; - - case KeyCodeEN_US::home: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadSeven : KeyCode::home); - break; - - case KeyCodeEN_US::pageUp: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadNine : KeyCode::pageUp); - break; - - case KeyCodeEN_US::numberPadLock: - - // Ensure this is not a repeat - if(!released){ - this ->m_keyboard_state.number_pad_lock = !this ->m_keyboard_state.number_pad_lock; - } - on_key_read(released, this ->m_keyboard_state, KeyCode::numberPadLock); - break; - - case KeyCodeEN_US::numberPadForwardSlash: - - // Check if number pad lock is on - if(this ->m_keyboard_state.number_pad_lock){ - on_key_read(released, this ->m_keyboard_state, KeyCode::numberPadForwardSlash); - }else{ - - // Normal Forward Slash - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::questionMark : KeyCode::forwardSlash); - } - break; - - // Number Pad Multiply is same as print screen - - case KeyCodeEN_US::numberPadMinus: - on_key_read(released, this ->m_keyboard_state, KeyCode::numberPadMinus); - break; - - // Third row - case KeyCodeEN_US::tab: - on_key_read(released, this ->m_keyboard_state, KeyCode::tab); - break; - - case KeyCodeEN_US::Q: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::Q : KeyCode::q); - break; - - case KeyCodeEN_US::W: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::W : KeyCode::w); - break; - - case KeyCodeEN_US::E: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::E : KeyCode::e); - break; - - case KeyCodeEN_US::R: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::R : KeyCode::r); - break; - - case KeyCodeEN_US::T: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::T : KeyCode::t); - break; - - case KeyCodeEN_US::Y: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::Y : KeyCode::y); - break; - - case KeyCodeEN_US::U: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::U : KeyCode::u); - break; - - case KeyCodeEN_US::I: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::I : KeyCode::i); - break; - - case KeyCodeEN_US::O: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::O : KeyCode::o); - break; - - case KeyCodeEN_US::P: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::P : KeyCode::p); - break; - - case KeyCodeEN_US::openSquareBracket: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::openCurlyBracket : KeyCode::openSquareBracket); - break; - - case KeyCodeEN_US::closeSquareBracket: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::closeCurlyBracket : KeyCode::closeSquareBracket); - break; - - case KeyCodeEN_US::backslash: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::lineThing : KeyCode::backslash); - break; - - case KeyCodeEN_US::deleteKey: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadFullStop : KeyCode::deleteKey); - break; - - case KeyCodeEN_US::end: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadOne : KeyCode::end); - break; - - case KeyCodeEN_US::pageDown: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadThree : KeyCode::pageDown); - break; - - // Number pad 7 is same as home - - case KeyCodeEN_US::numberPadEight: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadEight : KeyCode::upArrow); - break; - - // Number pad 9 is same as page up - - case KeyCodeEN_US::numberPadPlus: - on_key_read(released, this ->m_keyboard_state, KeyCode::numberPadPlus); - break; - - // Fourth row - - case KeyCodeEN_US::capsLock: - // Ensure this is not a repeat - if(!released){ - this ->m_keyboard_state.caps_lock = !this ->m_keyboard_state.caps_lock; - } - - on_key_read(released, this ->m_keyboard_state, KeyCode::capsLock); - break; - - case KeyCodeEN_US::A: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::A : KeyCode::a); - break; - - case KeyCodeEN_US::S: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::S : KeyCode::s); - break; - - case KeyCodeEN_US::D: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::D : KeyCode::d); - break; - - case KeyCodeEN_US::F: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::F : KeyCode::f); - break; - - case KeyCodeEN_US::G: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::G : KeyCode::g); - break; - - case KeyCodeEN_US::H: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::H : KeyCode::h); - break; - - case KeyCodeEN_US::J: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::J : KeyCode::j); - break; - - case KeyCodeEN_US::K: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::K : KeyCode::k); - break; - - case KeyCodeEN_US::L: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::L : KeyCode::l); - break; - - case KeyCodeEN_US::semicolon: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::colon : KeyCode::semicolon); - break; - - case KeyCodeEN_US::apostrophe: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::quotationMark : KeyCode::apostrophe); - break; - - case KeyCodeEN_US::enter: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock - ? KeyCode::numberPadEnter : KeyCode::enter); - break; - - case KeyCodeEN_US::numberPadFour: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadFour : KeyCode::leftArrow); - break; - - case KeyCodeEN_US::numberPadFive: - on_key_read(released, this ->m_keyboard_state, KeyCode::numberPadFive); - break; - - case KeyCodeEN_US::numberPadSix: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadSix : KeyCode::rightArrow); - break; - - // Fifth row - case KeyCodeEN_US::leftShift: - this ->m_keyboard_state.left_shift = !this ->m_keyboard_state.left_shift; - on_key_read(released, this ->m_keyboard_state, KeyCode::leftShift); - break; - - case KeyCodeEN_US::Z: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::Z : KeyCode::z); - break; - - case KeyCodeEN_US::X: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::X : KeyCode::x); - break; - - case KeyCodeEN_US::C: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::C : KeyCode::c); - break; - - case KeyCodeEN_US::V: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::V : KeyCode::v); - break; - - case KeyCodeEN_US::B: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::B : KeyCode::b); - break; - - case KeyCodeEN_US::N: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::N : KeyCode::n); - break; - - case KeyCodeEN_US::M: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::M : KeyCode::m); - break; - - case KeyCodeEN_US::comma: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::lessThan : KeyCode::comma); - break; - - case KeyCodeEN_US::fullStop: - on_key_read(released, this ->m_keyboard_state, - should_be_upper_case ? KeyCode::greaterThan : KeyCode::fullStop); - break; - - // Forward slash is same as number pad forward slash - case KeyCodeEN_US::rightShift: - // Check if this is a repeat - if(!released){ - this ->m_keyboard_state.right_shift = !this ->m_keyboard_state.right_shift; - } - - on_key_read(released, this ->m_keyboard_state, KeyCode::rightShift); - break; + // Check if the key was released + bool released = (scan_code & 0x80) && (m_current_extended_code_1 + || (scan_code != 0xe1)) && (m_next_is_extended_code_0 + || (scan_code != 0xe0)); + + // Clear the released bit + if (released) + scan_code &= ~0x80; + + // Start of an extended scan code + if (scan_code == 0xe0) { + m_next_is_extended_code_0 = true; + return; + } + + // Handle extended scan codes + ScanCodeType type = ScanCodeType::REGULAR; + if (m_next_is_extended_code_0) { + + type = ScanCodeType::EXTENDED; + m_next_is_extended_code_0 = false; + + // Check if the scan_code represents a shift key and return (fake shift) + if ((KeyboardInterpreterEN_US::KeyCodeEN_US) scan_code == KeyCodeEN_US::leftShift || + (KeyboardInterpreterEN_US::KeyCodeEN_US) scan_code == KeyCodeEN_US::rightShift) + return; + } + + // If the scan_code is 0xe1, set the e1Code flag to 1 and return + if (scan_code == 0xe1) { + m_current_extended_code_1 = 1; + return; + } + + // If e1Code is 1, set e1Code to 2, store the scan_code in e1CodeBuffer, and return + if (m_current_extended_code_1 == 1) { + m_current_extended_code_1 = 2; + m_extended_code_1_buffer = scan_code; + return; + } + + // If e1Code is 2, set keyType to 2, reset e1Code, and update e1CodeBuffer + if (m_current_extended_code_1 == 2) { + type = ScanCodeType::EXTENDED; + m_current_extended_code_1 = 0; + m_extended_code_1_buffer |= (((uint16_t) scan_code) << 8); + } + + // Scan code value manipulations + bool is_shifting = this->m_keyboard_state.left_shift || this->m_keyboard_state.right_shift; + bool should_be_upper_case = is_shifting != this->m_keyboard_state.caps_lock; + + // TODO: Probably a better way to do this (investigate when adding more keyboard layouts) + if (type == ScanCodeType::REGULAR) + switch ((KeyCodeEN_US) scan_code) { + + /// First row + case KeyCodeEN_US::escape: + on_key_read(released, this->m_keyboard_state, KeyCode::escape); + break; + + case KeyCodeEN_US::f1: + on_key_read(released, this->m_keyboard_state, KeyCode::f1); + break; + + case KeyCodeEN_US::f2: + on_key_read(released, this->m_keyboard_state, KeyCode::f2); + break; + + case KeyCodeEN_US::f3: + on_key_read(released, this->m_keyboard_state, KeyCode::f3); + break; + + case KeyCodeEN_US::f4: + on_key_read(released, this->m_keyboard_state, KeyCode::f4); + break; + + case KeyCodeEN_US::f5: + on_key_read(released, this->m_keyboard_state, KeyCode::f5); + break; + + case KeyCodeEN_US::f6: + on_key_read(released, this->m_keyboard_state, KeyCode::f6); + break; + + case KeyCodeEN_US::f7: + on_key_read(released, this->m_keyboard_state, KeyCode::f7); + break; + + case KeyCodeEN_US::f8: + on_key_read(released, this->m_keyboard_state, KeyCode::f8); + break; + + case KeyCodeEN_US::f9: + on_key_read(released, this->m_keyboard_state, KeyCode::f9); + break; + + case KeyCodeEN_US::f10: + on_key_read(released, this->m_keyboard_state, KeyCode::f10); + break; + + case KeyCodeEN_US::f11: + on_key_read(released, this->m_keyboard_state, KeyCode::f11); + break; + + case KeyCodeEN_US::f12: + on_key_read(released, this->m_keyboard_state, KeyCode::f12); + break; + + case KeyCodeEN_US::printScreen: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadMultiply : KeyCode::printScreen); + break; + + case KeyCodeEN_US::scrollLock: + on_key_read(released, this->m_keyboard_state, KeyCode::scrollLock); + break; + + /// Second row + case KeyCodeEN_US::squigglyLine: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::squigglyLine : KeyCode::slantedApostrophe); + break; + + case KeyCodeEN_US::one: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::exclamationMark : KeyCode::one); + break; + + case KeyCodeEN_US::two: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::atSign : KeyCode::two); + break; + + case KeyCodeEN_US::three: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::hash : KeyCode::three); + break; + + case KeyCodeEN_US::four: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::dollarSign : KeyCode::four); + break; + + case KeyCodeEN_US::five: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::percentSign : KeyCode::five); + break; + + case KeyCodeEN_US::six: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::powerSign : KeyCode::six); + break; + + case KeyCodeEN_US::seven: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::andSign : KeyCode::seven); + break; + + case KeyCodeEN_US::eight: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::multiply : KeyCode::eight); + break; + + case KeyCodeEN_US::nine: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::openBracket : KeyCode::nine); + break; + + case KeyCodeEN_US::zero: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::closeBracket : KeyCode::zero); + break; + + case KeyCodeEN_US::minus: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::underscore : KeyCode::minus); + break; + + case KeyCodeEN_US::equals: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::plus : KeyCode::equals); + break; + + case KeyCodeEN_US::backspace: + on_key_read(released, this->m_keyboard_state, KeyCode::backspace); + break; + + case KeyCodeEN_US::insert: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadZero : KeyCode::insert); + break; + + case KeyCodeEN_US::home: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock + ? KeyCode::numberPadSeven : KeyCode::home); + break; + + case KeyCodeEN_US::pageUp: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadNine : KeyCode::pageUp); + break; + + case KeyCodeEN_US::numberPadLock: + + // Ensure this is not a repeat + if (!released) { + this->m_keyboard_state.number_pad_lock = !this->m_keyboard_state.number_pad_lock; + } + on_key_read(released, this->m_keyboard_state, KeyCode::numberPadLock); + break; + + case KeyCodeEN_US::numberPadForwardSlash: + + // Check if number pad lock is on + if (this->m_keyboard_state.number_pad_lock) { + on_key_read(released, this->m_keyboard_state, KeyCode::numberPadForwardSlash); + } else { + + // Normal Forward Slash + on_key_read(released, this->m_keyboard_state, + should_be_upper_case ? KeyCode::questionMark : KeyCode::forwardSlash); + } + break; + + // Number Pad Multiply is same as print screen + + case KeyCodeEN_US::numberPadMinus: + on_key_read(released, this->m_keyboard_state, KeyCode::numberPadMinus); + break; + + // Third row + case KeyCodeEN_US::tab: + on_key_read(released, this->m_keyboard_state, KeyCode::tab); + break; + + case KeyCodeEN_US::Q: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::Q : KeyCode::q); + break; + + case KeyCodeEN_US::W: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::W : KeyCode::w); + break; + + case KeyCodeEN_US::E: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::E : KeyCode::e); + break; + + case KeyCodeEN_US::R: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::R : KeyCode::r); + break; + + case KeyCodeEN_US::T: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::T : KeyCode::t); + break; + + case KeyCodeEN_US::Y: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::Y : KeyCode::y); + break; + + case KeyCodeEN_US::U: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::U : KeyCode::u); + break; + + case KeyCodeEN_US::I: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::I : KeyCode::i); + break; + + case KeyCodeEN_US::O: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::O : KeyCode::o); + break; + + case KeyCodeEN_US::P: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::P : KeyCode::p); + break; + + case KeyCodeEN_US::openSquareBracket: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::openCurlyBracket : KeyCode::openSquareBracket); + break; + + case KeyCodeEN_US::closeSquareBracket: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::closeCurlyBracket : KeyCode::closeSquareBracket); + break; + + case KeyCodeEN_US::backslash: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::lineThing : KeyCode::backslash); + break; + + case KeyCodeEN_US::deleteKey: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock + ? KeyCode::numberPadFullStop : KeyCode::deleteKey); + break; + + case KeyCodeEN_US::end: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadOne : KeyCode::end); + break; + + case KeyCodeEN_US::pageDown: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock + ? KeyCode::numberPadThree : KeyCode::pageDown); + break; + + // Number pad 7 is same as home + + case KeyCodeEN_US::numberPadEight: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadEight : KeyCode::upArrow); + break; + + // Number pad 9 is same as page up + + case KeyCodeEN_US::numberPadPlus: + on_key_read(released, this->m_keyboard_state, KeyCode::numberPadPlus); + break; + + /// Fourth row + case KeyCodeEN_US::capsLock: + // Ensure this is not a repeat + if (!released) { + this->m_keyboard_state.caps_lock = !this->m_keyboard_state.caps_lock; + } + + on_key_read(released, this->m_keyboard_state, KeyCode::capsLock); + break; + + case KeyCodeEN_US::A: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::A : KeyCode::a); + break; + + case KeyCodeEN_US::S: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::S : KeyCode::s); + break; + + case KeyCodeEN_US::D: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::D : KeyCode::d); + break; + + case KeyCodeEN_US::F: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::F : KeyCode::f); + break; + + case KeyCodeEN_US::G: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::G : KeyCode::g); + break; + + case KeyCodeEN_US::H: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::H : KeyCode::h); + break; + + case KeyCodeEN_US::J: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::J : KeyCode::j); + break; + + case KeyCodeEN_US::K: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::K : KeyCode::k); + break; + + case KeyCodeEN_US::L: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::L : KeyCode::l); + break; + + case KeyCodeEN_US::semicolon: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::colon : KeyCode::semicolon); + break; + + case KeyCodeEN_US::apostrophe: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::quotationMark : KeyCode::apostrophe); + break; + + case KeyCodeEN_US::enter: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadEnter : KeyCode::enter); + break; + + case KeyCodeEN_US::numberPadFour: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadFour : KeyCode::leftArrow); + break; + + case KeyCodeEN_US::numberPadFive: + on_key_read(released, this->m_keyboard_state, KeyCode::numberPadFive); + break; + + case KeyCodeEN_US::numberPadSix: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadSix : KeyCode::rightArrow); + break; + + /// Fifth row + case KeyCodeEN_US::leftShift: + this->m_keyboard_state.left_shift = !this->m_keyboard_state.left_shift; + on_key_read(released, this->m_keyboard_state, KeyCode::leftShift); + break; + + case KeyCodeEN_US::Z: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::Z : KeyCode::z); + break; + + case KeyCodeEN_US::X: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::X : KeyCode::x); + break; + + case KeyCodeEN_US::C: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::C : KeyCode::c); + break; + + case KeyCodeEN_US::V: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::V : KeyCode::v); + break; + + case KeyCodeEN_US::B: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::B : KeyCode::b); + break; + + case KeyCodeEN_US::N: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::N : KeyCode::n); + break; + + case KeyCodeEN_US::M: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::M : KeyCode::m); + break; + + case KeyCodeEN_US::comma: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::lessThan : KeyCode::comma); + break; + + case KeyCodeEN_US::fullStop: + on_key_read(released, this->m_keyboard_state, should_be_upper_case ? KeyCode::greaterThan : KeyCode::fullStop); + break; + + // Forward slash is same as number pad forward slash + + case KeyCodeEN_US::rightShift: + // Check if this is a repeat + if (!released) { + this->m_keyboard_state.right_shift = !this->m_keyboard_state.right_shift; + } + + on_key_read(released, this->m_keyboard_state, KeyCode::rightShift); + break; + + // Up Arrow is the same as number pad 8 + + // Number pad 1 is the same as end - // Up Arrow is the same as number pad 8 + case KeyCodeEN_US::numberPadTwo: + on_key_read(released, this->m_keyboard_state, this->m_keyboard_state.number_pad_lock ? KeyCode::numberPadTwo : KeyCode::downArrow); + break; - // Number pad 1 is the same as end + // Number pad 3 is the same as page down - case KeyCodeEN_US::numberPadTwo: - on_key_read(released, this ->m_keyboard_state, this ->m_keyboard_state.number_pad_lock ? KeyCode::numberPadTwo : KeyCode::downArrow); - break; + // Number pad enter is the same as enter - // Number pad 3 is the same as page down + /// Sixth row + case KeyCodeEN_US::leftControl: + // Check if this is a repeat + if (!released) { + this->m_keyboard_state.left_control = !this->m_keyboard_state.left_control; + this->m_keyboard_state.right_control = !this->m_keyboard_state.right_control; + } - // Number pad enter is the same as enter + on_key_read(released, this->m_keyboard_state, KeyCode::leftControl); + break; - // Sixth row - case KeyCodeEN_US::leftControl: - // Check if this is a repeat - if(!released){ - this ->m_keyboard_state.left_control = !this ->m_keyboard_state.left_control; - this ->m_keyboard_state.right_control = !this ->m_keyboard_state.right_control; - } + case KeyCodeEN_US::leftOS: + on_key_read(released, this->m_keyboard_state, KeyCode::leftOS); + break; - on_key_read(released, this ->m_keyboard_state, KeyCode::leftControl); - break; + case KeyCodeEN_US::leftAlt: + // Check if this is a repeat + if (!released) { + this->m_keyboard_state.left_alt = !this->m_keyboard_state.left_alt; + this->m_keyboard_state.right_alt = !this->m_keyboard_state.right_alt; + } - case KeyCodeEN_US::leftOS: - on_key_read(released, this ->m_keyboard_state, KeyCode::leftOS); - break; + on_key_read(released, this->m_keyboard_state, KeyCode::leftAlt); + break; - case KeyCodeEN_US::leftAlt: - // Check if this is a repeat - if(!released){ - this ->m_keyboard_state.left_alt = !this ->m_keyboard_state.left_alt; - this ->m_keyboard_state.right_alt = !this ->m_keyboard_state.right_alt; - } + case KeyCodeEN_US::space: + on_key_read(released, this->m_keyboard_state, KeyCode::space); + break; - on_key_read(released, this ->m_keyboard_state, KeyCode::leftAlt); - break; + // Right Alt is the same as left alt - case KeyCodeEN_US::space: - on_key_read(released, this ->m_keyboard_state, KeyCode::space); - break; + // Right Control is the same as left control - // Right Alt is the same as left alt + // Left Arrow is the same as number pad 4 - // Right Control is the same as left control + // Down Arrow is the same as number pad 2 - // Left Arrow is the same as number pad 4 + // Right Arrow is the same as number pad 6 - // Down Arrow is the same as number pad 2 + // Number pad 0 is the same as insert - // Right Arrow is the same as number pad 6 + // Number pad full stop is the same as delete - // Number pad 0 is the same as insert + default: + break; - // Number pad full stop is the same as delete + } - default: - break; - - } - } -KeyDownEvent::KeyDownEvent(KeyCode keyCode, const KeyboardState& keyboardState) +KeyDownEvent::KeyDownEvent(KeyCode keyCode, const KeyboardState &keyboardState) : Event(KeyboardEvents::KEYDOWN), key_code(keyCode), keyboard_state(keyboardState) @@ -716,7 +641,7 @@ KeyDownEvent::KeyDownEvent(KeyCode keyCode, const KeyboardState& keyboardState) KeyDownEvent::~KeyDownEvent() = default; -KeyUpEvent::KeyUpEvent(KeyCode key_code, const KeyboardState& keyboard_state) +KeyUpEvent::KeyUpEvent(KeyCode key_code, const KeyboardState &keyboard_state) : Event(KeyboardEvents::KEYUP), key_code(key_code), keyboard_state(keyboard_state) diff --git a/kernel/src/drivers/peripherals/mouse.cpp b/kernel/src/drivers/peripherals/mouse.cpp index 31b68471..bc5841da 100644 --- a/kernel/src/drivers/peripherals/mouse.cpp +++ b/kernel/src/drivers/peripherals/mouse.cpp @@ -10,9 +10,6 @@ using namespace MaxOS::drivers; using namespace MaxOS::drivers::peripherals; using namespace MaxOS::hardwarecommunication; - -///__Handler__ - MouseEventHandler::MouseEventHandler() = default; /** @@ -21,26 +18,25 @@ MouseEventHandler::MouseEventHandler() = default; * @param event The event that was triggered * @return The event that was triggered with the modified data */ -Event* MouseEventHandler::on_event(Event *event) { - switch (event->type){ +Event *MouseEventHandler::on_event(Event *event) { + switch (event->type) { - case MouseEvents::MOVE: - this->on_mouse_move_event(((MouseMoveEvent *)event)->x, - ((MouseMoveEvent *)event)->y); - break; + case MouseEvents::MOVE: + this->on_mouse_move_event(((MouseMoveEvent *) event)->x, + ((MouseMoveEvent *) event)->y); + break; - case MouseEvents::DOWN: - this->on_mouse_down_event(((MouseDownEvent *)event)->button); - break; + case MouseEvents::DOWN: + this->on_mouse_down_event(((MouseDownEvent *) event)->button); + break; - case MouseEvents::UP: - this->on_mouse_up_event(((MouseUpEvent *)event)->button); - break; + case MouseEvents::UP: + this->on_mouse_up_event(((MouseUpEvent *) event)->button); + break; - } + } - // Return the event - return event; + return event; } /** @@ -48,7 +44,7 @@ Event* MouseEventHandler::on_event(Event *event) { * * @param button The button that was pressed */ -void MouseEventHandler::on_mouse_down_event(uint8_t){ +void MouseEventHandler::on_mouse_down_event(uint8_t) { } @@ -57,7 +53,7 @@ void MouseEventHandler::on_mouse_down_event(uint8_t){ * * @param button The button that was released */ -void MouseEventHandler::on_mouse_up_event(uint8_t){ +void MouseEventHandler::on_mouse_up_event(uint8_t) { } @@ -67,14 +63,12 @@ void MouseEventHandler::on_mouse_up_event(uint8_t){ * @param x How much the mouse moved in the x direction * @param y How much the mouse moved in the y direction */ -void MouseEventHandler::on_mouse_move_event(int8_t, int8_t){ +void MouseEventHandler::on_mouse_move_event(int8_t, int8_t) { } MouseEventHandler::~MouseEventHandler() = default; -///__Driver__ - MouseDriver::MouseDriver() : InterruptHandler(0x2C, 0xC, 0x28), data_port(0x60), @@ -82,70 +76,69 @@ MouseDriver::MouseDriver() { } -MouseDriver::~MouseDriver()= default; + +MouseDriver::~MouseDriver() = default; /** * @brief activate the mouse */ void MouseDriver::activate() { + // Get the current state of the mouse + command_port.write(0x20); + uint8_t status = (data_port.read() | 2); + // Write the new state + command_port.write(0x60); + data_port.write(status); - // Get the current state of the mouse - command_port.write(0x20); - uint8_t status = (data_port.read() | 2); + // Tell the PIC to start listening to the mouse + command_port.write(0xAB); - // write the new state - command_port.write(0x60); - data_port.write(status); - - // Tell the PIC to start listening to the mouse - command_port.write(0xAB); - - // activate the mouse - command_port.write(0xD4); - data_port.write(0xF4); - data_port.read(); + // Activate the mouse + command_port.write(0xD4); + data_port.write(0xF4); + data_port.read(); } /** * @brief Handle the mouse interrupt */ -void MouseDriver::handle_interrupt(){ +void MouseDriver::handle_interrupt() { - //Only if the 6th bit of data is one then there is data to handle - uint8_t status = command_port.read(); - if(!(status & 0x20)) - return; + // Check if there is data to handle + uint8_t status = command_port.read(); + if (!(status & 0x20)) + return; - // read the data and store it in the buffer - buffer[offset] = data_port.read(); - offset = (offset + 1) % 3; + // Read the data + m_buffer[m_offset] = data_port.read(); + m_offset = (m_offset + 1) % 3; - // If the mouse data transmission is incomplete (3rd piece of data isn't through) - if(offset != 0) - return; + // If the mouse data transmission is incomplete (3rd piece of data isn't through) + if (m_offset != 0) + return; - // If the mouse is moved (y-axis is inverted) - if(buffer[1] != 0 || buffer[2] != 0) - raise_event(new MouseMoveEvent(buffer[1], -buffer[2])); + // If the mouse is moved (y-axis is inverted) + if (m_buffer[1] != 0 || m_buffer[2] != 0) + raise_event(new MouseMoveEvent(m_buffer[1], -m_buffer[2])); - for (int i = 0; i < 3; ++i) { + // Handle button presses + for (int i = 0; i < 3; ++i) { - // Check if the button state has changed - if((buffer[0] & (0x1<(MouseEvents::UP), button(button) @@ -183,4 +174,4 @@ MouseMoveEvent::MouseMoveEvent(int8_t x, int8_t y) { } -MouseMoveEvent::~MouseMoveEvent() = default; +MouseMoveEvent::~MouseMoveEvent() = default; \ No newline at end of file diff --git a/kernel/src/drivers/video/vesa.cpp b/kernel/src/drivers/video/vesa.cpp index f8b98c0d..cc0639ed 100644 --- a/kernel/src/drivers/video/vesa.cpp +++ b/kernel/src/drivers/video/vesa.cpp @@ -12,60 +12,36 @@ using namespace MaxOS::memory; using namespace MaxOS::system; using namespace MaxOS::common; -VideoElectronicsStandardsAssociation::VideoElectronicsStandardsAssociation(multiboot_tag_framebuffer* framebuffer_info) -: VideoDriver(), - m_framebuffer_info(framebuffer_info) +VideoElectronicsStandardsAssociation::VideoElectronicsStandardsAssociation(multiboot_tag_framebuffer *framebuffer_info) +: m_framebuffer_info(framebuffer_info) { - // Get the framebuffer info - Logger::INFO() << "Setting up VESA driver\n"; - Logger::DEBUG() << "Framebuffer info: 0x " << (uint64_t)m_framebuffer_info << "\n"; - // Set the framebuffer address, bpp and pitch - m_bpp = m_framebuffer_info->common.framebuffer_bpp; - m_pitch = m_framebuffer_info->common.framebuffer_pitch; - m_framebuffer_size = m_framebuffer_info->common.framebuffer_height * m_pitch; + Logger::INFO() << "Setting up VESA driver\n"; - Logger::DEBUG() << "Framebuffer: bpp=" << m_bpp << ", pitch=" << m_pitch << ", size=" << m_framebuffer_size << "\n"; + // Save the framebuffer info + m_bpp = m_framebuffer_info->common.framebuffer_bpp; + m_pitch = m_framebuffer_info->common.framebuffer_pitch; + m_framebuffer_size = m_framebuffer_info->common.framebuffer_height * m_pitch; + this->set_mode(framebuffer_info->common.framebuffer_width, framebuffer_info->common.framebuffer_height, + framebuffer_info->common.framebuffer_bpp); + Logger::DEBUG() << "Framebuffer: bpp=" << m_bpp << ", pitch=" << m_pitch << ", size=" << m_framebuffer_size << "\n"; - // Get the framebuffer address - auto physical_address = (uint64_t)m_framebuffer_info->common.framebuffer_addr; - auto virtual_address = (uint64_t)PhysicalMemoryManager::to_dm_region(physical_address); - uint64_t end = physical_address + m_framebuffer_size; - m_framebuffer_address = (uint64_t*)virtual_address; + // Map the frame buffer into the higher half + auto physical_address = (uint64_t) m_framebuffer_info->common.framebuffer_addr; + m_framebuffer_address = (uint64_t *) PhysicalMemoryManager::to_dm_region(physical_address); + PhysicalMemoryManager::s_current_manager->map_area((physical_address_t *) physical_address, m_framebuffer_address, m_framebuffer_size, Write | Present); - Logger::DEBUG() << "Framebuffer address: physical=0x" << physical_address << ", virtual=0x" << virtual_address << "\n"; + // Reserve the physical memory + size_t pages = PhysicalMemoryManager::size_to_frames(m_framebuffer_size); + PhysicalMemoryManager::s_current_manager->reserve(m_framebuffer_info->common.framebuffer_addr, pages); - // Map the framebuffer - while (physical_address < end) { - - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)physical_address, (virtual_address_t*)virtual_address, Write | Present); - physical_address += PhysicalMemoryManager::s_page_size; - virtual_address += PhysicalMemoryManager::s_page_size; - } - - size_t pages = PhysicalMemoryManager::size_to_frames(virtual_address - (uint64_t)m_framebuffer_address); - Logger::DEBUG() << "Framebuffer mapped: 0x" << (uint64_t)m_framebuffer_address << " - 0x" << virtual_address << " (pages: " << pages << ")\n"; - - // Reserve the physical memory - PhysicalMemoryManager::s_current_manager->reserve(m_framebuffer_info->common.framebuffer_addr, pages); - - // Set the default video mode - this -> set_mode(framebuffer_info->common.framebuffer_width,framebuffer_info->common.framebuffer_height, framebuffer_info->common.framebuffer_bpp); + // Log info + Logger::DEBUG() << "Framebuffer address: physical=0x" << (uint64_t) physical_address << ", virtual=0x" << (uint64_t) m_framebuffer_address << "\n"; + Logger::DEBUG() << "Framebuffer mapped: 0x" << (uint64_t) m_framebuffer_address << " - 0x" << (uint64_t) (m_framebuffer_address + m_framebuffer_size) << " (pages: " << pages << ")\n"; } -VideoElectronicsStandardsAssociation::~VideoElectronicsStandardsAssociation()= default; - -/** - * @brief Initializes the VESA driver - * - * @return True if the driver was initialized successfully, false otherwise - */ -bool VideoElectronicsStandardsAssociation::init() { - - //Multiboot inits this for us - return true; -} +VideoElectronicsStandardsAssociation::~VideoElectronicsStandardsAssociation() = default; /** * @brief Sets the mode of the VESA driver @@ -75,11 +51,12 @@ bool VideoElectronicsStandardsAssociation::init() { * @param color_depth Color depth of the screen * @return True if the mode was set successfully, false otherwise */ -bool VideoElectronicsStandardsAssociation::internal_set_mode(uint32_t, uint32_t, uint32_t) { - - // Best mode is set by the bootloader - return true; +bool VideoElectronicsStandardsAssociation::internal_set_mode(uint32_t width, uint32_t height, uint32_t color_depth) { + // Can only use the mode set up already by grub + return width == m_framebuffer_info->common.framebuffer_width + && height == m_framebuffer_info->common.framebuffer_height + && color_depth == m_framebuffer_info->common.framebuffer_bpp; } @@ -93,11 +70,10 @@ bool VideoElectronicsStandardsAssociation::internal_set_mode(uint32_t, uint32_t, */ bool VideoElectronicsStandardsAssociation::supports_mode(uint32_t width, uint32_t height, uint32_t color_depth) { - // Check if the mode is supported - if(width == (uint32_t)m_framebuffer_info->common.framebuffer_width && height == (uint32_t)m_framebuffer_info->common.framebuffer_height && color_depth == (uint32_t)m_framebuffer_info->common.framebuffer_bpp) { - return true; - } - return false; + // Check if the mode is supported + return width == m_framebuffer_info->common.framebuffer_width + && height == m_framebuffer_info->common.framebuffer_height + && color_depth == m_framebuffer_info->common.framebuffer_bpp; } /** @@ -109,11 +85,8 @@ bool VideoElectronicsStandardsAssociation::supports_mode(uint32_t width, uint32_ */ void VideoElectronicsStandardsAssociation::render_pixel_32_bit(uint32_t x, uint32_t y, uint32_t colour) { - // Get the address of the pixel - auto* pixel_address = (uint32_t*)((uint8_t *)m_framebuffer_address + m_pitch * (y) + m_bpp * (x) / 8); - - // Set the pixel - *pixel_address = colour; + auto *pixel_address = (uint32_t *) ((uint8_t *) m_framebuffer_address + (m_pitch * y) + (m_bpp * x) / 8); + *pixel_address = colour; } @@ -126,11 +99,8 @@ void VideoElectronicsStandardsAssociation::render_pixel_32_bit(uint32_t x, uint3 */ uint32_t VideoElectronicsStandardsAssociation::get_rendered_pixel_32_bit(uint32_t x, uint32_t y) { - // Get the address of the pixel - auto* pixel_address = (uint32_t*)((uint8_t *)m_framebuffer_address + m_pitch * (y) + m_bpp * (x) / 8); - - // Return the pixel - return *pixel_address; + auto *pixel_address = (uint32_t *) ((uint8_t *) m_framebuffer_address + m_pitch * (y) + m_bpp * (x) / 8); + return *pixel_address; } /** @@ -139,7 +109,9 @@ uint32_t VideoElectronicsStandardsAssociation::get_rendered_pixel_32_bit(uint32_ * @return The name of the vendor */ string VideoElectronicsStandardsAssociation::vendor_name() { - return "NEC Home Electronics"; // Creator of the VESA standard + + // Creator of the VESA standard + return "NEC Home Electronics"; } /** @@ -148,5 +120,5 @@ string VideoElectronicsStandardsAssociation::vendor_name() { * @return The name of the device */ string VideoElectronicsStandardsAssociation::device_name() { - return "VESA compatible graphics card"; + return "VESA compatible graphics card"; } diff --git a/kernel/src/drivers/video/vga.cpp b/kernel/src/drivers/video/vga.cpp index a1831d08..0894866b 100644 --- a/kernel/src/drivers/video/vga.cpp +++ b/kernel/src/drivers/video/vga.cpp @@ -33,51 +33,49 @@ VideoGraphicsArray::~VideoGraphicsArray() = default; * * @param registers The VGA registers to write to. */ -void VideoGraphicsArray::write_registers(uint8_t* registers) -{ - // Move to the next register - m_misc_port.write(*(registers++)); - - // Set the sequencer registers - for (uint8_t i = 0; i < 5; i++ ) { - m_sequence_index_port.write(i); - m_sequence_data_port.write(*(registers++)); - } - - // Clear protection bit to enable writing to CR0-7 - m_crtc_index_port.write(0x03); - crtc_data_port.write(crtc_data_port.read() | 0x80); - m_crtc_index_port.write(0x11); - crtc_data_port.write(crtc_data_port.read() | ~0x80); - - // Ensure protection bit is set - registers[0x03] = registers[0x03] | 0x80; - registers[0x11] = registers[0x11] & ~0x80; - - // write the CRTC registers - for (uint8_t i = 0; i < 25; i++ ) { - m_crtc_index_port.write(i); - crtc_data_port.write(*(registers++)); - } - - // write the graphics controller registers - for(uint8_t i = 0; i < 9; i++) - { - m_graphics_controller_index_port.write(i); - m_graphics_controller_data_port.write(*(registers++)); - } - - // write the attribute controller registers - for(uint8_t i = 0; i < 21; i++) - { - m_attribute_controller_reset_port.read(); - m_attribute_controller_index_port.write(i); - m_attribute_controller_write_port.write(*(registers++)); - } - - // Re-Lock CRTC and unblank display - m_attribute_controller_reset_port.read(); - m_attribute_controller_index_port.write(0x20); +void VideoGraphicsArray::write_registers(uint8_t *registers) { + + // Move to the next register + m_misc_port.write(*(registers++)); + + // Set the sequencer registers + for (uint8_t i = 0; i < 5; i++) { + m_sequence_index_port.write(i); + m_sequence_data_port.write(*(registers++)); + } + + // Clear protection bit to enable writing to CR0-7 + m_crtc_index_port.write(0x03); + crtc_data_port.write(crtc_data_port.read() | 0x80); + m_crtc_index_port.write(0x11); + crtc_data_port.write(crtc_data_port.read() | ~0x80); + + // Ensure protection bit is set + registers[0x03] = registers[0x03] | 0x80; + registers[0x11] = registers[0x11] & ~0x80; + + // Write the CRTC registers + for (uint8_t i = 0; i < 25; i++) { + m_crtc_index_port.write(i); + crtc_data_port.write(*(registers++)); + } + + // Write the graphics controller registers + for (uint8_t i = 0; i < 9; i++) { + m_graphics_controller_index_port.write(i); + m_graphics_controller_data_port.write(*(registers++)); + } + + // Write the attribute controller registers + for (uint8_t i = 0; i < 21; i++) { + m_attribute_controller_reset_port.read(); + m_attribute_controller_index_port.write(i); + m_attribute_controller_write_port.write(*(registers++)); + } + + // Re-Lock CRTC and unblank display + m_attribute_controller_reset_port.read(); + m_attribute_controller_index_port.write(0x20); } @@ -86,9 +84,8 @@ void VideoGraphicsArray::write_registers(uint8_t* registers) * * @return True if the specified resolution is supported, otherwise false. */ -bool VideoGraphicsArray::supports_mode(uint32_t width, uint32_t height, uint32_t colour_depth) -{ - return width == 320 && height == 200 && colour_depth == 8; +bool VideoGraphicsArray::supports_mode(uint32_t width, uint32_t height, uint32_t colour_depth) { + return width == 320 && height == 200 && colour_depth == 8; } /** @@ -100,34 +97,31 @@ bool VideoGraphicsArray::supports_mode(uint32_t width, uint32_t height, uint32_t * * @return True if the card was able to set the resolution, otherwise false. */ -bool VideoGraphicsArray::internal_set_mode(uint32_t width, uint32_t height, uint32_t colour_depth) -{ - if(!supports_mode(width, height, colour_depth)) - return false; - - //Values from osdev / modes.c - unsigned char g_320x200x256[] = - { - // MISC - 0x63, - // SEQ - 0x03, 0x01, 0x0F, 0x00, 0x0E, - // CRTC - 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, - 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, - 0xFF, - // GC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, - 0xFF, - // AC - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, - 0x41, 0x00, 0x0F, 0x00, 0x00 - }; - - write_registers(g_320x200x256); - return true; +bool VideoGraphicsArray::internal_set_mode(uint32_t width, uint32_t height, uint32_t colour_depth) { + + //Values from osdev / modes.c + unsigned char g_320x200x256[] = + { + // MISC + 0x63, + // SEQ + 0x03, 0x01, 0x0F, 0x00, 0x0E, + // CRTC + 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, + 0xFF, + // GC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, + 0xFF, + // AC + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x41, 0x00, 0x0F, 0x00, 0x00 + }; + + write_registers(g_320x200x256); + return true; } /** @@ -135,27 +129,29 @@ bool VideoGraphicsArray::internal_set_mode(uint32_t width, uint32_t height, uint * * @return The framebuffer address. */ -uint8_t* VideoGraphicsArray::get_frame_buffer_segment() -{ - - // Optimise so that don't have to read and write to the port every time - return (uint8_t*)0xA0000; - - //read data from index number 6 - m_graphics_controller_index_port.write(0x06); - uint8_t segmentNumber = - m_graphics_controller_data_port.read() & (3<<2); //Shift by 2 as only interested in bits 3 & 4 (& 3 so all the other bits are removed) - switch(segmentNumber) - { - default: - case 0<<2: return (uint8_t*)nullptr; - case 1<<2: return (uint8_t*)0xA0000; - case 2<<2: return (uint8_t*)0xB0000; - case 3<<2: return (uint8_t*)0xB8000; - } +uint8_t *VideoGraphicsArray::get_frame_buffer_segment() { + + // Optimise so that don't have to read and write to the port every time + return (uint8_t *) 0xA0000; + + //read data from index number 6 + m_graphics_controller_index_port.write(0x06); + uint8_t segmentNumber = + m_graphics_controller_data_port.read() & + (3 << 2); //Shift by 2 as only interested in bits 3 & 4 (& 3 so all the other bits are removed) + switch (segmentNumber) { + default: + case 0 << 2: + return (uint8_t *) nullptr; + case 1 << 2: + return (uint8_t *) 0xA0000; + case 2 << 2: + return (uint8_t *) 0xB0000; + case 3 << 2: + return (uint8_t *) 0xB8000; + } } - /** * @brief Puts a 8 bit pixel on the screen. * @@ -163,13 +159,10 @@ uint8_t* VideoGraphicsArray::get_frame_buffer_segment() * @param y The y coordinate of the pixel. * @param colour The colour of the pixel. */ -void VideoGraphicsArray::render_pixel_8_bit(uint32_t x, uint32_t y, uint8_t colour){ - - // Get the address of the pixel - uint8_t*pixel_address = get_frame_buffer_segment() + 320*y + x; +void VideoGraphicsArray::render_pixel_8_bit(uint32_t x, uint32_t y, uint8_t colour) { - // Set the pixel - *pixel_address = colour; + uint8_t *pixel_address = get_frame_buffer_segment() + 320 * y + x; + *pixel_address = colour; } /** @@ -181,11 +174,8 @@ void VideoGraphicsArray::render_pixel_8_bit(uint32_t x, uint32_t y, uint8_t colo */ uint8_t VideoGraphicsArray::get_rendered_pixel_8_bit(uint32_t x, uint32_t y) { - // Get the address of the pixel - uint8_t*pixel_address = get_frame_buffer_segment() + 320*y + x; - - // Return the pixel - return *pixel_address; + uint8_t *pixel_address = get_frame_buffer_segment() + 320 * y + x; + return *pixel_address; } /** @@ -194,7 +184,9 @@ uint8_t VideoGraphicsArray::get_rendered_pixel_8_bit(uint32_t x, uint32_t y) { * @return The name of the vendor. */ string VideoGraphicsArray::vendor_name() { - return "IBM"; // VGA was made by IBM + + // VGA was made by IBM + return "IBM"; } /** @@ -203,5 +195,5 @@ string VideoGraphicsArray::vendor_name() { * @return The name of the device. */ string VideoGraphicsArray::device_name() { - return "VGA compatible graphics card"; + return "VGA compatible graphics card"; } \ No newline at end of file diff --git a/kernel/src/drivers/video/video.cpp b/kernel/src/drivers/video/video.cpp index e9dece67..7b805130 100644 --- a/kernel/src/drivers/video/video.cpp +++ b/kernel/src/drivers/video/video.cpp @@ -7,13 +7,7 @@ using namespace MaxOS::drivers::video; using namespace MaxOS::common; - -VideoDriver::VideoDriver() -: Driver(), - GraphicsContext() -{ - -} +VideoDriver::VideoDriver() = default; VideoDriver::~VideoDriver() = default; @@ -51,19 +45,17 @@ bool VideoDriver::supports_mode(uint32_t, uint32_t, uint32_t) { */ bool VideoDriver::set_mode(uint32_t width, uint32_t height, uint32_t colorDepth) { - // Check if the mode is supported + // Cant set it if not supported if(!supports_mode(width, height, colorDepth)) return false; - // Set the mode - if(internal_set_mode(width, height, colorDepth)) - { - this -> m_width = width; - this -> m_height = height; - this -> m_color_depth = colorDepth; - return true; - } + // Try set the mode + if(!internal_set_mode(width, height, colorDepth)) + return false; - // If setting the mode failed, return false - return false; + // Store the mode + m_width = width; + m_height = height; + m_color_depth = colorDepth; + return true; } \ No newline at end of file diff --git a/kernel/src/filesystem/fat32.cpp b/kernel/src/filesystem/fat32.cpp deleted file mode 100644 index 40d4dc97..00000000 --- a/kernel/src/filesystem/fat32.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// -// Created by 98max on 1/01/2023. -// - -#include - -using namespace MaxOS; -using namespace MaxOS::common; -using namespace MaxOS::drivers; -using namespace MaxOS::filesystem; -using namespace MaxOS::memory; diff --git a/kernel/src/filesystem/filesystem.cpp b/kernel/src/filesystem/filesystem.cpp index 44defcc4..d7c2c075 100644 --- a/kernel/src/filesystem/filesystem.cpp +++ b/kernel/src/filesystem/filesystem.cpp @@ -3,7 +3,343 @@ // #include +#include using namespace MaxOS; using namespace MaxOS::filesystem; +using namespace MaxOS::common; +File::File() = default; + +File::~File() = default; + +/** + * @brief Write data to the file + * + * @param data The byte buffer to write + * @param size The amount of data to write + */ +void File::write(const common::buffer_t* data, size_t amount) { +} + +/** + * @brief Read data from the file + * + * @param data The byte buffer to read into + * @param size The amount of data to read + */ +void File::read(common::buffer_t* data, size_t amount) { +} + +/** + * @brief Flush the file to the disk + */ +void File::flush() { +} + +/** + * @brief Seek to a position in the file + * + * @param seek_type The type of seek to perform (where to seek to) + * @param offset The amount to seek + */ +void File::seek(SeekType seek_type, size_t offset) { + + // Seek based on the type + switch (seek_type) { + case SeekType::SET: + m_offset = offset; + break; + + case SeekType::CURRENT: + m_offset += offset; + break; + + case SeekType::END: + m_offset = size() - offset; + break; + } +} + +/** + * @brief Get where the file is currently at (amount read/write/seeked) + * + * @return The current position in the file + */ +uint32_t File::position() { + return m_offset; +} + +/** + * @brief Get the name of the file + * + * @return The name of the file + */ +string File::name() { + return m_name; +} + +/** + * @brief Get the size of the file + * + * @return The size of the file (in bytes) + */ +size_t File::size() { + return m_size; +} + +Directory::Directory() = default; + +Directory::~Directory() { + + // Free the files + for (auto &file: m_files) + delete file; + + // Free the subdirectories + for (auto &subdirectory: m_subdirectories) + delete subdirectory; + +} + +/** + * @brief Read the directory from the disk + */ +void Directory::read_from_disk() { + +} + +/** + * @brief Get the files in the directory + * + * @return A list of all the files in the directory + */ +common::Vector Directory::files() { + return m_files; +} + +/** + * @brief Open a file in the directory + * + * @param name The name of the file to open + * @return + */ +File* Directory::open_file(const string &name) { + + // Try to find the file + for (auto &file: m_files) + if (file->name() == name) + return file; + + // File not found + return nullptr; +} + +/** + * @brief Create a file in the directory + * + * @param name The name of the file to create + * @return A new file object or null if it could not be created + */ +File* Directory::create_file(const string &name) { + return nullptr; +} + +/** + * @brief Delete a file in the directory + * + * @param name The name of the file to remove + */ +void Directory::remove_file(const string &name) { +} + +/** + * @brief Get the subdirectories in the directory + * + * @return The subdirectories in the directory + */ +common::Vector Directory::subdirectories() { + return m_subdirectories; +} + +/** + * @brief Open a directory in the directory + * + * @param name The name of the directory to open + * @return The directory object or null if it could not be opened + */ +Directory* Directory::open_subdirectory(const string &name) { + + // Try to find the directory + for (auto &subdirectory: m_subdirectories) + if (subdirectory->name() == name) { + subdirectory->read_from_disk(); + return subdirectory; + } + + // Directory not found + return nullptr; +} + +/** + * @brief Create a directory in the directory + * + * @param name The name of the directory to create + * @return The new directory object or null if it could not be created + */ +Directory* Directory::create_subdirectory(const string &name) { + return nullptr; +} + +/** + * @brief Try to remove a directory in the directory + * @param name The name of the directory to remove + */ +void Directory::remove_subdirectory(const string &name) { +} + + +/** + * @brief Get the name of the directory + * + * @return The name of the directory + */ +string Directory::name() { + return m_name; +} + +/** + * @brief Get the size of the directory + * + * @return The size of the directory (sum of all the files in bytes) + */ +size_t Directory::size() { + + // Sum the size of all the files + size_t size = 0; + for (auto &file: m_files) + size += file->size(); + + return size; + +} + +/** + * @brief Rename a file in the directory + * + * @param file The file to rename + * @param new_name The new name of the file + */ +void Directory::rename_file(File* file, string const &new_name) { + + rename_file(file->name(), new_name); + +} + +/** + * @brief Rename a file in the directory + * + * @param old_name The file to rename + * @param new_name The new name of the file + */ +void Directory::rename_file(string const &old_name, string const &new_name) { + + ASSERT(false, "not implemented"); + +} + +/** + * @brief Rename a subdirectory in the directory + * + * @param directory The directory to rename + * @param new_name The new name of the directory + */ +void Directory::rename_subdirectory(Directory* directory, string const &new_name) { + + rename_subdirectory(directory->name(), new_name); + +} + +/** + * @brief Rename a subdirectory in the directory + * + * @param old_name The directory to rename + * @param new_name The new name of the directory + */ +void Directory::rename_subdirectory(string const &old_name, string const &new_name) { + ASSERT(false, "not implemented"); +} + +FileSystem::FileSystem() = default; + +FileSystem::~FileSystem() { + + // Free the root directory + delete m_root_directory; + +}; + +/** + * @brief Get the directory at "/" + * + * @return The root directory of the filesystem + */ +Directory* FileSystem::root_directory() { + return m_root_directory; +} + +/** + * @brief Get the directory at a given path + * + * @param path The path to the directory + * @return The directory object or null if it could not be opened + */ +Directory* FileSystem::get_directory(const string &path) { + + // Check if the path is the root + if (path == "/") + return root_directory(); + + // Recursively open the directory + Directory* directory = root_directory(); + string directory_path = path; + while (directory_path.length() > 0) { + + // Get the name of the directory + string directory_name = Path::top_directory(directory_path); + + // Open the directory + Directory* subdirectory = directory->open_subdirectory(directory_name); + if (!subdirectory) + return nullptr; + + // Set the new directory + directory = subdirectory; + + // Get the path to the next directory + directory_path = directory_path.substring(directory_name.length() + 1, directory_path.length() - directory_name.length() - 1); + } + + return directory; +} + +/** + * @brief Check if a path exists on the filesystem + * + * @param path The path to check + * @return True if the path exists, false otherwise + */ +bool FileSystem::exists(const string &path) { + + // Get the directory at the path + string directory_path = Path::file_path(path); + Directory* directory = get_directory(directory_path); + + // Check if the directory exists + if (!directory) + return false; + + // Check if the file exists + const string file_name = Path::file_name(path); + return directory->open_file(file_name) != nullptr; +} \ No newline at end of file diff --git a/kernel/src/filesystem/format/ext2.cpp b/kernel/src/filesystem/format/ext2.cpp new file mode 100644 index 00000000..409cc751 --- /dev/null +++ b/kernel/src/filesystem/format/ext2.cpp @@ -0,0 +1,1136 @@ +// +// Created by 98max on 17/07/2025. +// +#include + +using namespace MaxOS; +using namespace MaxOS::filesystem; +using namespace MaxOS::common; +using namespace MaxOS::filesystem::format; +using namespace MaxOS::filesystem::format::ext2; +using namespace MaxOS::drivers; +using namespace MaxOS::drivers::disk; +using namespace MaxOS::drivers::clock; + +Ext2Volume::Ext2Volume(drivers::disk::Disk *disk, lba_t partition_offset) +: disk(disk), + partition_offset(partition_offset) +{ + + // Read superblock + buffer_t superblock_buffer(&superblock, 1024); + disk->read(partition_offset + 2, &superblock_buffer, 512); + disk->read(partition_offset + 3, &superblock_buffer, 512); + + // Validate signature + ASSERT(superblock.signature == 0xEF53, "Ext2 Filesystem doesnt have a valid signature\n"); + + // Version 0 has constant inode info + if (superblock.version_major < 1) { + superblock.first_inode = 11; + superblock.inode_size = 128; + } + + // Parse the superblock + block_size = 1024 << superblock.block_size; + total_block_groups = (superblock.total_blocks + superblock.blocks_per_group - 1) / superblock.blocks_per_group; + block_group_descriptor_table_block = superblock.starting_block + 1; + block_group_descriptor_table_size = total_block_groups * sizeof(block_group_descriptor_t); + pointers_per_block = block_size / sizeof(uint32_t); + inodes_per_block = block_size / superblock.inode_size; + sectors_per_block = block_size / 512; + blocks_per_inode_table = (superblock.inode_size * superblock.inodes_per_group + (block_size - 1)) / block_size; + sectors_per_inode_table = (superblock.inode_size * superblock.inodes_per_group + (512 - 1)) / 512; + + // Read the block groups + block_groups = new block_group_descriptor_t *[total_block_groups]{nullptr}; + uint32_t bgdt_lba = partition_offset + block_group_descriptor_table_block * sectors_per_block; + uint32_t sectors_to_read = (block_group_descriptor_table_size + block_size - 1) / block_size * sectors_per_block; + buffer_t bg_buffer(sectors_to_read * 512); + for (uint32_t i = 0; i < sectors_to_read; ++i) + disk->read(bgdt_lba + i, &bg_buffer, 512); + + // Store the block groups + for (uint32_t i = 0; i < total_block_groups; ++i) { + block_groups[i] = new block_group_descriptor_t; + memcpy(block_groups[i], bg_buffer.raw() + i * sizeof(block_group_descriptor_t), sizeof(block_group_descriptor_t)); + } +} + +Ext2Volume::~Ext2Volume() = default; + +/** + * @breif Write a single block from a buffer into onto the disk + * + * @param block_num The block to update + * @param buffer The buffer to read from + */ +void Ext2Volume::write_block(uint32_t block_num, buffer_t *buffer) { + + // Ensure the buffer is in the right format + buffer->set_offset(0); + bool old = buffer->update_offset; + buffer->update_offset = true; + + // Read each sector of the block + for (size_t i = 0; i < sectors_per_block; ++i) + disk->write(partition_offset + block_num * sectors_per_block + i, buffer, 512); + + // Reset buffer + buffer->set_offset(0); + buffer->update_offset = old; +}; + +/** + * @brief Write an inode to the filesystem + * + * @param inode_num The inode index + * @param inode The inode to read from + */ +void Ext2Volume::write_inode(uint32_t inode_num, inode_t *inode) { + + // Locate the inode + uint32_t group = (inode_num - 1) / superblock.inodes_per_group; + uint32_t index = (inode_num - 1) % superblock.inodes_per_group; + + // Locate the block + uint32_t inode_table = block_groups[group]->inode_table_address; + uint32_t offset = index * superblock.inode_size; + uint32_t block = offset / block_size; + uint32_t in_block_offset = offset % block_size; + + // Read the inode + buffer_t buffer(block_size); + read_block(inode_table + block, &buffer); + buffer.copy_from(inode, sizeof(inode_t), in_block_offset); + + // Modify the block + write_block(inode_table + block, &buffer); +} + +/** + * @breif Reads a single block from the disk into a buffer + * + * @param block_num The block to read + * @param buffer The buffer to read into + */ +void Ext2Volume::read_block(uint32_t block_num, buffer_t *buffer) const { + + // Ensure the buffer is in the right format + buffer->set_offset(0); + + // Read each sector of the block + for (size_t i = 0; i < sectors_per_block; ++i) + disk->read(partition_offset + block_num * sectors_per_block + i, buffer, 512); + + // Reset buffer + buffer->set_offset(0); + +} + +/** + * @brief Read an inode from the filesystem + * + * @param inode_num The inode index + */ +inode_t Ext2Volume::read_inode(uint32_t inode_num) const { + + inode_t inode; + + // Locate the inode + uint32_t group = (inode_num - 1) / superblock.inodes_per_group; + uint32_t index = (inode_num - 1) % superblock.inodes_per_group; + + // Locate the block + uint32_t inode_table = block_groups[group]->inode_table_address; + uint32_t offset = index * superblock.inode_size; + uint32_t block = offset / block_size; + uint32_t in_block_offset = offset % block_size; + + // Read the block + buffer_t buffer(block_size); + read_block(inode_table + block, &buffer); + + // Read the inode from the block + buffer.copy_to(&inode, sizeof(inode_t), in_block_offset); + return inode; +} + +/** + * @brief Allocates a single block to be used by an inode + * + * @return The new block number or 0 if the allocation failed + */ +uint32_t Ext2Volume::allocate_block() { + + return allocate_blocks(1)[0]; +} + +/** + * @brief Allocates a set of blocks to be used by an inode + * + * @param amount The amount of blocks to allocate + * @return A list of the allocated blocks or [0] if the allocation failed + */ +Vector Ext2Volume::allocate_blocks(uint32_t amount) { + + // No blocks to allocate + if (!amount) + return {1, 0}; + + // Find the block group with enough free blocks + block_group_descriptor_t *block_group = block_groups[0]; + for (uint32_t bg_index = 0; bg_index < total_block_groups; block_group = block_groups[++bg_index]) + if (block_group->free_blocks >= amount) + return allocate_group_blocks(bg_index, amount); + + // No block group can contain the block so split across multiple + Vector result{}; + while (amount > 0) { + + // Find the block group with most free blocks + block_group = block_groups[0]; + uint32_t bg_index = 0; + for (; bg_index < total_block_groups; ++bg_index) + if (block_groups[bg_index]->free_blocks > block_group->free_blocks) + block_group = block_groups[bg_index]; + + // No space + if (block_group->free_blocks == 0) + return {1, 0}; + + // Allocate the remaining blocks + auto allocated = allocate_group_blocks(bg_index, 1); + amount -= allocated.size(); + for (auto block: allocated) + result.push_back(block); + } + + return result; +} + +/** + * @brief Allocate a certain amount of blocks in a block group + * + * @param block_group The block group where the allocation is performed + * @param amount The amount of blocks to allocate + * @return The block numbers allocated + */ +common::Vector Ext2Volume::allocate_group_blocks(uint32_t block_group, uint32_t amount) { + + // Ensure enough space + block_group_descriptor_t *descriptor = block_groups[block_group]; + if (amount > descriptor->free_blocks) + return {1, 0}; + + // Prepare + Vector result{}; + buffer_t zeros(block_size); + zeros.clear(); + + // Read bitmap + buffer_t bitmap(block_size); + read_block(descriptor->block_usage_bitmap, &bitmap); + + // Allocate the blocks + for (uint32_t i = 0; i < superblock.blocks_per_group; ++i) { + + // Block is already used + if ((bitmap.raw()[i / 8] & (1u << (i % 8))) != 0) + continue; + + // Mark as used + descriptor->free_blocks--; + superblock.unallocated_blocks--; + bitmap.raw()[i / 8] |= (uint8_t) (1u << (i % 8)); + + // Zero out data + uint32_t block = block_group * superblock.blocks_per_group + superblock.starting_block + i; + write_block(block, &zeros); + result.push_back(block); + + // All done + amount--; + if (!amount) + break; + } + + // Save the changed metadata + write_block(descriptor->block_usage_bitmap, &bitmap); + write_back_block_groups(); + write_back_superblock(); + + return result; +} + +/** + * @brief Free a certain amount of blocks in a block group and zero's them out. + * + * @param block_group The block group where the blocks exist + * @param amount The amount of blocks to free + */ +void Ext2Volume::free_group_blocks(uint32_t block_group, uint32_t amount, uint32_t start) { + + // Read bitmap + block_group_descriptor_t *descriptor = block_groups[block_group]; + buffer_t bitmap(block_size); + read_block(descriptor->block_usage_bitmap, &bitmap); + + // Convert start to be index based on the group instead of global + start -= (block_group * superblock.blocks_per_group + superblock.starting_block); + + // Free the blocks + for (uint32_t i = start; i < start + amount; ++i) { + + // Block is already free (shouldn't happen) + if ((bitmap.raw()[i / 8] & (1u << (i % 8))) == 0) + continue; + + // Mark as free + descriptor->free_blocks++; + superblock.unallocated_blocks++; + bitmap.raw()[i / 8] &= ~(1u << (i % 8)); + + // TODO: Decide whether to zero out or not, my implementation zeros on allocation but idk about others + } + + // Save the changed metadata + write_block(descriptor->block_usage_bitmap, &bitmap); + write_back_block_groups(); + write_back_superblock(); + +} + + +/** + * @brief Save any changes of the block groups to the disk + */ +void Ext2Volume::write_back_block_groups() const { + + // Locate the block groups + uint32_t bgdt_blocks = (block_group_descriptor_table_size + block_size - 1) / block_size; + uint32_t sectors_to_write = bgdt_blocks * sectors_per_block; + uint32_t bgdt_lba = partition_offset + block_group_descriptor_table_block * sectors_per_block; + + // Copy the block groups into the buffer + buffer_t bg_buffer(sectors_to_write * 512); + for (uint32_t i = 0; i < total_block_groups; ++i) + bg_buffer.copy_from(block_groups[i], sizeof(block_group_descriptor_t)); + + // Write the buffer to disk + bg_buffer.set_offset(0); + for (uint32_t i = 0; i < sectors_to_write; ++i) + disk->write(bgdt_lba + i, &bg_buffer, 512); +} + +/** + * @brief Save the in memory superblock to the disk + */ +void Ext2Volume::write_back_superblock() { + + // Store superblock + buffer_t buffer(1024); + buffer.copy_from(&superblock, sizeof(superblock_t)); + buffer.set_offset(0); + + // Write to disk + disk->write(partition_offset + 2, &buffer, 512); + disk->write(partition_offset + 3, &buffer, 512); +} + +/** + * @brief How many blocks are needed to contain a set amount of bytes + * + * @param bytes Bytes needed + * @return The blocks required + */ +uint32_t Ext2Volume::bytes_to_blocks(size_t bytes) const { + return (bytes + block_size - 1) / block_size; +} + +/** + * @brief Allocates a new inode and sets the base metadata. Also allocates block 0 of the inode + * + * @param is_directory is the inode to be used for a directory + * @return The new inode + */ +uint32_t Ext2Volume::create_inode(bool is_directory) { + + ext2_lock.lock(); + + // Find the block group with enough free inodes + block_group_descriptor_t *block_group = block_groups[0]; + uint32_t bg_index = 0; + for (; bg_index < total_block_groups; block_group = block_groups[++bg_index]) + if (block_group->free_inodes >= 1) + break; + + // Read bitmap + buffer_t bitmap(block_size); + read_block(block_group->block_inode_bitmap, &bitmap); + + // First group contains reserved inodes + uint32_t inode_index = 0; + if (bg_index == 0 && superblock.first_inode > 1) + inode_index = superblock.first_inode - 1; + + // Find a free inode + for (; inode_index < superblock.inodes_per_group; ++inode_index) { + + // Block is already used + if ((bitmap.raw()[inode_index / 8] & (1u << (inode_index % 8))) != 0) + continue; + + // Mark as used + block_group->free_inodes--; + superblock.unallocated_inodes--; + bitmap.raw()[inode_index / 8] |= (uint8_t) (1u << (inode_index % 8)); + + break; + } + + // Convert into the 1-based inode index in the group + inode_index += bg_index * superblock.inodes_per_group + 1; + + // Save the changed metadata + write_block(block_group->block_inode_bitmap, &bitmap); + write_back_block_groups(); + write_back_superblock(); + + // Create the inode + inode_t inode{}; + inode.creation_time = time_to_epoch(Clock::active_clock()->get_time()); + inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time()); + inode.block_pointers[0] = allocate_block(); + inode.hard_links = is_directory ? 2 : 1; + inode.type = ((uint16_t) (is_directory ? InodeType::DIRECTORY : InodeType::FILE) >> 12) & 0xF; + inode.permissions = + (uint16_t) (is_directory ? InodePermissionsDefaults::DIRECTORY : InodePermissionsDefaults::FILE) & 0x0FFF; + write_inode(inode_index, &inode); + + ext2_lock.unlock(); + return inode_index; +} + +/** + * @brief Mark an inode as free. Note: does NOT unallocated the inodes blocks, use free_group_blocks(). + * @see free_group_blocks + * + * @param inode The inode number to mark as free + */ +void Ext2Volume::free_inode(uint32_t inode) { + + // Find the block group containing the inode + uint32_t bg_index = (inode - 1) / superblock.inodes_per_group; + block_group_descriptor_t *block_group = block_groups[bg_index]; + + // Read bitmap + buffer_t bitmap(block_size); + read_block(block_group->block_inode_bitmap, &bitmap); + + // First group contains reserved inodes + uint32_t inode_index = (inode - 1) % superblock.inodes_per_group; + if (bg_index == 0 && (inode_index < (superblock.first_inode - 1))) + return; + + // Mark as used + block_group->free_inodes++; + superblock.unallocated_inodes++; + bitmap.raw()[inode_index / 8] &= (uint8_t) ~(1u << (inode_index % 8)); + + // Save the changed metadata + write_block(block_group->block_inode_bitmap, &bitmap); + write_back_block_groups(); + write_back_superblock(); +} + +/** + * @brief Frees a group of blocks. Preferably with adjacent blocks apearing next to each other but not enforced. + * @param blocks The blocks to free + */ +void Ext2Volume::free_blocks(const common::Vector &blocks) { + + // No blocks to free + if (blocks.empty()) + return; + + uint32_t start = blocks[0]; + uint32_t previous = start; + uint32_t amount = 1; + + // Free each adjacent set of blocks + for (auto &block: blocks) { + + // First is already accounted for + if (block == start) + continue; + + // Is this block adjacent + if ((previous + 1) == block) { + previous = block; + amount += 1; + continue; + } + + // Adjacent set has ended + uint32_t group = (start - superblock.starting_block) / superblock.blocks_per_group; + free_group_blocks(group, amount, start); + + // Reset + start = block; + previous = start; + amount = 1; + } + + // Account for the last set of blocks in the loop + uint32_t group = (start - superblock.starting_block) / superblock.blocks_per_group; + free_group_blocks(group, amount, start); +} + + +InodeHandler::InodeHandler(Ext2Volume *volume, uint32_t inode_index) +: m_volume(volume), + inode_number(inode_index), + inode(m_volume->read_inode(inode_number)) +{ + + // Read the block pointers + for (uint32_t direct_pointer = 0; direct_pointer < 12; ++direct_pointer) + if (inode.block_pointers[direct_pointer]) + block_cache.push_back(inode.block_pointers[direct_pointer]); + + buffer_t buffer(m_volume->block_size); + parse_indirect(1, inode.l1_indirect, &buffer); + parse_indirect(2, inode.l2_indirect, &buffer); + parse_indirect(3, inode.l3_indirect, &buffer); +} + +/** + * @brief Read the size upper and lower into a single size_t + * @return The size of the inode data (not the inode itself) + */ +size_t InodeHandler::size() const { + return ((size_t) inode.size_upper << 32) | (size_t) inode.size_lower; +} + +/** + * @brief Store the size of the inode data. (does not write to disk) + * @param size The new size + */ +void InodeHandler::set_size(size_t size) { + + inode.size_lower = (uint32_t) (size & 0xFFFFFFFFULL); + inode.size_upper = (uint32_t) (size >> 32); + +} + +/** + * @brief Caches the indirect layers block pointers + * + * @param level Recursion level + * @param block The block number to parse from + * @param buffer Buffer to read into + */ +void InodeHandler::parse_indirect(uint32_t level, uint32_t block, buffer_t *buffer) { + + // Invalid + if (block == 0) + return; + + // Read the block + m_volume->read_block(block, buffer); + auto *pointers = (uint32_t *) (buffer->raw()); + + // Parse the pointers + for (size_t i = 0; i < m_volume->pointers_per_block; ++i) { + uint32_t pointer = pointers[i]; + + // Invaild + if (pointer == 0) + break; + + // Has indirect sub entries + if (level > 1) { + parse_indirect(level - 1, pointer, buffer); + continue; + } + + // Parse the entry + block_cache.push_back(pointer); + } +} + +/** + * @brief Writes the Cache to the indirect layer block pointers + * + * @param level Recursion level + * @param block The block number to parse from + * @param buffer Buffer to read into + * @param index Current entry to write + */ +void InodeHandler::write_indirect(uint32_t level, uint32_t &block, size_t &index) { + + // Nothing left to write + size_t remaining = block_cache.size() - index; + if (remaining == 0 || index >= block_cache.size()) + return; + + // Level hasn't been set yet + if (block == 0) + block = m_volume->allocate_block(); + + // Allocate a local buffer for this recursion level + buffer_t buffer(m_volume->block_size); + buffer.clear(); + auto *pointers = (uint32_t *) buffer.raw(); + + // Write the pointers + for (size_t i = 0; i < m_volume->pointers_per_block; ++i) { + + // Invalid + if (index >= block_cache.size()) + break; + + // Has indirect + if (level > 1) { + write_indirect(level - 1, pointers[i], index); + continue; + } + + // Save the pointer + pointers[i] = block_cache[index++]; + } + + m_volume->write_block(block, &buffer); +} + +/** + * @brief Saves the blocks to both the cached array and on disk inode + * + * @param blocks The blocks to save + */ +void InodeHandler::store_blocks(Vector const &blocks) { + + Logger::DEBUG() << "STORING BLOCKS\n"; + + // Store in cache + for (auto block: blocks) + block_cache.push_back(block); + + // Direct blocks + for (uint32_t i = 0; i < 12; ++i) + inode.block_pointers[i] = i < block_cache.size() ? block_cache[i] : 0; + + // No need to do any indirects + if (block_cache.size() < 12) + return; + + // Setup Recursive blocks + size_t index = 12; + + // Write the blocks + uint32_t indirect_blocks[3] = {inode.l1_indirect, inode.l2_indirect, inode.l3_indirect}; + for (int i = 0; i < 3; ++i) { + + // Have to use temp because of packed field + uint32_t temp = indirect_blocks[i]; + write_indirect(i + 1, temp, index); + indirect_blocks[i] = temp; + } + + // Save the new blocks + inode.l1_indirect = indirect_blocks[0]; + inode.l2_indirect = indirect_blocks[1]; + inode.l3_indirect = indirect_blocks[2]; + + // NOTE: Blocks get allocated when writing indirects. This is then saved later in the write() function +} + +/** + * @brief Increase the size of the inode's storage capacity by allocating new blocks. + * + * @param amount The amount to grow to in bytes + * @return + */ +size_t InodeHandler::grow(size_t amount, bool flush) { + + // Nothing to grow + if (amount <= 0) + return size(); + + // Allocate new blocks + auto blocks = m_volume->allocate_blocks(m_volume->bytes_to_blocks(amount)); + ASSERT(blocks[0] != 0, "Failed to allocate new blocks for file"); + + // Save the changes + store_blocks(blocks); + set_size(size() + amount); + if (flush) + save(); + + return size() + amount; +} + +/** + * @brief Writes the inode meta data to disk + */ +void InodeHandler::save() { + + m_volume->write_inode(inode_number, &inode); +} + +/** + * @brief Marks this inode's blocks as free and then the inode as free + */ +void InodeHandler::free() { + + m_volume->ext2_lock.lock(); + + // Free the inode + m_volume->free_blocks(block_cache); + m_volume->free_inode(inode_number); + + m_volume->ext2_lock.unlock(); +} + +InodeHandler::~InodeHandler() = default; + +Ext2File::Ext2File(Ext2Volume *volume, uint32_t inode, string const &name) +: m_volume(volume), + m_inode(volume, inode) +{ + + // Set up the base information + m_name = name; + m_size = m_inode.size(); + +} + +/** + * @brief Write data to the file (at the current seek position, updated to be += amount) + * + * @param data The byte buffer to write + * @param amount The amount of data to write + */ +void Ext2File::write(buffer_t const *data, size_t amount) { + + // Nothing to write + if (amount == 0) + return; + + // Prepare for writing + m_volume->ext2_lock.lock(); + const uint32_t block_size = m_volume->block_size; + buffer_t buffer(block_size); + + // Expand the file + if (m_offset + amount > m_size) + m_size = m_inode.grow((m_offset + amount) - m_size, false); + + // Save the updated metadata + m_inode.inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time()); + m_inode.save(); + + // Convert bytes to blocks + uint32_t block_start = m_offset / block_size; + uint32_t block_offset = m_offset % block_size; + + // Write each block + size_t current_block = block_start; + size_t written = 0; + while (written < amount) { + + // Read the block + uint32_t block = m_inode.block_cache[current_block++]; + m_volume->read_block(block, &buffer); + + // Where in this block to start writing + size_t buffer_start = (current_block - 1 == block_start) ? block_offset : 0; + size_t writable = (amount - written < block_size - buffer_start) ? (amount - written) : (block_size - + buffer_start); + + // Update the block + buffer.copy_from(data, writable, buffer_start, written); + m_volume->write_block(block, &buffer); + written += writable; + } + + // Clean up + m_offset += amount; + m_volume->ext2_lock.unlock(); +} + +/** +* @brief Read data from the file (at the current seek position, updated to be += amount) +* +* @param data The byte buffer to read into +* @param amount The amount of data to read +*/ +void Ext2File::read(buffer_t *data, size_t amount) { + + // Nothing to read + if (m_size == 0 || amount == 0) + return; + + // Prepare for reading + m_volume->ext2_lock.lock(); + const uint32_t block_size = m_volume->block_size; + buffer_t buffer(block_size); + + // Force bounds + if (m_offset + amount > m_size) + amount = m_size - m_offset; + + // Convert bytes to blocks + uint32_t block_start = m_offset / block_size; + uint32_t block_offset = m_offset % block_size; + + // Read each block + size_t current_block = block_start; + size_t read = 0; + while (read < amount) { + + // Read the block + uint32_t block = m_inode.block_cache[current_block++]; + m_volume->read_block(block, &buffer); + + // Where in this block to start reading + size_t buffer_start = (current_block - 1 == block_start) ? block_offset : 0; + size_t readable = (amount - read < block_size - buffer_start) ? (amount - read) : (block_size - buffer_start); + + // Read the block + buffer.copy_to(data, readable, buffer_start, read); + read += readable; + + } + + // Clean up + m_offset += amount; + m_volume->ext2_lock.unlock(); +} + +/** + * @brief Flush the file to the disk + */ +void Ext2File::flush() { + File::flush(); +} + +Ext2File::~Ext2File() = default; + +Ext2Directory::Ext2Directory(Ext2Volume *volume, uint32_t inode, const string &name) +: m_volume(volume), + m_inode(m_volume, inode) +{ + m_name = name; +} + +/** + * @brief Store all entries from a buffer and convert to File or Directory objects + */ +void Ext2Directory::parse_block(buffer_t *buffer) { + + size_t offset = 0; + while (offset < m_volume->block_size) { + + // Read the entry + auto *entry = (directory_entry_t *) (buffer->raw() + offset); + m_entries.push_back(*entry); + + // Not valid + if (entry->inode == 0 || entry->name_length == 0) + break; + + // Parse + string filename(buffer->raw() + offset + sizeof(directory_entry_t), entry->name_length); + m_entry_names.push_back(filename); + uint32_t inode = entry->inode; + + // Create the object + switch ((EntryType) entry->type) { + + case EntryType::FILE: + m_files.push_back(new Ext2File(m_volume, inode, filename)); + break; + + case EntryType::DIRECTORY: + m_subdirectories.push_back(new Ext2Directory(m_volume, inode, filename)); + break; + + default: + Logger::WARNING() << "Unknown entry type: " << entry->type << "\n"; + + } + + // Go to next + offset += entry->size; + } +} + +/** + * @brief Removes an entry reference from the directory + * + * @param name The name of the entry to remove + * @param is_directory Is the entry expected to be a directory (otherwise assumed to be a file) + * @param clear Should the inode be freed and the data blocks unallocated + */ +void Ext2Directory::remove_entry(string const &name, bool is_directory, bool clear) { + + // Find the entry + uint32_t index = 0; + directory_entry_t *entry = nullptr; + for (; index < m_entries.size(); ++index) + if (m_entry_names[index] == name) { + entry = &m_entries[index]; + break; + } + + // No entry found + if (!entry || entry->type != (uint8_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE)) + return; + + // Clear the inode + if (clear) { + InodeHandler inode(m_volume, entry->inode); + inode.free(); + } + + // Remove the reference from this directory + m_entries.erase(entry); + m_entry_names.erase(m_entry_names.begin() + index); + write_entries(); +} + +/** + * @brief Rename a directory entry and save it to disk + * + * @param old_name The current name of the entry + * @param new_name The name to change it to + * @param is_directory Search for entries with the type DIRECTORY (otherwise assume FILE) + */ +void Ext2Directory::rename_entry(string const &old_name, string const &new_name, bool is_directory) { + + // Change the name + for (int i = 0; i < m_entry_names.size(); ++i) + if (m_entry_names[i] == old_name && + m_entries[i].type == (uint8_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE)) + m_entry_names[i] = new_name; + + // Save the change + write_entries(); + +} + +/** + * @brief Read the directory from the inode on the disk + */ +void Ext2Directory::read_from_disk() { + + m_volume->ext2_lock.lock(); + m_entries.clear(); + m_entry_names.clear(); + + // Clear the old files & Directories + for (auto &file: m_files) + delete file; + m_files.clear(); + + for (auto &directory: m_subdirectories) + delete directory; + m_subdirectories.clear(); + + // Read the direct blocks (cant use for( : )) + buffer_t buffer(m_volume->block_size); + for (int i = 0; i < m_inode.block_cache.size(); ++i) { + uint32_t block_pointer = m_inode.block_cache[i]; + + // Invalid block + if (block_pointer == 0) + break; + + // Parse the block + m_volume->read_block(block_pointer, &buffer); + parse_block(&buffer); + } + + m_volume->ext2_lock.unlock(); +} + +void Ext2Directory::write_entries() { + + // Calculate the size needed to store the entries and the null entry + size_t size_required = sizeof(directory_entry_t); + for (int i = 0; i < m_entries.size(); ++i) { + size_t size = sizeof(directory_entry_t) + m_entry_names[i].length() + 1; + size += (size % 4) ? 4 - size % 4 : 0; + size_required += size; + } + + // Expand the directory + size_t blocks_required = m_volume->bytes_to_blocks(size_required); + if (blocks_required > m_inode.block_cache.size()) + m_inode.grow((blocks_required - m_inode.block_cache.size()) * m_volume->block_size, false); + + // Prepare for writing + m_volume->ext2_lock.lock(); + const uint32_t block_size = m_volume->block_size; + buffer_t buffer(block_size, false); + buffer.clear(); + + // Save the updated metadata + m_inode.set_size(blocks_required * block_size); + m_inode.inode.last_modification_time = time_to_epoch(Clock::active_clock()->get_time()); + m_inode.save(); + + // Write each entry + size_t current_block = 0; + size_t buffer_offset = 0; + for (int i = 0; i < m_entries.size(); ++i) { + + // Get the current entry + directory_entry_t &entry = m_entries[i]; + char *name = m_entry_names[i].c_str(); + + // Update the size + entry.name_length = m_entry_names[i].length(); + entry.size = sizeof(directory_entry_t) + entry.name_length + 1; + entry.size += (entry.size % 4) ? 4 - (entry.size % 4) : 0; + + // Entry needs to be stored in the next block + if (entry.size + buffer_offset > block_size) { + m_volume->write_block(m_inode.block_cache[current_block], &buffer); + buffer.clear(); + current_block++; + buffer_offset = 0; + } + + // If it is the last entry it takes up the rest of the block + if (i == m_entries.size() - 1) + entry.size = block_size - buffer_offset; + + // Copy the entry and the name + buffer.copy_from(&entry, sizeof(entry), buffer_offset); + buffer.copy_from(name, entry.name_length + 1, buffer_offset + sizeof(entry)); + buffer_offset += entry.size; + } + + // Save the last block + m_volume->write_block(m_inode.block_cache[current_block], &buffer); + + // Clean up + m_volume->ext2_lock.unlock(); +} + +directory_entry_t Ext2Directory::create_entry(const string &name, uint32_t inode, bool is_directory) { + + // Create the inode + directory_entry_t entry{}; + entry.inode = inode ? inode : m_volume->create_inode(is_directory); + entry.type = (uint32_t) (is_directory ? EntryType::DIRECTORY : EntryType::FILE); + entry.name_length = name.length(); + entry.size = sizeof(entry) + entry.name_length; + entry.size += entry.size % 4 ? 4 - entry.size % 4 : 0; + + // Save the inode + m_entries.push_back(entry); + m_entry_names.push_back(name); + write_entries(); + + return entry; +} + +/** + * @brief Create a new file in the directory + * + * @param name The name of the file to create + * @return The new file object or null if it could not be created + */ +File *Ext2Directory::create_file(string const &name) { + + // Check if the file already exists + for (auto &file: m_files) + if (file->name() == name) + return nullptr; + + // Create the file + auto file = new Ext2File(m_volume, create_entry(name, 0, false).inode, name); + m_files.push_back(file); + return file; +} + +/** + * @brief Delete a file from this directory + * + * @param name The name of the file to delete + */ +void Ext2Directory::remove_file(string const &name) { + remove_entry(name, false); +} + +/** + * @brief Renames the file from the old name to the new name if it exists + * + * @param old_name The current name of the file that is to be changed + * @param new_name What the new name should be + */ +void Ext2Directory::rename_file(string const &old_name, string const &new_name) { + rename_entry(old_name, new_name, false); +} + +/** + * @brief Create a new directory in the directory + * + * @param name The name of the directory to create + * @return The new directory object or null if it could not be created + */ +Directory *Ext2Directory::create_subdirectory(string const &name) { + + // Check if the directory already exists + for (auto &subdirectory: m_subdirectories) + if (subdirectory->name() == name) + return nullptr; + + // Store the directory + auto directory = new Ext2Directory(m_volume, create_entry(name, 0, true).inode, name); + m_subdirectories.push_back(directory); + + // Create self & parent references + directory->create_entry(".", directory->m_inode.inode_number, true); + directory->create_entry("..", m_inode.inode_number, true); + + return directory; +} + +/** + * @brief Remove a directory entry from the directory + * + * @param name The name of the entry to remove + */ +void Ext2Directory::remove_subdirectory(string const &name) { + remove_entry(name, true); +} + +/** + * @brief Renames the directory from the old name to the new name if it exists + * + * @param old_name The current name of the directory that is to be changed + * @param new_name What the new name should be + */ +void Ext2Directory::rename_subdirectory(string const &old_name, string const &new_name) { + rename_entry(old_name, new_name, true); +} + +Ext2Directory::~Ext2Directory() = default; + +Ext2FileSystem::Ext2FileSystem(Disk *disk, uint32_t partition_offset) + : m_volume(disk, partition_offset) { + + // Create the root directory + m_root_directory = new Ext2Directory(&m_volume, 2, "/"); + m_root_directory->read_from_disk(); + +} + +Ext2FileSystem::~Ext2FileSystem() { + delete m_root_directory; +}; \ No newline at end of file diff --git a/kernel/src/filesystem/format/fat32.cpp b/kernel/src/filesystem/format/fat32.cpp new file mode 100644 index 00000000..381f77f3 --- /dev/null +++ b/kernel/src/filesystem/format/fat32.cpp @@ -0,0 +1,965 @@ +// +// Created by 98max on 1/01/2023. +// + +#include +#include + +using namespace MaxOS; +using namespace MaxOS::common; +using namespace MaxOS::drivers; +using namespace MaxOS::drivers::disk; +using namespace MaxOS::filesystem; +using namespace MaxOS::filesystem::format; +using namespace MaxOS::memory; + +Fat32Volume::Fat32Volume(Disk *hd, uint32_t partition_offset) +: disk(hd) +{ + + // Read the BIOS parameter block + buffer_t bpb_buffer(&bpb, sizeof(bpb32_t)); + disk->read(partition_offset, &bpb_buffer); + + // Parse the FAT info + uint32_t total_data_sectors = bpb.total_sectors_32 - (bpb.reserved_sectors + (bpb.table_copies * bpb.table_size_32)); + fat_total_clusters = total_data_sectors / bpb.sectors_per_cluster; + fat_lba = partition_offset + bpb.reserved_sectors; + fat_copies = bpb.table_copies; + fat_info_lba = partition_offset + bpb.fat_info; + data_lba = fat_lba + (bpb.table_copies * bpb.table_size_32); + root_lba = data_lba + bpb.sectors_per_cluster * (bpb.root_cluster - 2); + + // Read the fs info + buffer_t fs_buffer(&fsinfo, sizeof(fs_info_t)); + disk->read(fat_info_lba, &fs_buffer); + + // Validate the fat information + if (fsinfo.lead_signature != 0x41615252 || fsinfo.structure_signature != 0x61417272 || + fsinfo.trail_signature != 0xAA550000) { + Logger::ERROR() << "Invalid FAT32 filesystem information TODO: Handle this\n"; + return; + } +} + +Fat32Volume::~Fat32Volume() = default; + +/** + * @brief Take the cluster and gets the next cluster in the chain + * + * @param cluster The base cluster to start from + * @return The next cluster in the chain + */ +lba_t Fat32Volume::next_cluster(lba_t cluster) { + + // Get the location in the FAT table + lba_t offset = cluster * sizeof(uint32_t); + lba_t sector = fat_lba + (offset / bpb.bytes_per_sector); + uint32_t entry_index = offset % bpb.bytes_per_sector; + + // Read the FAT entry + buffer_t fat(bpb.bytes_per_sector); + disk->read(sector, &fat); + + // Get the next cluster info (mask the upper 4 bits) + auto entry = (uint32_t *) (&(fat.raw()[entry_index])); // TODO & here is weird + return *entry & 0x0FFFFFFF; +} + +/** + * @brief Sets the next cluster in the chain (where the base cluster should point) + * + * @param cluster The base cluster to start from + * @param next_cluster The next cluster in the chain + * @return The next cluster in the chain + */ +uint32_t Fat32Volume::set_next_cluster(uint32_t cluster, uint32_t next_cluster) { + + // TODO - when in userspace: For performance cache fat entirely, cache file data, cache cluster chains + + // Get the location in the FAT table + lba_t offset = cluster * sizeof(uint32_t); + + for (int i = 0; i < fat_copies; ++i) { + + lba_t sector = (fat_lba + i * bpb.table_size_32) + (offset / bpb.bytes_per_sector); + uint32_t entry_index = offset % bpb.bytes_per_sector; + + // Read the FAT entry + buffer_t fat(bpb.bytes_per_sector); + disk->read(sector, &fat); + + // Set the next cluster info (mask the upper 4 bits) + auto entry = (uint32_t *) (&(fat.raw()[entry_index])); + *entry = next_cluster & 0x0FFFFFFF; + disk->write(sector, &fat); + + } + + return next_cluster; +} + +/** + * @brief Searches the fat table for a free cluster starting from the first free cluster in the fsinfo, will then wrap around + * + * @return The first free cluster in the FAT table + */ +uint32_t Fat32Volume::find_free_cluster() { + + // Get the first free cluster + for (uint32_t start = fsinfo.next_free_cluster; start < fat_total_clusters + 1; start++) + if (next_cluster(start) == 0) + return start; + + // Check any clusters before the first free cluster + for (uint32_t start = 2; start < fsinfo.next_free_cluster; start++) + if (next_cluster(start) == 0) + return start; + + ASSERT(false, "No free clusters found in the FAT table"); + return 0; +} + +/** + * @brief Allocate a cluster in the FAT table + * + * @param cluster The base cluster to start from or 0 if this is a new chain + * @return The next cluster in the chain + */ +uint32_t Fat32Volume::allocate_cluster(uint32_t cluster) { + + // Allocate 1 cluster + return allocate_cluster(cluster, 1); +} + +/** + * @brief Allocate a number of clusters in the FAT table, updates the fsinfo and the chain + * + * @param cluster The base cluster to start from or 0 if this is a new chain + * @param amount The number of clusters to allocate + * @return The next cluster in the chain + */ +uint32_t Fat32Volume::allocate_cluster(uint32_t cluster, size_t amount) { + + // Make sure within bounds + if (cluster > fat_total_clusters || cluster + amount > fat_total_clusters) + return 0; + + // Go through allocating the clusters + for (size_t i = 0; i < amount; i++) { + uint32_t next_cluster = find_free_cluster(); + + // Update the fsinfo + fsinfo.next_free_cluster = next_cluster + 1; + fsinfo.free_cluster_count -= 1; + + // If there is an existing chain it needs to be updated + if (cluster != 0) + set_next_cluster(cluster, next_cluster); + + cluster = next_cluster; + } + + // Once all the updates are done flush the changes to the disk + buffer_t fs_info_buffer(&fsinfo, sizeof(fs_info_t)); + disk->write(fat_info_lba, &fs_info_buffer); + + // Finish the chin + set_next_cluster(cluster, (uint32_t) ClusterState::END_OF_CHAIN); + uint32_t next = next_cluster(cluster); + return cluster; +} + +/** + * @brief Free a cluster in the FAT table + * + * @param cluster The base cluster to start from + * @param full Weather the chain's length is 1 or not + */ +void Fat32Volume::free_cluster(lba_t cluster) { + + // Free 1 cluster + free_cluster(cluster, 1); + +} + +/** + * @brief Free a number of clusters in the FAT table + * + * @param cluster The base cluster to start from + * @param amount The number of clusters to free + */ +void Fat32Volume::free_cluster(uint32_t cluster, size_t amount) { + + // Make sure within bounds + if (cluster < 2 || cluster > fat_total_clusters || cluster + amount > fat_total_clusters) + return; + + // Go through freeing the clusters + for (size_t i = 0; i < amount; i++) { + + // Find the next cluster before it is removed from the chain + uint32_t next_in_chain = next_cluster(cluster); + + // Update the fsinfo + fsinfo.next_free_cluster = cluster; + fsinfo.free_cluster_count += 1; + + // Update the chain + set_next_cluster(cluster, (lba_t) ClusterState::FREE); + cluster = next_in_chain; + } + + // Save the fsinfo + buffer_t fs_info_buffer(&fsinfo, sizeof(fs_info_t)); + disk->write(fat_info_lba, &fs_info_buffer); + + // Mark the end of the chain + set_next_cluster(cluster, (uint32_t) ClusterState::END_OF_CHAIN); +} + + +Fat32File::Fat32File(Fat32Volume *volume, Fat32Directory *parent, dir_entry_t *info, const string &name) +: m_volume(volume), + m_parent_directory(parent), + m_entry(info), + m_first_cluster((info->first_cluster_high << 16) | info->first_cluster_low) +{ + + m_name = name; + m_size = info->size; + m_offset = 0; +} + +Fat32File::~Fat32File() = default; + +/** + * @brief Write data to the file (at the current seek position, updated to be += amount) + * + * @param data The byte buffer to write + * @param amount The amount of data to write + */ +void Fat32File::write(const buffer_t *data, size_t amount) { + + size_t buffer_space = m_volume->bpb.bytes_per_sector * m_volume->bpb.sectors_per_cluster; + buffer_t buffer(buffer_space); + buffer.clear(); + + uint64_t current_offset = 0; + uint64_t bytes_written = 0; + uint32_t last = m_first_cluster; + + // Read the file + for (uint32_t cluster = last; + cluster != (uint32_t) ClusterState::END_OF_CHAIN; cluster = m_volume->next_cluster(cluster)) { + last = cluster; + + // No cluster to read from (blank file) + if (cluster == 0) + break; + + // Skip clusters before the offset + if ((current_offset + buffer_space) < m_offset) { + current_offset += buffer_space; + continue; + } + + // Read each sector in the cluster (prevent overwriting the data) + lba_t lba = m_volume->data_lba + (cluster - 2) * m_volume->bpb.sectors_per_cluster; + for (size_t sector = 0; sector < m_volume->bpb.sectors_per_cluster; sector++) + m_volume->disk->read(lba + sector, &buffer, m_volume->bpb.bytes_per_sector); + buffer.set_offset(0); + + // If the offset is in the middle of the cluster + size_t buffer_offset = 0; + if (m_offset > current_offset) + buffer_offset = m_offset - current_offset; + + // Calculate how many bytes are being copied (read from cluster at offset? + // or read part of cluster?) + size_t cluster_remaining_bytes = buffer_space - buffer_offset; + size_t data_remaining_bytes = amount - bytes_written; + size_t bytes_to_copy = (cluster_remaining_bytes < data_remaining_bytes) ? cluster_remaining_bytes + : data_remaining_bytes; + bytes_to_copy = (bytes_to_copy > buffer_space) ? buffer_space : bytes_to_copy; + + // Update the data + buffer.copy_from(data, bytes_to_copy, buffer_offset, bytes_written); + bytes_written += bytes_to_copy; + current_offset += bytes_to_copy; + buffer.set_offset(0); + + // Write the data back to the disk + for (size_t sector = 0; sector < m_volume->bpb.sectors_per_cluster; sector++) + m_volume->disk->write(lba + sector, &buffer, m_volume->bpb.bytes_per_sector); + } + + // Extend the file + while (bytes_written < amount) { + // Allocate a new cluster + uint32_t new_cluster = m_volume->allocate_cluster(last); + if (new_cluster == 0) + break; + + if (last == 0) + m_first_cluster = new_cluster; + + // Update the data + size_t bytes_to_copy = (amount - bytes_written) > buffer_space ? buffer_space : (amount - bytes_written); + buffer.copy_from(data, bytes_to_copy, 0, bytes_written); + bytes_written += bytes_to_copy; + current_offset += bytes_to_copy; + buffer.set_offset(0); + + // Write the data back to the disk + lba_t lba = m_volume->data_lba + (new_cluster - 2) * m_volume->bpb.sectors_per_cluster; + for (size_t sector = 0; sector < m_volume->bpb.sectors_per_cluster; sector++) + m_volume->disk->write(lba + sector, &buffer, m_volume->bpb.bytes_per_sector); + + // Go to the next cluster + last = new_cluster; + } + + // Update file size + m_offset += bytes_written; + if (m_offset > m_size) + m_size = m_offset; + + // Update entry info + m_entry->size = m_size; + m_entry->first_cluster_high = (m_first_cluster >> 16) & 0xFFFF; + m_entry->first_cluster_low = m_first_cluster & 0xFFFF; + // TODO: When implemented as a usermode driver save the time + m_parent_directory->save_entry_to_disk(m_entry); + +} + +/** + * @brief Read data from the file (at the current seek position, updated to be += amount) + * + * @param data The byte buffer to read into + * @param amount The amount of data to read + */ +void Fat32File::read(buffer_t *data, size_t amount) { + size_t buffer_space = m_volume->bpb.bytes_per_sector * m_volume->bpb.sectors_per_cluster; + buffer_t buffer(buffer_space); + buffer.clear(); + + uint64_t current_offset = 0; + uint64_t bytes_read = 0; + + // Read the file + for (uint32_t cluster = m_first_cluster; + cluster != (uint32_t) ClusterState::END_OF_CHAIN; cluster = m_volume->next_cluster(cluster)) { + + // Skip clusters before the offset + if ((current_offset + buffer_space) < m_offset) { + current_offset += buffer_space; + continue; + } + + // Read each sector in the cluster + lba_t lba = m_volume->data_lba + (cluster - 2) * m_volume->bpb.sectors_per_cluster; + for (size_t sector = 0; sector < m_volume->bpb.sectors_per_cluster; sector++) + m_volume->disk->read(lba + sector, &buffer, m_volume->bpb.bytes_per_sector); + buffer.set_offset(0); + + // If the offset is in the middle of the cluster + size_t buffer_offset = 0; + if (m_offset > current_offset) + buffer_offset = m_offset - current_offset; + + // Calculate how many bytes are being copied (read from cluster at offset? or read part of cluster?) + size_t cluster_remaining_bytes = buffer_space - buffer_offset; + size_t data_remaining_bytes = amount - bytes_read; + size_t bytes_to_copy = (cluster_remaining_bytes < data_remaining_bytes) ? cluster_remaining_bytes + : data_remaining_bytes; + bytes_to_copy = (bytes_to_copy > buffer_space) ? buffer_space : bytes_to_copy; + + // Read the data + buffer.copy_from(data, bytes_to_copy, buffer_offset, bytes_read); + bytes_read += bytes_to_copy; + current_offset += buffer_space; + + // Dont read more than needed + if (bytes_read >= amount) + break; + } + + m_offset += bytes_read; +} + +/** + * @brief Flush the file to the disk + */ +void Fat32File::flush() { + File::flush(); +} + +Fat32Directory::Fat32Directory(Fat32Volume *volume, uint32_t cluster, const string &name) +: m_volume(volume), + m_first_cluster(cluster) +{ + m_name = name; +} + +Fat32Directory::~Fat32Directory() = default; + +/** + * @brief Create a new entry in the directory + * + * @param name The name of the file or directory to create + * @param is_directory True if the entry is a directory, false if it is a file + * @return The cluster of the new entry + */ +dir_entry_t *Fat32Directory::create_entry(const string &name, bool is_directory) { + + // Allocate a cluster for the new entry + uint32_t cluster = m_volume->allocate_cluster(0); + if (cluster == 0) + return nullptr; + + // Store the name + Vector lfn_entries = to_long_filenames(name); + char short_name[8]; + char short_extension[3]; + for (int i = 0; i < 8; i++) + short_name[i] = (i < name.length()) ? name[i] : ' '; + for (int i = 0; i < 3; i++) + short_extension[i] = (8 + i < name.length()) ? name[8 + i] : ' '; + + // Create the directory entry + dir_entry_t entry = {}; + memcpy(entry.name, short_name, sizeof(short_name)); + memcpy(entry.extension, short_extension, sizeof(short_extension)); + entry.attributes = is_directory ? (uint8_t) DirectoryEntryAttributes::DIRECTORY + : (uint8_t) DirectoryEntryAttributes::ARCHIVE; + entry.first_cluster_high = (cluster >> 16) & 0xFFFF; + entry.first_cluster_low = cluster & 0xFFFF; + + // Find free space for the new entry and the name + size_t entries_needed = 1 + lfn_entries.size(); + int entry_index = find_free_entries(entries_needed); + if (entry_index == -1) + entry_index = expand_directory(entries_needed); + + // Store the entries in the cache + for (size_t i = 0; i < entries_needed; i++) + m_entries[entry_index + i] = i == 0 ? entry : *(dir_entry_t *) &lfn_entries[i - 1]; + + // Write the long file name entries + for (size_t index = entry_index + lfn_entries.size() - 1; index >= entry_index; index--) + update_entry_on_disk(index); + + // Creating the file is done + if (!is_directory) + return &m_entries[entry_index]; + + // Create the "." in the directory + dir_entry_t current_dir_entry = {}; + memcpy(current_dir_entry.name, ".", 1); + current_dir_entry.attributes = 0x10; + current_dir_entry.first_cluster_high = (cluster >> 16) & 0xFFFF; + current_dir_entry.first_cluster_low = cluster & 0xFFFF; + + // Create the ".." in the directory + dir_entry_t parent_dir_entry = {}; + memcpy(parent_dir_entry.name, "..", 2); + parent_dir_entry.attributes = 0x10; + parent_dir_entry.first_cluster_high = (m_first_cluster >> 16) & 0xFFFF; + parent_dir_entry.first_cluster_low = m_first_cluster & 0xFFFF; + + // Write the entries to the disk + uint32_t bytes_per_sector = m_volume->bpb.bytes_per_sector; + lba_t child_lba = m_volume->data_lba + (cluster - 2) * bytes_per_sector; + buffer_t buffer(bytes_per_sector); + buffer.clear(); + buffer.copy_from(¤t_dir_entry, sizeof(dir_entry_t)); + buffer.copy_from(&parent_dir_entry, sizeof(dir_entry_t)); + m_volume->disk->write(child_lba, &buffer); + + // Directory created + return &m_entries[entry_index]; +} + +/** + * @brief Remove an entry from the directory + * + * @param cluster The cluster of the entry to remove + * @param name The name of the entry to remove + */ +void Fat32Directory::remove_entry(uint32_t cluster, const string &name) { + + // Find the entry in the directory + int entry = entry_index(cluster); + if (entry == -1) + return; + + // Find any long file name entries that belong to this entry + size_t delete_entry_index = entry; + while (delete_entry_index > 0 && + m_entries[delete_entry_index - 1].attributes == (uint8_t) DirectoryEntryAttributes::LONG_NAME) + delete_entry_index--; + + // Mark the entries as free + for (size_t i = delete_entry_index; i < entry; i++) + m_entries[i].name[0] = (uint8_t) DirectoryEntryType::FREE; + + // Update the entries on the disk + for (size_t i = delete_entry_index; i <= entry; i++) + update_entry_on_disk(i); + + // Count the number of clusters in the chain + size_t cluster_count = 0; + for (uint32_t next_cluster = cluster; + next_cluster < (lba_t) ClusterState::BAD; next_cluster = m_volume->next_cluster(next_cluster)) + cluster_count++; + + // Free all the clusters in the chain + m_volume->free_cluster(cluster, cluster_count); +} + +/** + * @brief Read all of the directory entries for each cluster in this directory + */ +void Fat32Directory::read_all_entries() { + m_entries.clear(); + + size_t buffer_space = m_volume->bpb.bytes_per_sector * m_volume->bpb.sectors_per_cluster; + buffer_t buffer(buffer_space); + buffer.clear(); + + // Read the directory + for (uint32_t cluster = m_first_cluster; + cluster != (uint32_t) ClusterState::END_OF_CHAIN; cluster = m_volume->next_cluster(cluster)) { + + // Cache info to prevent re-traversing the chain + m_last_cluster = cluster; + m_current_cluster_length++; + + // Read each sector in the cluster + lba_t lba = m_volume->data_lba + (cluster - 2) * m_volume->bpb.sectors_per_cluster; + for (size_t sector = 0; sector < m_volume->bpb.sectors_per_cluster; sector++) + m_volume->disk->read(lba + sector, &buffer, m_volume->bpb.bytes_per_sector); + + // Parse the directory entries (each entry is 32 bytes) + for (size_t entry_offset = 0; entry_offset < buffer_space; entry_offset += 32) { + + // Store the entry + auto entry = (dir_entry_t *) &(buffer.raw()[entry_offset]); + m_entries.push_back(*entry); + + // Check if the entry is the end of the directory + if (entry->name[0] == (uint8_t) DirectoryEntryType::LAST) + return; + } + + } +} + + +/** + * @brief Writes an updated directory entry to the disk + * + * @param entry The entry to write + */ +void Fat32Directory::save_entry_to_disk(DirectoryEntry *entry) { + + int index = 0; + for (auto &m_entry: m_entries) { + if (&m_entry == entry) + break; + index++; + } + + update_entry_on_disk(index); +} + +void Fat32Directory::update_entry_on_disk(int index) { + + // Get the entry + auto entry = m_entries[index]; + + // Determine sector offset and in-sector byte offset + uint32_t bytes_per_sector = m_volume->bpb.bytes_per_sector; + uint32_t entry_offset = index * sizeof(dir_entry_t); + uint32_t sector_offset = entry_offset / bytes_per_sector; + uint32_t in_sector_offset = entry_offset % bytes_per_sector; + + // Find which cluster has the entry + uint32_t cluster = m_first_cluster; + for (uint32_t offset_remaining = entry_offset; + offset_remaining >= bytes_per_sector; offset_remaining -= bytes_per_sector) + cluster = m_volume->next_cluster(cluster); + + // Read the full sector into a buffer + lba_t base_lba = m_volume->data_lba + (cluster - 2) * m_volume->bpb.sectors_per_cluster; + buffer_t sector_buffer(bytes_per_sector, false); + m_volume->disk->read(base_lba + sector_offset, §or_buffer); + + // Update the entry in the buffer + sector_buffer.copy_from(&entry, sizeof(dir_entry_t), in_sector_offset); + m_volume->disk->write(base_lba + sector_offset, §or_buffer); +} + +/** + * @brief Find cluster's directory entry index in the store entries + * + * @param cluster The cluster + * @return The index or -1 if not found + */ +int Fat32Directory::entry_index(lba_t cluster) { + + int entry_index = 0; + for (; entry_index < m_entries.size(); entry_index++) { + auto &entry = m_entries[entry_index]; + + // End of directory means no more entries + if (entry.name[0] == (uint8_t) DirectoryEntryType::LAST) + return -1; + + // Skip free entries + if (entry.name[0] == (uint8_t) DirectoryEntryType::FREE) + continue; + + // Check if the entry is the one + uint32_t start_cluster = (entry.first_cluster_high << 16) | entry.first_cluster_low; + if (start_cluster == cluster) + break; + } + + // Make sure the entry is valid + if (entry_index >= m_entries.size()) + return -1; + + return entry_index; + +} + +/** + * @brief Find a series of free entries in a row + * + * @param amount The amount of adjacent entries to find + * + * @return The index of the first free entry or -1 if cant find that many free entries + */ +int Fat32Directory::find_free_entries(size_t amount) { + + for (int entry_index = 0; entry_index < m_entries.size(); entry_index++) { + // Check if there are enough free entries in a row + bool found = true; + for (size_t j = 0; j < amount; j++) + if (m_entries[entry_index + j].name[0] != (char) DirectoryEntryType::FREE) + found = false; + + if (found) + return entry_index; + } + + return -1; +} + + +/** + * @brief Expand the directory to fit 'amount - free' more entries. + * + * @note Caller must write data to the entries as only the updated LAST entry is + * saved to disk. Theoretically there wont be an issue if caller doesn't as the + * old LAST will still be in the same space. + * + * @param amount How many free entries are needed + * @return The index of the first free entry in the chain + */ +int Fat32Directory::expand_directory(size_t amount) { + + // Remove the old end of directory marker + int free_start = m_entries.size() - 1; + ASSERT(m_entries[free_start].name[0] == (uint8_t) DirectoryEntryType::LAST, "Last entry is not marked"); + m_entries[free_start].name[0] = (uint8_t) DirectoryEntryType::FREE; + + // Count how many free entries there is before the end + for (int i = free_start; i >= 0; --i) { + if (m_entries[i].name[0] == (char) DirectoryEntryType::FREE) + free_start = i; + else + break; + } + + // Calculate how many entries are need to be created (ie was there enough free entries already) + uint32_t found = m_entries.size() - free_start; + uint32_t needed_entries = amount + 1; + uint32_t additional_entries = 0; + if (needed_entries > found) + additional_entries = needed_entries - found; + + // Find the length of the current cluster chain + uint32_t total_entries = m_entries.size() + additional_entries; + uint32_t total_clusters = (total_entries * sizeof(dir_entry_t)) / + (m_volume->bpb.bytes_per_sector * m_volume->bpb.sectors_per_cluster); + + // Expand the cluster chain if needed + if (total_clusters > m_current_cluster_length) { + m_volume->allocate_cluster(m_last_cluster, total_clusters - m_current_cluster_length); + m_current_cluster_length = total_clusters; + } + + // Expand the directory to fit the remaining entries + for (int i = 0; i < additional_entries; ++i) { + dir_entry_t free = {}; + free.name[0] = (uint8_t) DirectoryEntryType::FREE; + m_entries.push_back(free); + } + + // Write the updated end of entries + m_entries[m_entries.size() - 1].name[0] = (uint8_t) DirectoryEntryType::LAST; + update_entry_on_disk(m_entries.size() - 1); + + return free_start; +} + +/** + * @brief Converts a string into a series of long file name entries (in reverse + * order, correct directory entry order) + * + * @param name The name + * @return A vector of longfile names + */ +Vector Fat32Directory::to_long_filenames(string name) { + + size_t lfn_count = (name.length() + 12) / 13; + Vector lfn_entries; + + // Create the long file name entries (in reverse order) + for (int i = lfn_count - 1; i >= 0; i--) { + // Create the long file name entry + long_file_name_entry_t lfn_entry; + lfn_entry.order = i + 1; + lfn_entry.attributes = 0x0F; + lfn_entry.type = 0; + lfn_entry.checksum = 0; + + // If it is the last entry, set the last bit + if (i == lfn_entries.size() - 1) + lfn_entry.order |= 0x40; + + // Set the name + for (int j = 0; j < 13; j++) { + + // Get the character info (0xFFFF if out of bounds) + size_t char_index = i * 13 + j; + char c = (char_index < name.length()) ? name[char_index] : 0xFFFF; + + // Set the character in the entry + if (j < 5) + lfn_entry.name1[j] = c; + else if (j < 11) + lfn_entry.name2[j - 5] = c; + else + lfn_entry.name3[j - 11] = c; + } + lfn_entries.push_back(lfn_entry); + } + + return lfn_entries; +} + +/** + * @brief Loads the characters from the entry and correctly appends to the whole name + * + * @param entry The entry to parse + * @param current The currently parsed string + * + * @return The parsed entry prepended to the current string + */ +string Fat32Directory::parse_long_filename(long_file_name_entry_t *entry, const string ¤t) { + + // Extract the long name from each part (in reverse order) + string current_long_name = ""; + for (int i = 0; i < 13; i++) { + + // Get the character (in utf8 encoding) + char c; + if (i < 5) + c = entry->name1[i] & 0xFF; + else if (i < 11) + c = entry->name2[i - 5] & 0xFF; + else + c = entry->name3[i - 11] & 0xFF; + + // Padding / invalid or end of string + if (c == (char) 0xFF || c == '\0') + break; + + // Add to the start as the entries are stored in reverse + current_long_name += string(c); + } + + // Entry parsed (prepend name) + return current_long_name + current; +} + +void Fat32Directory::read_from_disk() { + + for (auto &file: m_files) + delete file; + m_files.clear(); + + for (auto &directory: m_subdirectories) + delete directory; + m_subdirectories.clear(); + + // Load the entries from the disk into memory + read_all_entries(); + + // Parse the entries + string long_name = ""; + for (auto &entry: m_entries) { + + // Skip free entries and volume labels + if (entry.name[0] == (uint8_t) DirectoryEntryType::FREE + || entry.attributes == (uint8_t) DirectoryEntryAttributes::FREE + || entry.attributes == (uint8_t) DirectoryEntryAttributes::VOLUME_ID) + continue; + + // Extract the long name + if (entry.attributes == (uint8_t) DirectoryEntryAttributes::LONG_NAME) { + long_name = parse_long_filename((long_file_name_entry_t *) &entry, long_name); + continue; + } + + bool is_directory = entry.attributes == (uint8_t) DirectoryEntryAttributes::DIRECTORY; + + // Get the name of the entry + string name = long_name; + if (long_name == "") { + name = string(entry.name, 8); + + // Add the extension + if (!is_directory) + name = name.strip() + "." + string(entry.extension, 3); + } + + long_name = ""; + + // Get the starting cluster + uint32_t start_cluster = (entry.first_cluster_high << 16) | entry.first_cluster_low; + + // Store the file or directory + if (is_directory) + m_subdirectories.push_back(new Fat32Directory(m_volume, start_cluster, name.strip())); + else + m_files.push_back(new Fat32File(m_volume, this, &entry, name)); + + } +} + +/** + * @brief Create a new file in the directory + * + * @param name The name of the file to create + * @return The new file object or null if it could not be created + */ +File *Fat32Directory::create_file(const string &name) { + + // Check if the file already exists + for (auto &file: m_files) + if (file->name() == name) + return nullptr; + + // Check if the name is too long + if (name.length() > MAX_NAME_LENGTH) + return nullptr; + + // Create the file + auto file = new Fat32File(m_volume, this, create_entry(name, false), name); + m_files.push_back(file); + return file; +} + +/** + * @brief Delete a file from the subdirectory + * + * @param name The name of the file to delete + */ +void Fat32Directory::remove_file(const string &name) { + // Find the file if it exists + for (auto &file: m_files) + if (file->name() == name) { + + // Remove the file from the directory + m_files.erase(file); + remove_entry(((Fat32File *) file)->first_cluster(), name); + + // Delete the file reference + delete file; + return; + } +} + +/** + * @brief Create a new directory in the directory + * + * @param name The name of the directory to create + * @return The new directory object or null if it could not be created + */ +Directory *Fat32Directory::create_subdirectory(const string &name) { + + // Check if the directory already exists + for (auto &subdirectory: m_subdirectories) + if (subdirectory->name() == name) + return nullptr; + + // Check if the name is too long + if (name.length() > MAX_NAME_LENGTH) + return nullptr; + + // Create the directory + auto entry = create_entry(name, true); + uint32_t cluster = ((entry->first_cluster_high << 16) | entry->first_cluster_low); + + // Store the directory + auto directory = new Fat32Directory(m_volume, cluster, name); + m_subdirectories.push_back(directory); + return directory; +} + +/** + * @brief Remove a directory entry from the directory + * + * @param name The name of the entry to remove + */ +void Fat32Directory::remove_subdirectory(const string &name) { + // Find the directory if it exists + for (auto &subdirectory: m_subdirectories) { + if (subdirectory->name() != name) + continue; + + // Remove all the files in the directory + for (auto &file: subdirectory->files()) + subdirectory->remove_file(file->name()); + + // Remove all the subdirectories in the directory + for (auto &subdirectory: subdirectory->subdirectories()) + subdirectory->remove_subdirectory(subdirectory->name()); + + // Remove the entry + m_subdirectories.erase(subdirectory); + remove_entry(((Fat32Directory *) subdirectory)->first_cluster(), name); + + // Delete the directory + delete subdirectory; + return; + } +} + +Fat32FileSystem::Fat32FileSystem(Disk *disk, uint32_t partition_offset) +: m_volume(disk, partition_offset) +{ + + // Create the root directory + m_root_directory = new Fat32Directory(&m_volume, m_volume.bpb.root_cluster, "/"); + m_root_directory->read_from_disk(); + +} + +Fat32FileSystem::~Fat32FileSystem() = default; \ No newline at end of file diff --git a/kernel/src/filesystem/msdospart.cpp b/kernel/src/filesystem/msdospart.cpp deleted file mode 100644 index 32876599..00000000 --- a/kernel/src/filesystem/msdospart.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Created by 98max on 12/28/2022. -// - -#include - -using namespace MaxOS; -using namespace MaxOS::drivers::disk; -using namespace MaxOS::common; -using namespace MaxOS::filesystem; - -/** - * @brief read the partition table of a given hard disk - * - * @param hd The hard disk to read the partition table from - */ -void MSDOSPartitionTable::read_partitions(AdvancedTechnologyAttachment *hd) { - - // read the MBR from the hard disk - MasterBootRecord masterBootRecord = {}; - hd -> read_28(0, (uint8_t *)&masterBootRecord, sizeof(MasterBootRecord)); - - // Check if the magic number is correct - if(masterBootRecord.magicNumber != 0xAA55) - return; - - - // Loop through the primary partitions - for(auto& entry : masterBootRecord.primaryPartition){ - - if(entry.partitionId == 0) continue; // If the partition id is 0, skip it - - //TODO: Create a new FAT32 object - - } -} diff --git a/kernel/src/filesystem/partition/msdos.cpp b/kernel/src/filesystem/partition/msdos.cpp new file mode 100644 index 00000000..956576b3 --- /dev/null +++ b/kernel/src/filesystem/partition/msdos.cpp @@ -0,0 +1,65 @@ +// +// Created by 98max on 12/28/2022. +// + +#include + +using namespace MaxOS; +using namespace MaxOS::common; +using namespace MaxOS::filesystem; +using namespace MaxOS::filesystem::partition; +using namespace MaxOS::filesystem::format; +using namespace MaxOS::drivers::disk; + +/** + * @brief read the partition table of a given hard disk + * + * @param hd The hard disk to read the partition table from + */ +void MSDOSPartition::mount_partitions(Disk *hd) { + + // Read the MBR from the hard disk + MasterBootRecord mbr = {}; + buffer_t mbr_buffer(&mbr, sizeof(MasterBootRecord)); + hd->read(0, &mbr_buffer); + + // Check if the magic number is correct + if (mbr.magic != 0xAA55) { + Logger::WARNING() << "Could not find valid MBR on disk 0x" << (uint64_t) hd << "\n"; + return; + } + + // Get the VFS + VirtualFileSystem *vfs = VirtualFileSystem::current_file_system(); + + // Loop through the primary partitions + for (auto &entry: mbr.primary_partition) { + + // Empty entry + if (entry.type == 0) + continue; + + Logger::DEBUG() << "Partition 0x" << (uint64_t) entry.type << " at 0x" << (uint64_t) entry.start_LBA << ": "; + + // Create a file system for the partition + switch ((PartitionType) entry.type) { + case PartitionType::EMPTY: + Logger::Out() << "Empty partition\n"; + break; + + case PartitionType::FAT32: + Logger::Out() << "FAT32 partition\n"; + vfs->mount_filesystem(new Fat32FileSystem(hd, entry.start_LBA)); + break; + + case PartitionType::LINUX_EXT2: + Logger::Out() << "EXT2 partition\n"; + vfs->mount_filesystem(new ext2::Ext2FileSystem(hd, entry.start_LBA)); + break; + + default: + Logger::Out() << "Unknown or unimplemented partition type: 0x" << (uint64_t) entry.type << "\n"; + + } + } +} diff --git a/kernel/src/filesystem/path.cpp b/kernel/src/filesystem/path.cpp new file mode 100644 index 00000000..89354538 --- /dev/null +++ b/kernel/src/filesystem/path.cpp @@ -0,0 +1,210 @@ +// +// Created by 98max on 9/2/2025. +// +#include + +using namespace MaxOS; +using namespace MaxOS::common; +using namespace MaxOS::filesystem; + +/** + * @brief Check if a path is valid + * + * @param path The path to check + * @return True if the path is valid, false otherwise + */ +bool Path::valid(string path) { + + // Must not be empty + if (path.length() == 0) + return false; + + // Must start with a / + if (path[0] != '/') + return false; + + // Valid + return true; + +} + +/** + * @brief Check if a path is a file + * + * @param path The path to check + * @return True if the path is a file, false otherwise + */ +bool Path::is_file(const string& path) { + + return file_name(path).length() > 0 && file_extension(path).length() > 0; +} + + +/** + * @brief Get the file name component of a path if it exists or an empty string + * + * @param path The path to get the file name from + * @return The file name or the original path if it does not exist + */ +string Path::file_name(string path) { + + // Find the last / + int last_slash = -1; + for (int i = 0; i < path.length(); i++) + if (path[i] == '/') + last_slash = i; + + // Make sure there was a slash to split + if (last_slash == -1) + return path; + + // Get the file name + string file_name = path.substring(last_slash + 1, path.length() - last_slash - 1); + return file_name; +} + +/** + * @brief Try to get the file extension from a path (assumes split by ".") + * + * @param path The path to get the file extension from + * @return The file extension or the original path if it does not exist + */ +string Path::file_extension(string path) { + + // Find the last . + int last_dot = -1; + for (int i = 0; i < path.length(); i++) + if (path[i] == '.') + last_dot = i; + + // Make sure there was a dot to split + if (last_dot == -1) + return ""; + + // Get the file extension (what is after the ".") + string file_extension = path.substring(last_dot + 1, path.length() - last_dot - 1); + return file_extension; + +} + +/** + * @brief Finds the path to the directory the file is in + * + * @param path The path to get the file path from + * @return The file path or the original path if it does not exist + */ +string Path::file_path(string path) { + + // Try to find the last / + int last_slash = -1; + for (int i = 0; i < path.length(); i++) + if (path[i] == '/') + last_slash = i; + + // Make sure there was a slash to split + if (last_slash == -1) + return path; + + // Get the file path + string file_path = path.substring(0, last_slash); + return file_path; + +} + +/** + * @brief Get the top directory of a path + * + * @param path The path to get the top directory from + * @return The top directory or the original path if it does not exist + */ +string Path::top_directory(string path) { + + // Find the first / + int first_slash = -1; + for (int i = 0; i < path.length(); i++) + if (path[i] == '/') + first_slash = i; + + // Make sure there was a slash to split + if (first_slash == -1) + return path; + + // Get the top directory + string top_directory = path.substring(0, first_slash); + return top_directory; +} + +/** + * @brief Get the parent directory of the path + * + * @param path The path to either the file or the directory + * @return + */ +string Path::parent_directory(string path) { + + // Find the last / + int last_slash = -1; + for (int i = 0; i < path.length(); i++) + if (path[i] == '/') + last_slash = i; + + // If no slash or only root, return empty string + if (last_slash <= 0) + return "/"; + + // Return substring up to last slash + return path.substring(0, last_slash); +} + +/** + * @brief Calculates path with out any "../" or "./" + * + * @param path The path + * @return The new path, direct from root + */ +string Path::absolute_path(string path) { + + // Split the path into components + auto components = path.split("/"); + common::Vector absolute_components; + for (auto &component: components) { + + // Skip empty or self references + if (component == "" || component == ".") + continue; + + // Handle parent directory references + if (component == "..") { + if (!absolute_components.empty()) + absolute_components.pop_back(); + } else + absolute_components.push_back(component); + } + + // Join the components back into a path + string absolute_path = "/"; + for(const auto& component : absolute_components) + absolute_path += component + "/"; + + // Remove trailing slash if file + if(Path::is_file(absolute_path) && absolute_path.length() > 1) + absolute_path = absolute_path.substring(0, absolute_path.length() - 1); + + return absolute_path; +} + +/** + * @brief Joins two paths together + * + * @param base The base path + * @param extended What to add to the base path (if its from root, "/", then it will just return this) + * @return The joint path + */ +string Path::join_path(string base, string extended) { + + // The new path is from root + if(extended[0] == '/') + return extended; + + return base + extended; +} diff --git a/kernel/src/filesystem/vfs.cpp b/kernel/src/filesystem/vfs.cpp new file mode 100644 index 00000000..65f46f25 --- /dev/null +++ b/kernel/src/filesystem/vfs.cpp @@ -0,0 +1,504 @@ +// +// Created by Max Tyson on 20/04/2025. +// +#include +#include + +using namespace MaxOS; +using namespace MaxOS::filesystem; +using namespace MaxOS::common; + +VirtualFileSystem::VirtualFileSystem() { + + // Set the current file system to this instance + s_current_file_system = this; + +} + +VirtualFileSystem::~VirtualFileSystem() { + + // Remove all mounted filesystems + unmount_all(); + + // Set the current file system to null + s_current_file_system = nullptr; + +} + +/** + * @brief Get the active virtual file system + * + * @return The current virtual file system or null if none is set + */ +VirtualFileSystem* VirtualFileSystem::current_file_system() { + return s_current_file_system; +} + +/** + * @brief Add a filesystem to the virtual file system + * + * @param filesystem The filesystem to add + */ +void VirtualFileSystem::mount_filesystem(FileSystem* filesystem) { + + // Check if the filesystem is already mounted + if (filesystems.find(filesystem) != filesystems.end()) { + Logger::WARNING() << "Filesystem already mounted\n"; + return; + } + + // Get the mount point for the filesystem + string mount_point = "/filesystem_" + filesystems.size(); + + // If this is the first filesystem to be mounted, set the root filesystem + if (filesystems.size() == 0) + mount_point = "/"; + + // Add the filesystem to the map + filesystems.insert(filesystem, mount_point); +} + +/** + * @brief Add a filesystem to the virtual file system at a given mount point + * + * @param filesystem The filesystem to add + * @param mount_point The mount point for the filesystem + */ +void VirtualFileSystem::mount_filesystem(FileSystem* filesystem, const string& mount_point) { + + // Check if the filesystem is already mounted + if (filesystems.find(filesystem) != filesystems.end()) { + Logger::WARNING() << "Filesystem already mounted at " << mount_point << "\n"; + return; + } + + // Add the filesystem to the map + filesystems.insert(filesystem, mount_point); +} + +/** + * @brief Remove a filesystem from the virtual file system & delete it + * + * @param filesystem The filesystem to remove + */ +void VirtualFileSystem::unmount_filesystem(FileSystem* filesystem) { + + // Check if the filesystem is mounted + if (filesystems.find(filesystem) == filesystems.end()) + return; + + // Remove the filesystem from the map + filesystems.erase(filesystem); + + // Delete the filesystem + delete filesystem; +} + +/** + * @brief Remove a filesystem from the virtual file system + * + * @param mount_point Where the filesystem is mounted + */ +void VirtualFileSystem::unmount_filesystem(const string& mount_point) { + + // Remove the filesystem from the map + for(const auto& filesystem : filesystems) + if (filesystem.second == mount_point) + return unmount_filesystem(filesystem.first); + + // Filesystem not found + Logger::WARNING() << "Filesystem not found at " << mount_point << "\n"; +} + +/** + * @brief Remove all mounted filesystems + */ +void VirtualFileSystem::unmount_all() { + + // Loop through the filesystems and unmount them + for(const auto& filesystem : filesystems) + unmount_filesystem(filesystem.first); + +} + +/** + * @brief Get the root directory of the virtual file system + * + * @return The root directory of the virtual file system + */ +Directory* VirtualFileSystem::root_directory() { + + // Get the root filesystem + FileSystem* fs = root_filesystem(); + if (!fs) + return nullptr; + + // Get the root directory + return fs->root_directory(); +} + +/** + * @brief Get the filesystem mounted at the root + * + * @return The file system mounted "/" or null if none is mounted + */ +FileSystem* VirtualFileSystem::root_filesystem() { + + // Ensure there is at least one filesystem mounted + if (filesystems.size() == 0) + return nullptr; + + // It is always the first filesystem mounted + return filesystems.begin()->first; +} + +/** + * @brief Get a specific filesystem mounted at a given mount point + * + * @param mount_point The mount point to search for + * @return The filesystem mounted at the given mount point or null if none is found + */ +FileSystem* VirtualFileSystem::get_filesystem(const string& mount_point) { + + // Check if the filesystem is mounted + for(const auto& filesystem : filesystems) + if (filesystem.second == mount_point) + return filesystem.first; + + // Filesystem not found + return nullptr; +} + +/** + * @brief Find the filesystem that is responsible for a given path + * + * @param path The path to search for + * @return The filesystem that contains the path or null if none is found + */ +FileSystem* VirtualFileSystem::find_filesystem(string path) { + + // Longest matching path will be where the filesystem is mounted + string longest_match = ""; + FileSystem* longest_match_fs = nullptr; + + // Search through the filesystems + for (const auto& filesystem : filesystems) { + // Get the filesystem and mount point + FileSystem* fs = filesystem.first; + string mount_point = filesystem.second; + + // Check if the path starts with the mount point + if (path.starts_with(mount_point) && mount_point.length() > longest_match.length()) { + longest_match = mount_point; + longest_match_fs = fs; + } + } + + return longest_match_fs; +} + +/** + * @brief Get the relative path on a filesystem for a given VFS path (ie remove the mount point) + * + * @param filesystem The filesystem to get the path for + * @param path The path to get the relative path for + * @return The relative path on the filesystem or an empty string if none is found + */ +string VirtualFileSystem::get_relative_path(FileSystem* filesystem, string path) { + + // Find the mount point for the filesystem + auto fs = filesystems.find(filesystem); + if (fs == filesystems.end()) + return ""; + + // Get the mount point + string mount_point = fs->second; + + // Make sure that the path points to the filesystem + if (!path.starts_with(mount_point)) + return ""; + + // Get the relative path + string relative_path = path.substring(mount_point.length(), path.length() - mount_point.length()); + return relative_path; +} + +/** + * @brief Try to open a directory on the virtual file system and read it's contents + * + * @param path The path to the directory + * @return The directory object or null if it could not be opened + */ +Directory* VirtualFileSystem::open_directory(const string &path) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return nullptr; + + // Try to find the filesystem that is responsible for the path + FileSystem* fs = find_filesystem(path); + if (!fs) + return nullptr; + + // Get where to open the directory + string relative_path = get_relative_path(fs, path); + string directory_path = Path::file_path(relative_path); + + // Open the directory + Directory* directory = fs->get_directory(directory_path); + if (!directory) + return nullptr; + directory->read_from_disk(); + + return directory; +} + +Directory* VirtualFileSystem::open_directory(Directory* parent, string const& name) { + + // Open the file + Directory* opened_directory = parent->open_subdirectory(name); + if (!opened_directory) + return nullptr; + + opened_directory->read_from_disk(); + return opened_directory; +} + +/** + * @brief Attempts to open the parent directory and creates the sub directory at the end of the path + * + * @param path The path to the directory + * @return The directory object or null if it could not be opened + */ +Directory* VirtualFileSystem::create_directory(string path) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return nullptr; + + path = path.strip('/'); + + // Try to find the filesystem that is responsible for the path + FileSystem* fs = find_filesystem(path); + if (!fs) + return nullptr; + + // Open the parent directory + Directory* parent_directory = open_directory(path); + if (!parent_directory) + return nullptr; + + string directory_name = Path::file_name(path); + return create_directory(parent_directory, directory_name); +} + +/** + * @brief Creates a subdirectory in the specified directory and + * + * @param parent Where to create the directory + * @param name The name of the new directory + * @return The created directory + */ +Directory* VirtualFileSystem::create_directory(Directory* parent, string const &name) { + + // Create the directory + Directory* directory = parent->create_subdirectory(name); + directory->read_from_disk(); + + return directory; +} + + +/** + * @brief Attempts to open the parent directory and deletes the sub directory at the end of the path + * + * @param path The path to the directory + */ +void VirtualFileSystem::delete_directory(string path) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return; + + path = path.strip('/'); + + // Open the directory + Directory* parent_directory = open_directory(path); + if (!parent_directory) + return; + + // Delete the directory + string directory_name = Path::file_name(path); + delete_directory(parent_directory, directory_name); +} + +/** + * @brief Delete a directory on the virtual file system and it's sub contents + * + * @param parent The directory that contains the reference to the directory being deleted + * @param name The name of the directory to delete + */ +void VirtualFileSystem::delete_directory(Directory* parent, const string &name) { + + // Find the directory and delete it + for (const auto& directory: parent->subdirectories()) + if (directory->name() == name) + delete_directory(parent, directory); + +} + +/** + * @brief Delete a directory on the virtual file system and it's sub contents + * + * @param parent The directory that contains the reference to the directory being deleted + * @param directory The the directory to delete + */ +void VirtualFileSystem::delete_directory(Directory* parent, Directory* directory) { + + // Nothing to delete + if (!directory) + return; + + // Store a reference to each subdirectory and its parent + Map stack; + Vector> to_delete; + stack.push_back(parent, directory); + + while (!stack.empty()) { + + // Save the current + auto current = stack.pop_back(); + auto current_directory = current.second; + current_directory->read_from_disk(); + to_delete.push_back({current.first, current_directory}); + + // Empty the directory + for (const auto &file: current_directory->files()) + delete_file(current_directory, file->name()); + + // Process the subdirectories + for (const auto &subdir: current_directory->subdirectories()) + if (subdir->name() != "." && subdir->name() != "..") + stack.push_back(current_directory, subdir); + + } + + // Delete the directory from the bottom of the tree + for (int i = to_delete.size() - 1; i >= 0; --i) { + + // Get the parent and child + const auto ¤t = to_delete[i]; + Directory* owner = current.first; + Directory* subdirectory = current.second; + + owner->remove_subdirectory(subdirectory->name()); + } +} + + +/** + * @brief Attempts to open the parent directory and create the file at the end of the path + * + * @param path The path to the file (including the extension) + * @return The file object or null if it could not be created + */ +File* VirtualFileSystem::create_file(const string &path) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return nullptr; + + // Open the directory + Directory* directory = open_directory(path); + if (!directory) + return nullptr; + + // Create the file + string file_name = Path::file_name(path); + return create_file(directory, file_name); +} + +/** + * @brief Create a file in a directory + * + * @param parent The directory where the file should be created + * @param name The name of the file to create + */ +File* VirtualFileSystem::create_file(Directory* parent, string const &name) { + return parent->create_file(name); +} + +/** + * @brief Try to open a file on the virtual file system with a given offset + * + * @param path The path to the file (including the extension) + * @param offset The offset to seek to (default = 0) + * @return The file or null pointer if not found + */ +File* VirtualFileSystem::open_file(const string &path, size_t offset) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return nullptr; + + // Open the directory + Directory* directory = open_directory(path); + if (!directory) + return nullptr; + + return open_file(directory, Path::file_name(path), offset); +} + +/** + * @brief Opens a file in a directory with the given offset + * + * @param parent The directory containing the file + * @param name The name of the file to open + * @param offset How far in the file to open (default = 0) + * @return The file or null pointer if not found + */ +File* VirtualFileSystem::open_file(Directory* parent, string const &name, size_t offset) { + + // Open the file + File* opened_file = parent->open_file(name); + if (!opened_file) + return nullptr; + + // Seek to the offset + opened_file->seek(SeekType::SET, offset); + + return opened_file; +} + +/** + * @brief Opens a directory on the vfs and deletes the file at the end of the path + * + * @param path The path to the file (including the extension) + */ +void VirtualFileSystem::delete_file(const string &path) { + + // Ensure a valid path is given + if (!Path::valid(path)) + return; + + // Open the directory + Directory* directory = open_directory(path); + if (!directory) + return; + + // Delete the file + string file_name = Path::file_name(path); + delete_file(directory, file_name); +} + +/** + * @brief Delete a file in the given directory + * + * @param parent The directory containing the file + * @param name The name of the file + */ +void VirtualFileSystem::delete_file(Directory* parent, string const &name) { + + // Delete the file + parent->remove_file(name); +} \ No newline at end of file diff --git a/kernel/src/filesystem/vfsresource.cpp b/kernel/src/filesystem/vfsresource.cpp new file mode 100644 index 00000000..8f520f33 --- /dev/null +++ b/kernel/src/filesystem/vfsresource.cpp @@ -0,0 +1,387 @@ +// +// Created by 98max on 9/1/2025. +// +#include + +using namespace MaxOS; +using namespace MaxOS::filesystem; +using namespace MaxOS::processes; +using namespace MaxOS::common; +using namespace syscore::filesystem; + +FileResource::FileResource(string const& name, size_t flags, processes::resource_type_t type) +: Resource(name, flags, type), + file(nullptr) // Initialised by the registry +{ + +} + +FileResource::~FileResource() = default; + +/** + * @brief Read from a file resource + * + * @param buffer The buffer to read into + * @param size The number of bytes to read + * @param flags The flags to pass to the reading + * @return The number of bytes successfully read or -1 on error + */ +int FileResource::read(void* buffer, size_t size, size_t flags) { + + // File not found + if(!file) + return -1; + + // Handle the operation + switch ((FileFlags)flags) { + + case FileFlags::DEFAULT:{ + + buffer_t file_buffer(buffer, size); + file->read(&file_buffer, size); + break; + } + + case FileFlags::READ_SIZE:{ + return file->size(); + } + + case FileFlags::READ_OFFSET:{ + + return file->position(); + } + + default: + return -1; + } + return size; +} + +/** + * @brief Write to a file resource + * + * @param buffer The buffer to write from + * @param size The number of bytes to write + * @param flags The flags to pass to the writing + * @return The number of bytes successfully written or -1 on error + */ +int FileResource::write(void const* buffer, size_t size, size_t flags) { + + // File not found + if(!file) + return -1; + + // Handle the operation + switch ((FileFlags)flags) { + case FileFlags::DEFAULT:{ + + buffer_t file_buffer((void*)buffer, size); + file->write(&file_buffer, size); + break; + } + + case FileFlags::WRITE_SEEK_SET: + file->seek(SeekType::SET, size); + break; + + case FileFlags::WRITE_SEEK_CUR: + file->seek(SeekType::CURRENT, size); + break; + + case FileFlags::WRITE_SEEK_END: + file->seek(SeekType::END, size); + break; + + case FileFlags::WRITE_NAME:{ + + // Open the parent + Resource* parent = GlobalResourceRegistry::get_registry(resource_type_t::FILESYSTEM)->get_resource(Path::parent_directory(name())); + auto parent_directory = ((DirectoryResource*)parent)->directory; + + // Rename + string new_name = string((uint8_t*)buffer, size); + parent_directory->rename_file(file, new_name); + break; + } + + default: + return -1; + + } + + return size; +} + +DirectoryResource::DirectoryResource(string const& name, size_t flags, processes::resource_type_t type) +: Resource(name, flags, type), + directory(nullptr) +{ + +} + +DirectoryResource::~DirectoryResource() = default; + +/** + * @brief Copies all the entries in this directory into a buffer + * + * @param buffer The buffer to copy into + * @param size The size of the buffer + */ +void DirectoryResource::write_entries(void const* buffer, size_t size) const { + + size_t amount_written = 0; + + entry_information_t* entry = nullptr; + size_t entry_capacity = 0; + + auto write_single_entry = [&](const string& name, size_t entry_size, bool is_file) { + + // Make sure there is enough space + size_t required_size = sizeof(entry_information_t) + name.length() + 1; + if (required_size > entry_capacity) { + delete[] (uint8_t*)entry; + entry = (entry_information_t*)(new uint8_t[required_size]); + entry_capacity = required_size; + } + + // Create the entry + entry->is_file = is_file; + entry->size = entry_size; + entry->entry_length = required_size; + memcpy(entry->name, name.c_str(), name.length()); + entry->name[name.length()] = '\0'; + + // Not enough space + if (amount_written + entry->entry_length > size) + return false; + + // Copy the entry + memcpy((uint8_t*)buffer + amount_written, entry, entry->entry_length); + amount_written += entry->entry_length; + return true; + }; + + // Write files + for (const auto& file : directory->files()) { + if (!write_single_entry(file->name(), file->size(), true)) + break; + } + + // Write directories + for (const auto& dir : directory->subdirectories()) { + if (!write_single_entry(dir->name(), dir->size(), false)) + break; + } + + delete[] (uint8_t*)entry; +} + +/** + * @brief Gets the size required to store all the entries + * + * @return The total size + */ +size_t DirectoryResource::entries_size() const { + + size_t size = 0; + + // Files + for (const auto& file : directory->files()) { + size_t entry_size = sizeof(entry_information_t) + file->name().length() + 1; + size += entry_size; + } + + // Subdirectories + for (const auto& dir : directory->subdirectories()) { + size_t entry_size = sizeof(entry_information_t) + dir->name().length() + 1; + size += entry_size; + } + + return size; +} + +/** + * @brief Read from a directory resource + * + * @param buffer The buffer to read into + * @param size The number of bytes to read + * @param flags The flags to pass to the reading + * @return The number of bytes successfully read or -1 on error + */ +int DirectoryResource::read(void* buffer, size_t size, size_t flags) { + + // Directory not found + if(!directory) + return -1; + + switch ((DirectoryFlags)flags){ + case DirectoryFlags::READ_ENTRIES:{ + write_entries(buffer, size); + break; + } + + case DirectoryFlags::READ_ENTRIES_SIZE:{ + return entries_size(); + } + + default: + return -1; + } + + return size; + +} + +/** + * @brief Write to a directory resource + * + * @param buffer The buffer to write from + * @param size The number of bytes to write + * @param flags The flags to pass to the writing + * @return The number of bytes successfully written or -1 on error + */ +int DirectoryResource::write(void const* buffer, size_t size, size_t flags) { + + // Directory not found + if(!directory) + return -1; + + auto registry = GlobalResourceRegistry::get_registry(resource_type_t::FILESYSTEM); + + switch ((DirectoryFlags)flags){ + + case DirectoryFlags::WRITE_NAME : { + + // Open the parent + Resource* parent = registry->get_resource(Path::parent_directory(name())); + auto parent_directory = ((DirectoryResource*)parent)->directory; + + // Rename + string new_name = string((uint8_t*)buffer, size); + parent_directory->rename_subdirectory(directory, new_name); + break; + } + + case DirectoryFlags::WRITE_NEW_FILE:{ + + string new_name = string((uint8_t*)buffer, size); + directory->create_file(new_name); + break; + } + + + case DirectoryFlags::WRITE_NEW_DIR:{ + + string new_name = string((uint8_t*)buffer, size); + directory->create_subdirectory(new_name); + break; + } + + case DirectoryFlags::WRITE_REMOVE_FILE:{ + + string new_name = string((uint8_t*)buffer, size); + directory->remove_file(new_name); + break; + } + case DirectoryFlags::WRITE_REMOVE_DIR:{ + + string new_name = string((uint8_t*)buffer, size); + directory->remove_subdirectory(new_name); + break; + } + + default: + return -1; + } + + return size; +} + +VFSResourceRegistry::VFSResourceRegistry(VirtualFileSystem* vfs) +: BaseResourceRegistry(resource_type_t::FILESYSTEM), + m_vfs(vfs) +{ + +} + +VFSResourceRegistry::~VFSResourceRegistry() = default; + +/** + * @brief Open a directory as a resource + * + * @param name The name to call the resource + * @param directory The directory to open + * @return The new resource or nullptr if it failed to open + */ +Resource* VFSResourceRegistry::open_as_resource(const string& name, Directory* directory) { + + // Doesnt exist + if(!directory) + return nullptr; + + // Create the resource + auto resource = new DirectoryResource(name, 0, resource_type_t::FILESYSTEM); + resource->directory = directory; + + register_resource(resource); + return resource; +} + +/** + * @brief Open a file as a resource + * + * @param name The name to call the resource + * @param file The file to open + * @return The new resource or nullptr if it failed to open + */ +Resource* VFSResourceRegistry::open_as_resource(string const& name, File* file) { + + // Doesnt exist + if(!file) + return nullptr; + + // Create the resource + auto resource = new FileResource(name, 0, resource_type_t::FILESYSTEM); + resource->file = file; + + register_resource(resource); + return resource; +} + + +Resource* VFSResourceRegistry::get_resource(string const& name) { + + string path = Path::absolute_path(name); + path = Path::join_path(Scheduler::current_process()->working_directory, path); + + // Resource already opened + auto resource = BaseResourceRegistry::get_resource(path); + if(resource != nullptr) + return resource; + + // Open the resource + bool is_file = Path::is_file(path); + if(is_file) + return open_as_resource(path, m_vfs->open_file(path)); + else + return open_as_resource(path, m_vfs->open_directory(path)); +} + +Resource* VFSResourceRegistry::create_resource(string const& name, size_t flags) { + + string path = Path::absolute_path(name); + path = Path::join_path(Scheduler::current_process()->working_directory, path); + + // Resource already opened + auto resource = BaseResourceRegistry::get_resource(path); + if(resource != nullptr) + return resource; + + // Open the resource + bool is_file = Path::is_file(path); + if(is_file) + return open_as_resource(path, m_vfs->create_file(path)); + else + return open_as_resource(path, m_vfs->create_directory(path)); + +} \ No newline at end of file diff --git a/kernel/src/gui/desktop.cpp b/kernel/src/gui/desktop.cpp index 0eef46c2..f98e1b91 100644 --- a/kernel/src/gui/desktop.cpp +++ b/kernel/src/gui/desktop.cpp @@ -14,23 +14,23 @@ using namespace MaxOS::drivers::peripherals; * * @param gc The graphics context to use */ -Desktop::Desktop(GraphicsContext *gc) -: CompositeWidget(0,0, gc->width(), gc->height()), +Desktop::Desktop(GraphicsContext* gc) +: CompositeWidget(0, 0, gc->width(), gc->height()), MouseEventHandler(), ClockEventHandler(), m_graphics_context(gc), colour(Colour(0xA8, 0xA8, 0xA8)) { - // Set the mouse m_position to the center of the screen - m_mouse_x = gc->width() / 2; - m_mouse_y = gc->height() / 2; + // Set the mouse m_position to the center of the screen + m_mouse_x = gc->width() / 2; + m_mouse_y = gc->height() / 2; - // Draw the initial mouse cursor - invert_mouse_cursor(); + // Draw the initial mouse cursor + invert_mouse_cursor(); - // Draw the desktop - Widget::invalidate(); + // Draw the desktop + Widget::invalidate(); } Desktop::~Desktop() = default; @@ -42,14 +42,13 @@ Desktop::~Desktop() = default; */ void Desktop::set_focus(Widget* widget) { - // If there is a widget in focus then send a focus lost event to it - if(this ->m_focussed_widget != nullptr) - this->m_focussed_widget->on_focus_lost(); - - // Focus the new widget and send a focus event to it - this ->m_focussed_widget = widget; - this->m_focussed_widget->on_focus(); + // If there is a widget in focus then send a focus lost event to it + if (this->m_focussed_widget != nullptr) + this->m_focussed_widget->on_focus_lost(); + // Focus the new widget and send a focus event to it + this->m_focussed_widget = widget; + this->m_focussed_widget->on_focus(); } /** @@ -57,13 +56,13 @@ void Desktop::set_focus(Widget* widget) { * * @param front_widget The widget to bring to the front */ -void Desktop::bring_to_front(Widget*front_widget) { +void Desktop::bring_to_front(Widget* front_widget) { - // Remove the widget from where ever it already is - m_children.erase(front_widget); + // Remove the widget from where ever it already is + m_children.erase(front_widget); - // Add it back in the front - m_children.push_front(front_widget); + // Add it back in the front + m_children.push_front(front_widget); } /** @@ -71,17 +70,17 @@ void Desktop::bring_to_front(Widget*front_widget) { */ void Desktop::invert_mouse_cursor() { - //TODO: Get image drawing going and draw a proper mouse + //TODO: Get image drawing going and draw a proper mouse - // Draw the horizontal line - for (int32_t x = m_mouse_x - 3; x <= m_mouse_x + 3; ++x) { - m_graphics_context->invert_pixel(x, m_mouse_y); - } + // Draw the horizontal line + for (int32_t x = m_mouse_x - 3; x <= m_mouse_x + 3; ++x) { + m_graphics_context->invert_pixel(x, m_mouse_y); + } - // Draw the vertical line - for (int32_t y = m_mouse_y - 3; y <= m_mouse_y + 3; ++y) { - m_graphics_context->invert_pixel(m_mouse_x, y); - } + // Draw the vertical line + for (int32_t y = m_mouse_y - 3; y <= m_mouse_y + 3; ++y) { + m_graphics_context->invert_pixel(m_mouse_x, y); + } } /** @@ -91,38 +90,39 @@ void Desktop::invert_mouse_cursor() { * @param start The start of the invalid areas * @param stop The end of the invalid areas */ -void Desktop::internal_invalidate(common::Rectangle&area, Vector>::iterator start, Vector>::iterator stop) { +void Desktop::internal_invalidate(common::Rectangle& area, Vector>::iterator start, + Vector>::iterator stop) { - // Loop through the invalid rectangles - for(Vector>::iterator invaild_rect = start; invaild_rect != stop; invaild_rect++){ + // Loop through the invalid rectangles + for (Vector>::iterator invaild_rect = start; invaild_rect != stop; invaild_rect++) { - // Check if the area intersects with the invalid rectangle - if(!area.intersects(*invaild_rect)) - continue; + // Check if the area intersects with the invalid rectangle + if (!area.intersects(*invaild_rect)) + continue; - // Get the parts of the area that are covered by the invalid rectangle - Vector> coveredAreas = area.subtract(*invaild_rect); + // Get the parts of the area that are covered by the invalid rectangle + Vector> coveredAreas = area.subtract(*invaild_rect); - // Invalidate the covered areas - for(auto& coveredArea : coveredAreas) - internal_invalidate(coveredArea, invaild_rect + 1, stop); + // Invalidate the covered areas + for (auto& coveredArea: coveredAreas) + internal_invalidate(coveredArea, invaild_rect + 1, stop); - // The entire area will be invalidated by now - return; + // The entire area will be invalidated by now + return; - } + } - // Add the area to the invalid areas, store where it was added - Vector>::iterator vectorPosition = m_invalid_areas.push_back(area); + // Add the area to the invalid areas, store where it was added + Vector>::iterator vectorPosition = m_invalid_areas.push_back(area); - // If the m_position is the last item then the invalidation buffer is full - if(vectorPosition == m_invalid_areas.end()){ + // If the m_position is the last item then the invalidation buffer is full + if (vectorPosition == m_invalid_areas.end()) { - // Invalidate the entire desktop - m_invalid_areas.clear(); - Widget::invalidate(); + // Invalidate the entire desktop + m_invalid_areas.clear(); + Widget::invalidate(); - } + } } /** @@ -131,19 +131,18 @@ void Desktop::internal_invalidate(common::Rectangle&area, Vector &area) { - - //TODO: Draw a background image instead +void Desktop::draw_self(common::GraphicsContext* gc, common::Rectangle& area) { - // Calculate the rectangle - int32_t topCornerX = area.left; - int32_t topCornerY = area.top; - int32_t bottomCornerX = area.left + area.width; - int32_t bottomCornerY = area.top + area.height; + //TODO: Draw a background image instead - // Draw the background, a rectangle the size of the desktop of the given colour - gc->fill_rectangle(topCornerX, topCornerY, bottomCornerX, bottomCornerY, colour); + // Calculate the rectangle + int32_t topCornerX = area.left; + int32_t topCornerY = area.top; + int32_t bottomCornerX = area.left + area.width; + int32_t bottomCornerY = area.top + area.height; + // Draw the background, a rectangle the size of the desktop of the given colour + gc->fill_rectangle(topCornerX, topCornerY, bottomCornerX, bottomCornerY, colour); } /** @@ -151,21 +150,21 @@ void Desktop::draw_self(common::GraphicsContext *gc, common::Rectangle * * @param widget The widget to add */ -void Desktop::add_child(Widget*child_widget) { +void Desktop::add_child(Widget* child_widget) { - // Check if the new widget is under the mouse - bool underMouse = child_widget->contains_coordinate(m_mouse_x, m_mouse_y); + // Check if the new widget is under the mouse + bool underMouse = child_widget->contains_coordinate(m_mouse_x, m_mouse_y); - // If the mouse is over the widget then send a mouse leave event to the child widget as it is no longer under the mouse - if(underMouse) - CompositeWidget::on_mouse_leave_widget(m_mouse_x, m_mouse_y); + // If the mouse is over the widget then send a mouse leave event to the child widget as it is no longer under the mouse + if (underMouse) + CompositeWidget::on_mouse_leave_widget(m_mouse_x, m_mouse_y); - // Add the widget to the desktop - CompositeWidget::add_child(child_widget); + // Add the widget to the desktop + CompositeWidget::add_child(child_widget); - // If the mouse is over the new widget then send a mouse enter event to the child widget - if(underMouse) - CompositeWidget::on_mouse_enter_widget(m_mouse_x, m_mouse_y); + // If the mouse is over the new widget then send a mouse enter event to the child widget + if (underMouse) + CompositeWidget::on_mouse_enter_widget(m_mouse_x, m_mouse_y); } /** @@ -173,27 +172,27 @@ void Desktop::add_child(Widget*child_widget) { * * @param time The time when the event occurred */ -void Desktop::on_time(common::Time const &) { +void Desktop::on_time(common::Time const&) { - // Check if anything is invalid and needs to be redrawn - if(m_invalid_areas.empty()) - return; + // Check if anything is invalid and needs to be redrawn + if (m_invalid_areas.empty()) + return; - // Erase the mouse cursor - invert_mouse_cursor(); + // Erase the mouse cursor + invert_mouse_cursor(); - // Loop through the invalid areas - while (!m_invalid_areas.empty()) { + // Loop through the invalid areas + while (!m_invalid_areas.empty()) { - // Redraw the m_first_memory_chunk area - Rectangle invalidArea = *(m_invalid_areas.begin()); - m_invalid_areas.pop_front(); - draw(m_graphics_context, invalidArea); + // Redraw the m_first_memory_chunk area + Rectangle invalidArea = *(m_invalid_areas.begin()); + m_invalid_areas.pop_front(); + draw(m_graphics_context, invalidArea); - } + } - // Can now draw the mouse cursor - invert_mouse_cursor(); + // Can now draw the mouse cursor + invert_mouse_cursor(); } @@ -202,10 +201,10 @@ void Desktop::on_time(common::Time const &) { * * @param area The area that is now invalid */ -void Desktop::invalidate(Rectangle &area) { +void Desktop::invalidate(Rectangle& area) { - // Invalidate the area - internal_invalidate(area, m_invalid_areas.begin(), m_invalid_areas.end()); + // Invalidate the area + internal_invalidate(area, m_invalid_areas.begin(), m_invalid_areas.end()); } @@ -218,34 +217,34 @@ void Desktop::invalidate(Rectangle &area) { */ void Desktop::on_mouse_move_event(int8_t x, int8_t y) { - // Calculate the m_position of the mouse on the desktop - Rectangle desktopPosition = position(); - int32_t newMouseX = m_mouse_x + x; - int32_t newMouseY = m_mouse_y + y; + // Calculate the m_position of the mouse on the desktop + Rectangle desktopPosition = position(); + int32_t newMouseX = m_mouse_x + x; + int32_t newMouseY = m_mouse_y + y; - // Restrain the mouse to the desktop - if(newMouseX < 0) newMouseX = 0; - if(newMouseY < 0) newMouseY = 0; - if(newMouseX > desktopPosition.width) newMouseX = desktopPosition.width - 1; - if(newMouseY > desktopPosition.height) newMouseY = desktopPosition.height - 1; + // Restrain the mouse to the desktop + if (newMouseX < 0) newMouseX = 0; + if (newMouseY < 0) newMouseY = 0; + if (newMouseX > desktopPosition.width) newMouseX = desktopPosition.width - 1; + if (newMouseY > desktopPosition.height) newMouseY = desktopPosition.height - 1; - // Remove the old cursor from the screen as it will be redrawn in the new m_position - invert_mouse_cursor(); + // Remove the old cursor from the screen as it will be redrawn in the new m_position + invert_mouse_cursor(); - // If a widget is being dragged then pass the event to it - if(m_dragged_widget != nullptr) - m_dragged_widget->on_mouse_move_event(newMouseX - m_mouse_x, newMouseY - m_mouse_y); + // If a widget is being dragged then pass the event to it + if (m_dragged_widget != nullptr) + m_dragged_widget->on_mouse_move_event(newMouseX - m_mouse_x, newMouseY - m_mouse_y); - // Handle the mouse moving event (pass it to the widget that the mouse is over) - CompositeWidget::on_mouse_move_widget(m_mouse_x, m_mouse_y, newMouseX, - newMouseY); + // Handle the mouse moving event (pass it to the widget that the mouse is over) + CompositeWidget::on_mouse_move_widget(m_mouse_x, m_mouse_y, newMouseX, + newMouseY); - // Update the mouse m_position - m_mouse_x = newMouseX; - m_mouse_y = newMouseY; + // Update the mouse m_position + m_mouse_x = newMouseX; + m_mouse_y = newMouseY; - // Draw the new cursor - invert_mouse_cursor(); + // Draw the new cursor + invert_mouse_cursor(); } /** @@ -255,9 +254,8 @@ void Desktop::on_mouse_move_event(int8_t x, int8_t y) { */ void Desktop::on_mouse_down_event(uint8_t button) { - // The widget that handled the event becomes the widget being dragged - m_dragged_widget = CompositeWidget::on_mouse_button_pressed(m_mouse_x, m_mouse_y, button); - + // The widget that handled the event becomes the widget being dragged + m_dragged_widget = CompositeWidget::on_mouse_button_pressed(m_mouse_x, m_mouse_y, button); } /** @@ -267,12 +265,11 @@ void Desktop::on_mouse_down_event(uint8_t button) { */ void Desktop::on_mouse_up_event(uint8_t button) { - // Pass the event to the widget - CompositeWidget::on_mouse_button_released(m_mouse_x, m_mouse_y, button); - - // Dragging has stopped - m_dragged_widget = nullptr; + // Pass the event to the widget + CompositeWidget::on_mouse_button_released(m_mouse_x, m_mouse_y, button); + // Dragging has stopped + m_dragged_widget = nullptr; } /** @@ -282,9 +279,9 @@ void Desktop::on_mouse_up_event(uint8_t button) { */ void Desktop::on_key_down(KeyCode keyDownCode, KeyboardState keyDownState) { - // Pass the event to the widget that is in focus - if (m_focussed_widget != nullptr) - m_focussed_widget->on_key_down(keyDownCode, keyDownState); + // Pass the event to the widget that is in focus + if (m_focussed_widget != nullptr) + m_focussed_widget->on_key_down(keyDownCode, keyDownState); } /** @@ -294,7 +291,7 @@ void Desktop::on_key_down(KeyCode keyDownCode, KeyboardState keyDownState) { */ void Desktop::on_key_up(KeyCode keyUpCode, KeyboardState keyUpState) { - // Pass the event to the widget that is in focus - if (m_focussed_widget != nullptr) - m_focussed_widget->on_key_up(keyUpCode, keyUpState); + // Pass the event to the widget that is in focus + if (m_focussed_widget != nullptr) + m_focussed_widget->on_key_up(keyUpCode, keyUpState); } \ No newline at end of file diff --git a/kernel/src/gui/font.cpp b/kernel/src/gui/font.cpp index 2ad54aeb..a9d56b74 100644 --- a/kernel/src/gui/font.cpp +++ b/kernel/src/gui/font.cpp @@ -8,13 +8,12 @@ using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::gui; -Font::Font(const uint8_t* font_data) -{ +Font::Font(const uint8_t* font_data) { - // Store the font data - for (int i = 0; i < 2048; ++i) { - m_font8x8[i] = font_data[i]; - } + // Store the font data + for (int i = 0; i < 2048; ++i) { + m_font8x8[i] = font_data[i]; + } } Font::~Font() = default; @@ -27,20 +26,20 @@ Font::~Font() = default; * @param text The text to draw */ void Font::draw_text(int32_t x, int32_t y, common::Colour foreground_colour, - common::Colour background_colour, - common::GraphicsContext *context, string text) { + common::Colour background_colour, + common::GraphicsContext* context, string text) { - // Calculate the rectangle of the text - int32_t top = 0; - int32_t left = 0; - int32_t width = get_text_width(text); - int32_t height = get_text_height(text); + // Calculate the rectangle of the text + int32_t top = 0; + int32_t left = 0; + int32_t width = get_text_width(text); + int32_t height = get_text_height(text); - // Create the rectangle - Rectangle text_area(left, top, width, height); + // Create the rectangle + Rectangle text_area(left, top, width, height); - // Draw the text - draw_text(x, y, foreground_colour, background_colour, context, text, text_area); + // Draw the text + draw_text(x, y, foreground_colour, background_colour, context, text, text_area); } @@ -54,70 +53,69 @@ void Font::draw_text(int32_t x, int32_t y, common::Colour foreground_colour, * @param limitArea The area of the text to draw */ void Font::draw_text(int32_t x, int32_t y, common::Colour foreground_colour, - common::Colour background_colour, - common::GraphicsContext *context, string text, - common::Rectangle limitArea) -{ - // Convert the colours - uint32_t foreground = context->colour_to_int(foreground_colour); - uint32_t background = context->colour_to_int(background_colour); + common::Colour background_colour, + common::GraphicsContext* context, string text, + common::Rectangle limitArea) { - // Ensure the area is within the actual area of the text - if (limitArea.top < 0) { - limitArea.height += limitArea.top; - limitArea.top = 0; - } + // Convert the colours + uint32_t foreground = context->colour_to_int(foreground_colour); + uint32_t background = context->colour_to_int(background_colour); - if (limitArea.left < 0) { - limitArea.width += limitArea.left; - limitArea.left = 0; - } + // Ensure the area is within the actual area of the text + if (limitArea.top < 0) { + limitArea.height += limitArea.top; + limitArea.top = 0; + } - // Clamp the height and width max - if (limitArea.top + limitArea.height > (int)get_text_height(text)) - limitArea.height = get_text_height(text) - limitArea.top; + if (limitArea.left < 0) { + limitArea.width += limitArea.left; + limitArea.left = 0; + } - if (limitArea.left + limitArea.width > (int)get_text_width(text)) - limitArea.width = get_text_width(text) - limitArea.left; + // Clamp the height and width max + if (limitArea.top + limitArea.height > (int) get_text_height(text)) + limitArea.height = get_text_height(text) - limitArea.top; + if (limitArea.left + limitArea.width > (int) get_text_width(text)) + limitArea.width = get_text_width(text) - limitArea.left; - // Calculate limits - int32_t xLimit = limitArea.left + limitArea.width; - int32_t yLimit = limitArea.top + limitArea.height; + // Calculate limits + int32_t xLimit = limitArea.left + limitArea.width; + int32_t yLimit = limitArea.top + limitArea.height; - // Draw the text from top to bottom - for (int yBitMapOffset = limitArea.top; yBitMapOffset putPixel(x + xBitMapOffset, y + yBitMapOffset, foreground); - continue; - } + // Draw the pixel + context->putPixel(x + xBitMapOffset, y + yBitMapOffset, foreground); + continue; + } - // If the y is the bottom then add an underline - if (is_underlined && yBitMapOffset == yLimit - 1) { + // If the y is the bottom then add an underline + if (is_underlined && yBitMapOffset == yLimit - 1) { - // Draw the pixel - context -> putPixel(x + xBitMapOffset, y + yBitMapOffset, foreground); - continue; - } + // Draw the pixel + context->putPixel(x + xBitMapOffset, y + yBitMapOffset, foreground); + continue; + } - //TODO: Bold, Italic when other fonts are working + //TODO: Bold, Italic when other fonts are working - // Get the character - uint8_t character = text[xBitMapOffset/8]; + // Get the character + uint8_t character = text[xBitMapOffset / 8]; - // Check if this pixel is set or not - bool set = m_font8x8[(uint16_t)character * 8 + yBitMapOffset] & (128 >> (xBitMapOffset % 8)); + // Check if this pixel is set or not + bool set = m_font8x8[(uint16_t) character * 8 + yBitMapOffset] & (128 >> (xBitMapOffset % 8)); - // Draw the pixel - context -> putPixel(x + xBitMapOffset, y + yBitMapOffset, set ? foreground : background); + // Draw the pixel + context->putPixel(x + xBitMapOffset, y + yBitMapOffset, set ? foreground : background); - } - } + } + } } /** @@ -128,7 +126,7 @@ void Font::draw_text(int32_t x, int32_t y, common::Colour foreground_colour, */ int32_t Font::get_text_height(string) { - return 8; + return 8; } @@ -139,5 +137,6 @@ int32_t Font::get_text_height(string) { * @return The width of the text */ int32_t Font::get_text_width(string text) { - return text.length()*8; + + return text.length() * 8; } diff --git a/kernel/src/gui/widget.cpp b/kernel/src/gui/widget.cpp index f95d2bef..5c696216 100644 --- a/kernel/src/gui/widget.cpp +++ b/kernel/src/gui/widget.cpp @@ -8,16 +8,12 @@ using namespace MaxOS::gui; using namespace MaxOS::drivers; using namespace MaxOS::drivers::peripherals; -///__DEFAULT WIDGET__ - Widget::Widget() : KeyboardEventHandler() { } - - Widget::Widget(int32_t left, int32_t top, uint32_t width, uint32_t height) : KeyboardEventHandler(), m_position(left, top, width, height) @@ -43,14 +39,14 @@ void Widget::draw(GraphicsContext*, Rectangle&) { */ void Widget::invalidate() { - // Convert the relative coordinates to absolute coordinates - Coordinates coordinates = absolute_coordinates(Coordinates(0, 0)); + // Convert the relative coordinates to absolute coordinates + Coordinates coordinates = absolute_coordinates(Coordinates(0, 0)); - // Create a rectangle with the absolute coordinates and the size of the widget - Rectangle invalidArea = Rectangle(coordinates.first, coordinates.second, m_position.width, m_position.height); + // Create a rectangle with the absolute coordinates and the size of the widget + Rectangle invalidArea = Rectangle(coordinates.first, coordinates.second, m_position.width, m_position.height); - // Invalidate the area - invalidate(invalidArea); + // Invalidate the area + invalidate(invalidArea); } @@ -59,11 +55,11 @@ void Widget::invalidate() { * * @param area The area of the widget to invalidate */ -void Widget::invalidate(Rectangle &area) { +void Widget::invalidate(Rectangle& area) { - // If the widget has a parent, invalidate the area of the parent - if(m_parent != nullptr) - m_parent->invalidate(area); + // If the widget has a parent, invalidate the area of the parent + if (m_parent != nullptr) + m_parent->invalidate(area); } /** @@ -71,21 +67,21 @@ void Widget::invalidate(Rectangle &area) { * * @param child The child to add */ -void Widget::add_child(Widget *child) { +void Widget::add_child(Widget* child) { - // Parent the child to this widget - child ->m_parent = this; + // Parent the child to this widget + child->m_parent = this; } Coordinates Widget::absolute_coordinates(common::Coordinates coordinates) { - // Return the parents absolute coordinates - if(m_parent != nullptr) - return m_parent->absolute_coordinates(Coordinates(coordinates.first + m_position.left, coordinates.second + m_position.top)); + // Return the parents absolute coordinates + if (m_parent != nullptr) + return m_parent->absolute_coordinates( Coordinates(coordinates.first + m_position.left, coordinates.second + m_position.top)); - // If the widget has no m_parent, return the coordinates of the widget - return {coordinates.first + m_position.left, coordinates.second + m_position.top}; + // If the widget has no m_parent, return the coordinates of the widget + return {coordinates.first + m_position.left, coordinates.second + m_position.top}; } @@ -98,8 +94,8 @@ Coordinates Widget::absolute_coordinates(common::Coordinates coordinates) { */ bool Widget::contains_coordinate(uint32_t x, uint32_t y) { - // Check if the coordinates are within the bounds of the widget - return m_position.contains(x,y); + // Check if the coordinates are within the bounds of the widget + return m_position.contains(x, y); } /** @@ -108,7 +104,8 @@ bool Widget::contains_coordinate(uint32_t x, uint32_t y) { * @return The position of the widget */ Rectangle Widget::position() { - return m_position; + + return m_position; } /** @@ -119,15 +116,15 @@ Rectangle Widget::position() { */ void Widget::move(int32_t left, int32_t top) { - // Invalidate the old position - invalidate(); + // Invalidate the old position + invalidate(); - // Set the new position - m_position.left = left; - m_position.top = top; + // Set the new position + m_position.left = left; + m_position.top = top; - // Re draw the widget in the new position - invalidate(); + // Re draw the widget in the new position + invalidate(); } /** @@ -138,33 +135,34 @@ void Widget::move(int32_t left, int32_t top) { */ void Widget::resize(int32_t width, int32_t height) { - // Restrict the width and height to the minimum and maximum values - if(width < (int)m_min_width) width = m_min_width; - if(height < (int)m_min_height) height = m_min_height; - if(width > (int)m_max_width) width = m_max_width; - if(height > (int)m_max_height) height = m_max_height; + // Restrict the width and height to the minimum and maximum values + if (width < (int) m_min_width) width = m_min_width; + if (height < (int) m_min_height) height = m_min_height; + if (width > (int) m_max_width) width = m_max_width; + if (height > (int) m_max_height) height = m_max_height; - // Store the old position, set the new position - Rectangle old_position = m_position; - m_position.width = width; - m_position.height = height; + // Store the old position, set the new position + Rectangle old_position = m_position; + m_position.width = width; + m_position.height = height; - // Find the areas that need to be redrawn by subtracting the old position from the new position, and vice versa - Vector> invalid_areas_old = old_position.subtract(m_position); - Vector> invalid_areas_new = m_position.subtract(old_position); + // Find the areas that need to be redrawn by subtracting the old position from the new position, and vice versa + Vector> invalid_areas_old = old_position.subtract(m_position); + Vector> invalid_areas_new = m_position.subtract(old_position); - // Right and Bottom require to be fully invalidated TODO: Fix this hack - if(m_position.width > old_position.width || m_position.height > old_position.height || old_position.width > m_position.width || old_position.height > m_position.height){ - invalidate(); - return; - } + // Right and Bottom require to be fully invalidated TODO: Fix this hack + if (m_position.width > old_position.width || m_position.height > old_position.height || + old_position.width > m_position.width || old_position.height > m_position.height) { + invalidate(); + return; + } - //Loop through the areas that need to be redrawn and invalidate them - for(auto& area : invalid_areas_old) - invalidate(area); + //Loop through the areas that need to be redrawn and invalidate them + for (auto& area: invalid_areas_old) + invalidate(area); - for(auto& area : invalid_areas_new) - invalidate(area); + for (auto& area: invalid_areas_new) + invalidate(area); } @@ -173,8 +171,8 @@ void Widget::resize(int32_t width, int32_t height) { */ void Widget::focus() { - // Set the focus the widget to this widget - set_focus(this); + // Set the focus the widget to this widget + set_focus(this); } /** @@ -182,11 +180,11 @@ void Widget::focus() { * * @param widget The widget to set as focussed */ -void Widget::set_focus(Widget *widget) { +void Widget::set_focus(Widget* widget) { - // Focus the parent to this widget - if(m_parent != nullptr) - m_parent->set_focus(widget); + // Focus the parent to this widget + if (m_parent != nullptr) + m_parent->set_focus(widget); } /** @@ -208,9 +206,8 @@ void Widget::on_focus_lost() { */ void Widget::bring_to_front() { - // Bring this widget to the front of the screen - bring_to_front(this); - + // Bring this widget to the front of the screen + bring_to_front(this); } /** @@ -218,11 +215,11 @@ void Widget::bring_to_front() { * * @param widget The widget to bring to the front */ -void Widget::bring_to_front(Widget *widget) { +void Widget::bring_to_front(Widget* widget) { - // Bring the parent to the front of the screen - if(m_parent != nullptr) - m_parent->bring_to_front(widget); + // Bring the parent to the front of the screen + if (m_parent != nullptr) + m_parent->bring_to_front(widget); } @@ -268,14 +265,14 @@ void Widget::on_mouse_move_widget(uint32_t, uint32_t, uint32_t, uint32_t) { */ peripherals::MouseEventHandler* Widget::on_mouse_button_pressed(uint32_t, uint32_t, uint8_t) { - // Bring the widget to the front of the screen - bring_to_front(); + // Bring the widget to the front of the screen + bring_to_front(); - // Focus the widget - focus(); + // Focus the widget + focus(); - // Return 0 as the event has been handled - return nullptr; + // Return 0 as the event has been handled + return nullptr; } /** @@ -289,13 +286,10 @@ void Widget::on_mouse_button_released(uint32_t, uint32_t, uint8_t) { } -///__COMPOSITE WIDGET__ - CompositeWidget::CompositeWidget() = default; CompositeWidget::CompositeWidget(int32_t left, int32_t top, uint32_t width, uint32_t height) -: Widget(left, top, width,height) -{ + : Widget(left, top, width, height) { } @@ -307,11 +301,10 @@ CompositeWidget::~CompositeWidget() = default; * @param gc The graphics context to draw to * @param area The area to draw */ -void CompositeWidget::draw(GraphicsContext *gc, Rectangle &area) { - - // Draw the widget with its m_children - draw(gc, area, m_children.begin()); +void CompositeWidget::draw(GraphicsContext* gc, Rectangle& area) { + // Draw the widget with its m_children + draw(gc, area, m_children.begin()); } /** @@ -321,44 +314,44 @@ void CompositeWidget::draw(GraphicsContext *gc, Rectangle &area) { * @param area The area to draw * @param start The child to start drawing from */ -void CompositeWidget::draw(GraphicsContext *gc, Rectangle &area, Vector::iterator start) { +void CompositeWidget::draw(GraphicsContext* gc, Rectangle& area, Vector::iterator start) { - // Draw the widget - Widget::draw(gc, area); + // Draw the widget + Widget::draw(gc, area); - // Get the area of the widget - Rectangle own_area = position(); + // Get the area of the widget + Rectangle own_area = position(); - //Note: has to use iterator as the start is not necessarily the m_first_memory_chunk child - for(Vector::iterator child_widget = start; child_widget != m_children.end(); child_widget++){ + //Note: has to use iterator as the start is not necessarily the m_first_memory_chunk child + for (Vector::iterator child_widget = start; child_widget != m_children.end(); child_widget++) { - Rectangle child_area = (*child_widget)->position(); + Rectangle child_area = (*child_widget)->position(); - // Check if the child is in the area that needs to be redrawn - if(area.intersects(child_area)){ + // Check if the child is in the area that needs to be redrawn + if (area.intersects(child_area)) { - // Get the area that needs to be redrawn - Rectangle rectangle = area.intersection(child_area); + // Get the area that needs to be redrawn + Rectangle rectangle = area.intersection(child_area); - // Translate the area so that it is relative to the child - rectangle.left -= child_area.left; - rectangle.top -= child_area.top; + // Translate the area so that it is relative to the child + rectangle.left -= child_area.left; + rectangle.top -= child_area.top; - // Draw the child - (*child_widget)->draw(gc, rectangle); + // Draw the child + (*child_widget)->draw(gc, rectangle); - // Draw what is left of the area that needs to be redrawn - Vector> rest_draw_area = area.subtract(child_area); - for(auto & rest_area_part : rest_draw_area) - draw(gc, rest_area_part, child_widget + 1); + // Draw what is left of the area that needs to be redrawn + Vector> rest_draw_area = area.subtract(child_area); + for (auto& rest_area_part: rest_draw_area) + draw(gc, rest_area_part, child_widget + 1); - // Return as the entire area has now been drawn - return; - } - } + // Return as the entire area has now been drawn + return; + } + } - // Now draw the widget itself - draw_self(gc, area); + // Now draw the widget itself + draw_self(gc, area); } /** @@ -376,11 +369,11 @@ void CompositeWidget::draw_self(common::GraphicsContext*, common::Rectangle child_area = child_widget->position(); - if(child_area.contains(toX, toY)){ + for (auto& child_widget: m_children) { - // Get the position of the mouse relative to the child - uint32_t child_x = toX - child_area.left; - uint32_t child_y = toY - child_area.top; + // Check if the mouse is in the child + Rectangle child_area = child_widget->position(); + if (child_area.contains(toX, toY)) { - // Call the child's on_mouse_enter_widget function - child_widget->on_mouse_enter_widget(child_x, child_y); + // Get the position of the mouse relative to the child + uint32_t child_x = toX - child_area.left; + uint32_t child_y = toY - child_area.top; - // Break as the event has been handled - break; - } - } + // Call the child's on_mouse_enter_widget function + child_widget->on_mouse_enter_widget(child_x, child_y); + // Break as the event has been handled + break; + } + } } + /** * @brief Passes the event to the child that the mouse is over. (Event handling should be done by the derived class) * @@ -418,23 +411,23 @@ void CompositeWidget::on_mouse_enter_widget(uint32_t toX, uint32_t toY) { */ void CompositeWidget::on_mouse_leave_widget(uint32_t fromX, uint32_t fromY) { - for(auto&child_widget : m_children){ + for (auto& child_widget: m_children) { - // Check if the mouse is in the child - Rectangle child_area = child_widget->position(); - if(child_area.contains(fromX, fromY)){ + // Check if the mouse is in the child + Rectangle child_area = child_widget->position(); + if (child_area.contains(fromX, fromY)) { - // Get the position of the mouse relative to the child - uint32_t child_x = fromX - child_area.left; - uint32_t child_y = fromY - child_area.top; + // Get the position of the mouse relative to the child + uint32_t child_x = fromX - child_area.left; + uint32_t child_y = fromY - child_area.top; - // Call the child's on_mouse_leave_widget function - child_widget->on_mouse_leave_widget(child_x, child_y); + // Call the child's on_mouse_leave_widget function + child_widget->on_mouse_leave_widget(child_x, child_y); - // Event has been handled - break; - } - } + // Event has been handled + break; + } + } } /** @@ -447,42 +440,42 @@ void CompositeWidget::on_mouse_leave_widget(uint32_t fromX, uint32_t fromY) { */ void CompositeWidget::on_mouse_move_widget(uint32_t fromX, uint32_t fromY, uint32_t toX, uint32_t toY) { - Widget* left_child = nullptr; - Widget* entered_child = nullptr; + Widget* left_child = nullptr; + Widget* entered_child = nullptr; - for(auto&child_widget : m_children){ + for (auto& child_widget: m_children) { - // Check if the mouse is in the child - Rectangle child_area = child_widget->position(); - bool mouse_in_from = child_area.contains(fromX, fromY); - bool mouse_in_to = child_area.contains(toX, toY); + // Check if the mouse is in the child + Rectangle child_area = child_widget->position(); + bool mouse_in_from = child_area.contains(fromX, fromY); + bool mouse_in_to = child_area.contains(toX, toY); - // If the mouse started in the child - if(mouse_in_from){ + // If the mouse started in the child + if (mouse_in_from) { - // The mouse moved out of the child - if(!mouse_in_to){ - left_child = child_widget; - continue; - } + // The mouse moved out of the child + if (!mouse_in_to) { + left_child = child_widget; + continue; + } - // Mouse still in the child - child_widget->on_mouse_move_widget(fromX, fromY, toX, toY); + // Mouse still in the child + child_widget->on_mouse_move_widget(fromX, fromY, toX, toY); - }else{ + } else { - // Mouse moved into the child - if(mouse_in_to) - entered_child = child_widget; - } + // Mouse moved into the child + if (mouse_in_to) + entered_child = child_widget; + } - // Pass the events to the child - if(left_child != nullptr) - left_child->on_mouse_leave_widget(fromX, fromY); + // Pass the events to the child + if (left_child != nullptr) + left_child->on_mouse_leave_widget(fromX, fromY); - if(entered_child != nullptr) - entered_child->on_mouse_enter_widget(toX, toY); - } + if (entered_child != nullptr) + entered_child->on_mouse_enter_widget(toX, toY); + } } /** @@ -493,22 +486,21 @@ void CompositeWidget::on_mouse_move_widget(uint32_t fromX, uint32_t fromY, uint3 * @param button The button that was pressed * @return The object that has the mouseEventHandler which handled the event */ -peripherals::MouseEventHandler *CompositeWidget::on_mouse_button_pressed(uint32_t x, uint32_t y, uint8_t button) { - - MouseEventHandler*mouse_event_handler = nullptr; - - for(auto&child_widget : m_children){ +peripherals::MouseEventHandler* CompositeWidget::on_mouse_button_pressed(uint32_t x, uint32_t y, uint8_t button) { - // Pass the event to the child - if(child_widget->contains_coordinate(x, y)){ - mouse_event_handler = child_widget -> on_mouse_button_pressed(x - child_widget->m_position.left, y - child_widget->m_position.top, button); - break; - } + MouseEventHandler* mouse_event_handler = nullptr; - } + for (auto& child_widget: m_children) { - return mouse_event_handler; + // Pass the event to the child + if (child_widget->contains_coordinate(x, y)) { + mouse_event_handler = child_widget->on_mouse_button_pressed(x - child_widget->m_position.left, + y - child_widget->m_position.top, button); + break; + } + } + return mouse_event_handler; } /** @@ -520,13 +512,14 @@ peripherals::MouseEventHandler *CompositeWidget::on_mouse_button_pressed(uint32_ */ void CompositeWidget::on_mouse_button_released(uint32_t x, uint32_t y, uint8_t button) { - // Loop through the m_children - for(auto&child_widget : m_children){ + // Loop through the m_children + for (auto& child_widget: m_children) { - // Pass the event to the child - if(child_widget->contains_coordinate(x, y)){ - child_widget->on_mouse_button_released(x - child_widget->m_position.left, y - child_widget->m_position.top, button); - break; - } - } + // Pass the event to the child + if (child_widget->contains_coordinate(x, y)) { + child_widget->on_mouse_button_released(x - child_widget->m_position.left, y - child_widget->m_position.top, + button); + break; + } + } } diff --git a/kernel/src/gui/widgets/button.cpp b/kernel/src/gui/widgets/button.cpp index e6d94f29..598f9ce2 100644 --- a/kernel/src/gui/widgets/button.cpp +++ b/kernel/src/gui/widgets/button.cpp @@ -11,8 +11,6 @@ using namespace MaxOS::gui::widgets; using namespace MaxOS::drivers; using namespace MaxOS::drivers::peripherals; -/// ___ Button Event Handler ___ - ButtonEventHandler::ButtonEventHandler() = default; ButtonEventHandler::~ButtonEventHandler() = default; @@ -22,23 +20,20 @@ ButtonEventHandler::~ButtonEventHandler() = default; * * @param event The event to handle */ -Event* ButtonEventHandler::on_event(Event *event) { +Event* ButtonEventHandler::on_event(Event* event) { - // Check the event type - switch (event -> type) { + switch (event->type) { - // Button pressed - case ButtonEvents::PRESSED: - on_button_pressed(((ButtonPressedEvent *)event)->source); - break; + case ButtonEvents::PRESSED: + on_button_pressed(((ButtonPressedEvent*) event)->source); + break; - // Button released - case ButtonEvents::RELEASED: - on_button_released(((ButtonReleasedEvent *)event)->source); - break; + case ButtonEvents::RELEASED: + on_button_released(((ButtonReleasedEvent*) event)->source); + break; - } - return event; + } + return event; } /** @@ -59,16 +54,12 @@ void ButtonEventHandler::on_button_released(Button*) { } - - -//// ___ Button ___ - Button::Button(int32_t left, int32_t top, uint32_t width, uint32_t height, const string& text) : Widget(left, top, width, height), background_colour(Colour(0xFF, 0xFF, 0xFF)), foreground_colour(Colour(0x00, 0x00, 0x00)), border_colour(Colour(0x57, 0x57, 0x57)), - font((uint8_t*)AMIGA_FONT), + font((uint8_t*) AMIGA_FONT), text(text) { @@ -82,63 +73,63 @@ Button::~Button() = default; * @param gc The graphics context to draw to * @param area The area to draw to */ -void Button::draw(GraphicsContext *gc, Rectangle &area) { +void Button::draw(GraphicsContext* gc, Rectangle& area) { - // Default Draw Operation - Widget::draw(gc, area); + // Default Draw Operation + Widget::draw(gc, area); - // Get the absolute m_position of the button - Coordinates buttonCoordinates = absolute_coordinates(Coordinates(0, 0)); - Rectangle buttonPosition = position(); + // Get the absolute m_position of the button + Coordinates buttonCoordinates = absolute_coordinates(Coordinates(0, 0)); + Rectangle buttonPosition = position(); - // Get the x and y m_position of the button - int32_t x = buttonCoordinates.first; - int32_t y = buttonCoordinates.second; + // Get the x and y m_position of the button + int32_t x = buttonCoordinates.first; + int32_t y = buttonCoordinates.second; - // Draw the background for the button - gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, - y + area.top + area.height, background_colour); + // Draw the background for the button + gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, + y + area.top + area.height, background_colour); - // Draw the border (TODO: Make a border class?? Window uses it too) + // Draw the border (TODO: Make a border class?? Window uses it too) - // Top Border - if(area.intersects(Rectangle(0,0,buttonPosition.width,1))){ + // Top Border + if (area.intersects(Rectangle(0, 0, buttonPosition.width, 1))) { - // Start in the top left corner of the button and end in the top right corner - gc->draw_line(x + area.left, y, x + area.left + area.width - 1, y, - border_colour); - } + // Start in the top left corner of the button and end in the top right corner + gc->draw_line(x + area.left, y, x + area.left + area.width - 1, y, + border_colour); + } - // Left Border - if(area.intersects(Rectangle(0,0,1,buttonPosition.height))){ + // Left Border + if (area.intersects(Rectangle(0, 0, 1, buttonPosition.height))) { - // Start in the top left corner and end in the bottom left corner - gc->draw_line(x, y + area.top, x, y + area.top + area.height - 1, - border_colour); - } + // Start in the top left corner and end in the bottom left corner + gc->draw_line(x, y + area.top, x, y + area.top + area.height - 1, + border_colour); + } - // Right Border - if(area.intersects(Rectangle(0,buttonPosition.height - 1,buttonPosition.width,1))){ + // Right Border + if (area.intersects(Rectangle(0, buttonPosition.height - 1, buttonPosition.width, 1))) { - // Start in the top right corner and end in the bottom right corner - gc->draw_line(x + area.left, y + buttonPosition.height - 1, - x + area.left + area.width - 1, - y + buttonPosition.height - 1, border_colour); - } + // Start in the top right corner and end in the bottom right corner + gc->draw_line(x + area.left, y + buttonPosition.height - 1, + x + area.left + area.width - 1, + y + buttonPosition.height - 1, border_colour); + } - // Bottom Border - if(area.intersects(Rectangle(buttonPosition.width - 1,0,1,buttonPosition.height))){ + // Bottom Border + if (area.intersects(Rectangle(buttonPosition.width - 1, 0, 1, buttonPosition.height))) { - // Start in the bottom left corner and end in the bottom right corner - gc->draw_line(x + buttonPosition.width - 1, y + area.top, - x + buttonPosition.width - 1, - y + area.top + area.height - 1, border_colour); - } + // Start in the bottom left corner and end in the bottom right corner + gc->draw_line(x + buttonPosition.width - 1, y + area.top, + x + buttonPosition.width - 1, + y + area.top + area.height - 1, border_colour); + } - // Draw the text - common::Rectangle textArea(area.left - 1, area.top - 1, area.width, area.height); - font.draw_text(x + 1, y + 1, foreground_colour, background_colour, gc, text, - textArea); + // Draw the text + common::Rectangle textArea(area.left - 1, area.top - 1, area.width, area.height); + font.draw_text(x + 1, y + 1, foreground_colour, background_colour, gc, text, + textArea); } @@ -152,15 +143,15 @@ void Button::draw(GraphicsContext *gc, Rectangle &area) { */ MouseEventHandler* Button::on_mouse_button_pressed(uint32_t x, uint32_t y, uint8_t button) { - // Raise the event - raise_event(new ButtonPressedEvent(this)); + // Raise the event + raise_event(new ButtonPressedEvent(this)); - // Change the button colour - background_colour = Colour(0x57, 0x57, 0x57); - Widget::invalidate(); + // Change the button colour + background_colour = Colour(0x57, 0x57, 0x57); + Widget::invalidate(); - // Pass the event on (that it was handled) - return Widget::on_mouse_button_pressed(x, y, button); + // Pass the event on (that it was handled) + return Widget::on_mouse_button_pressed(x, y, button); } /** @@ -172,20 +163,20 @@ MouseEventHandler* Button::on_mouse_button_pressed(uint32_t x, uint32_t y, uint8 */ void Button::on_mouse_button_released(uint32_t x, uint32_t y, uint8_t button) { - // Raise the button released event - raise_event(new ButtonReleasedEvent(this)); + // Raise the button released event + raise_event(new ButtonReleasedEvent(this)); - // Change the button colour - background_colour = Colour(0xFF, 0xFF, 0xFF); - Widget::invalidate(); + // Change the button colour + background_colour = Colour(0xFF, 0xFF, 0xFF); + Widget::invalidate(); - // Pass the event on (that it was handled) - Widget::on_mouse_button_released(x, y, button); + // Pass the event on (that it was handled) + Widget::on_mouse_button_released(x, y, button); } /// ___ Event ___ -ButtonReleasedEvent::ButtonReleasedEvent(Button *source) +ButtonReleasedEvent::ButtonReleasedEvent(Button* source) : Event(ButtonEvents::RELEASED), source(source) { @@ -194,7 +185,7 @@ ButtonReleasedEvent::ButtonReleasedEvent(Button *source) ButtonReleasedEvent::~ButtonReleasedEvent() = default; -ButtonPressedEvent::ButtonPressedEvent(Button *source) +ButtonPressedEvent::ButtonPressedEvent(Button* source) : Event(ButtonEvents::PRESSED), source(source) { diff --git a/kernel/src/gui/widgets/inputbox.cpp b/kernel/src/gui/widgets/inputbox.cpp index 8e0b6dbb..de2e4473 100644 --- a/kernel/src/gui/widgets/inputbox.cpp +++ b/kernel/src/gui/widgets/inputbox.cpp @@ -11,34 +11,40 @@ using namespace MaxOS::gui; using namespace MaxOS::gui::widgets; using namespace MaxOS::drivers::peripherals; -/// ___ Event Handlers ___ /// - InputBoxEventHandler::InputBoxEventHandler() = default; InputBoxEventHandler::~InputBoxEventHandler() = default; -Event* InputBoxEventHandler::on_event(Event *event) { - switch (event->type) { - case InputBoxEvents::TEXT_CHANGED: - on_input_box_text_changed(((InputBoxTextChangedEvent *)event)->new_text); - break; - } - - return event; +/** + * @brief Delegates an event to the event handler for that event type + * + * @param event The event triggered + * @return The event triggered potentially modified by the handler. + */ +Event* InputBoxEventHandler::on_event(Event* event) { + + switch (event->type) { + case InputBoxEvents::TEXT_CHANGED: + on_input_box_text_changed(((InputBoxTextChangedEvent*) event)->new_text); + break; + } + + return event; } +/** + * @brief Event triggered when the text in the input box is changed + */ void InputBoxEventHandler::on_input_box_text_changed(string) { } -/// ___ InputBox ___ /// - InputBox::InputBox(int32_t left, int32_t top, uint32_t width, uint32_t height) : Widget(left, top, width, height), background_colour(Colour(0xFF, 0xFF, 0xFF)), foreground_colour(Colour(0x00, 0x00, 0x00)), border_colour(Colour(0x57, 0x57, 0x57)), - font((uint8_t*)AMIGA_FONT) + font((uint8_t*) AMIGA_FONT) { } @@ -48,197 +54,217 @@ InputBox::InputBox(int32_t left, int32_t top, uint32_t width, uint32_t height, c background_colour(Colour(0xFF, 0xFF, 0xFF)), foreground_colour(Colour(0x00, 0x00, 0x00)), border_colour(Colour(0x57, 0x57, 0x57)), - font((uint8_t*)AMIGA_FONT) + font((uint8_t*) AMIGA_FONT) { - // Update the text - update_text(text); + // Update the text + update_text(text); } InputBox::~InputBox() = default; -void InputBox::draw(GraphicsContext *gc, Rectangle &area) { +/** + * @brief Draw an area of the input box onto a graphics context + * + * @param gc The context to draw onto + * @param area What part of the input box to draw + */ +void InputBox::draw(GraphicsContext* gc, Rectangle& area) { - // Default Draw - Widget::draw(gc, area); + // Default Draw + Widget::draw(gc, area); - // Get the absolute m_position of the input box - Coordinates inputBoxCoordinates = absolute_coordinates(Coordinates(0, 0)); - Rectangle inputBoxPosition = position(); + // Get the absolute m_position of the input box + Coordinates inputBoxCoordinates = absolute_coordinates(Coordinates(0, 0)); + Rectangle inputBoxPosition = position(); - // Get the x and y m_position of the input box - int32_t x = inputBoxCoordinates.first; - int32_t y = inputBoxCoordinates.second; + // Get the x and y m_position of the input box + int32_t x = inputBoxCoordinates.first; + int32_t y = inputBoxCoordinates.second; - // Draw the background for the input box - gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, - y + area.top + area.height, background_colour); + // Draw the background for the input box + gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, + y + area.top + area.height, background_colour); - // Draw the border (TODO: Make this a function because it is used in multiple places) + // Draw the border (TODO: Make this a function because it is used in multiple places) - // Top Border - if(area.intersects(Rectangle(0,0,inputBoxPosition.width,1))){ + // Top Border + if (area.intersects(Rectangle(0, 0, inputBoxPosition.width, 1))) { - // Start in the top left corner of the button and end in the top right corner - gc->draw_line(x + area.left, y, x + area.left + area.width - 1, y, - border_colour); - } + // Start in the top left corner of the button and end in the top right corner + gc->draw_line(x + area.left, y, x + area.left + area.width - 1, y, + border_colour); + } - // Left Border - if(area.intersects(Rectangle(0,0,1,inputBoxPosition.height))){ + // Left Border + if (area.intersects(Rectangle(0, 0, 1, inputBoxPosition.height))) { - // Start in the top left corner and end in the bottom left corner - gc->draw_line(x, y + area.top, x, y + area.top + area.height - 1, - border_colour); - } + // Start in the top left corner and end in the bottom left corner + gc->draw_line(x, y + area.top, x, y + area.top + area.height - 1, + border_colour); + } - // Right Border - if(area.intersects(Rectangle(0,inputBoxPosition.height - 1,inputBoxPosition.width,1))){ + // Right Border + if (area.intersects(Rectangle(0, inputBoxPosition.height - 1, inputBoxPosition.width, 1))) { - // Start in the top right corner and end in the bottom right corner - gc->draw_line(x + area.left, y + inputBoxPosition.height - 1, - x + area.left + area.width - 1, - y + inputBoxPosition.height - 1, border_colour); - } + // Start in the top right corner and end in the bottom right corner + gc->draw_line(x + area.left, y + inputBoxPosition.height - 1, + x + area.left + area.width - 1, + y + inputBoxPosition.height - 1, border_colour); + } - // Bottom Border - if(area.intersects(Rectangle(inputBoxPosition.width - 1,0,1,inputBoxPosition.height))){ + // Bottom Border + if (area.intersects(Rectangle(inputBoxPosition.width - 1, 0, 1, inputBoxPosition.height))) { - // Start in the bottom left corner and end in the bottom right corner - gc->draw_line(x + inputBoxPosition.width - 1, y + area.top, - x + inputBoxPosition.width - 1, - y + area.top + area.height - 1, border_colour); - } + // Start in the bottom left corner and end in the bottom right corner + gc->draw_line(x + inputBoxPosition.width - 1, y + area.top, + x + inputBoxPosition.width - 1, + y + area.top + area.height - 1, border_colour); + } - // Draw the text - common::Rectangle textArea(area.left - 1, area.top - 1, area.width, area.height); - font.draw_text(x + 1, y + 1, foreground_colour, background_colour, gc, m_widget_text, textArea); + // Draw the text + common::Rectangle textArea(area.left - 1, area.top - 1, area.width, area.height); + font.draw_text(x + 1, y + 1, foreground_colour, background_colour, gc, m_widget_text, textArea); } +/** + * @brief Update the border when the input box is focussed + */ void InputBox::on_focus() { - // Make the border black on focus - border_colour = Colour(0x00, 0x00, 0x00); - invalidate(); + // Make the border black on focus + border_colour = Colour(0x00, 0x00, 0x00); + invalidate(); } +/** + * @brief Reset to the original input box style when it is no longer focussed + */ void InputBox::on_focus_lost() { - // Reset the border colour - border_colour = Colour(0x57, 0x57, 0x57); - invalidate(); + // Reset the border colour + border_colour = Colour(0x57, 0x57, 0x57); + invalidate(); } +/** + * @brief Handles a keypress event by updating the rendered text in the input box + * + * @param keyDownCode The key being pressed + */ void InputBox::on_key_down(KeyCode keyDownCode, KeyboardState) { - // Handle the key press - switch(keyDownCode) - { - case KeyCode::backspace: - { - if(cursor_position == 0) - break; - - cursor_position--; - // no break - we move the cursor to the left and use the code - [[fallthrough]]; - } - case KeyCode::deleteKey: - { - // Move the text to the left - for (int i = cursor_position; i < m_widget_text.length(); ++i) - m_widget_text[i] = m_widget_text[i+1]; - - // Put a null character at the end of the string - m_widget_text[m_widget_text.length() - 1] = '\0'; - - break; - } - case KeyCode::leftArrow: - { - // If the cursor is not at the beginning of the text, move it to the left - if(cursor_position > 0) - cursor_position--; - break; - } - case KeyCode::rightArrow: - { - - // If the cursor is not at the end of the text, move it to the right - if(m_widget_text[cursor_position] != '\0') - cursor_position++; - break; - } - default: - { - - // If the key is a printable character, add it to the text - if(31 < (int)keyDownCode && (int)keyDownCode < 127) - { - uint32_t length = cursor_position; - - // Find the length of the text buffer - while (m_widget_text[length] != '\0') { - ++length; - } - - // Check if we need to make space for the new character - if (length >= (uint32_t)m_widget_text.length()) { - m_widget_text += " "; - } - - // Shift elements to the right - while (length > cursor_position) { - m_widget_text[length + 1] = m_widget_text[length]; - --length; - } - - // Insert the new character - m_widget_text[cursor_position + 1] = m_widget_text[cursor_position]; - m_widget_text[cursor_position] = (uint8_t)keyDownCode; - cursor_position++; - }else{ - - // Don't want to redraw the widget if nothing has changed - return; - } - break; - } - } - - // Redraw the widget - invalidate(); - - // Fire the text changed event - if(keyDownCode != KeyCode::leftArrow && keyDownCode != KeyCode::rightArrow) - raise_event(new InputBoxTextChangedEvent(m_widget_text)); + // Handle the key press + switch (keyDownCode) { + case KeyCode::backspace: { + if (cursor_position == 0) + break; + + cursor_position--; + // no break - we move the cursor to the left and use the code + [[fallthrough]]; + } + case KeyCode::deleteKey: { + // Move the text to the left + for (int i = cursor_position; i < m_widget_text.length(); ++i) + m_widget_text[i] = m_widget_text[i + 1]; + + // Put a null character at the end of the string + m_widget_text[m_widget_text.length() - 1] = '\0'; + + break; + } + case KeyCode::leftArrow: { + // If the cursor is not at the beginning of the text, move it to the left + if (cursor_position > 0) + cursor_position--; + break; + } + case KeyCode::rightArrow: { + + // If the cursor is not at the end of the text, move it to the right + if (m_widget_text[cursor_position] != '\0') + cursor_position++; + break; + } + default: { + + // If the key is a printable character, add it to the text + if (31 < (int) keyDownCode && (int) keyDownCode < 127) { + uint32_t length = cursor_position; + + // Find the length of the text buffer + while (m_widget_text[length] != '\0') { + ++length; + } + + // Check if we need to make space for the new character + if (length >= (uint32_t) m_widget_text.length()) { + m_widget_text += " "; + } + + // Shift elements to the right + while (length > cursor_position) { + m_widget_text[length + 1] = m_widget_text[length]; + --length; + } + + // Insert the new character + m_widget_text[cursor_position + 1] = m_widget_text[cursor_position]; + m_widget_text[cursor_position] = (uint8_t) keyDownCode; + cursor_position++; + } else { + + // Don't want to redraw the widget if nothing has changed + return; + } + break; + } + } + + // Redraw the widget + invalidate(); + + // Fire the text changed event + if (keyDownCode != KeyCode::leftArrow && keyDownCode != KeyCode::rightArrow) + raise_event(new InputBoxTextChangedEvent(m_widget_text)); } +/** + * @brief Update the text in the input box + * + * @param new_text The new text to display + */ void InputBox::update_text(const string& new_text) { - m_widget_text.copy(new_text); - cursor_position = m_widget_text.length(); + m_widget_text.copy(new_text); + cursor_position = m_widget_text.length(); - // Redraw the widget - invalidate(); - - // Fire the text changed event - raise_event(new InputBoxTextChangedEvent(new_text)); + // Redraw the widget + invalidate(); + // Fire the text changed event + raise_event(new InputBoxTextChangedEvent(new_text)); } +/** + * @brief Get the text currently in the input box + * + * @return The text in the input box + */ string InputBox::text() { - return m_widget_text; + + return m_widget_text; } -/// ___ Events ___ /// InputBoxTextChangedEvent::InputBoxTextChangedEvent(const string& new_text) : Event(InputBoxEvents::TEXT_CHANGED), new_text(new_text) { + } InputBoxTextChangedEvent::~InputBoxTextChangedEvent() = default; \ No newline at end of file diff --git a/kernel/src/gui/widgets/text.cpp b/kernel/src/gui/widgets/text.cpp index 7614d0a3..f090f198 100644 --- a/kernel/src/gui/widgets/text.cpp +++ b/kernel/src/gui/widgets/text.cpp @@ -12,12 +12,13 @@ using namespace MaxOS::gui::widgets; Text::Text(int32_t left, int32_t top, uint32_t width, uint32_t height, const string& text) : Widget(left, top, width, height), - font((uint8_t*)AMIGA_FONT), - foreground_colour(Colour(0,0,0)), - background_colour(Colour(255,255,255)) + font((uint8_t*) AMIGA_FONT), + foreground_colour(Colour(0, 0, 0)), + background_colour(Colour(255, 255, 255)) { - // Set the text - update_text(text); + + // Set the text + update_text(text); } Text::~Text() = default; @@ -28,25 +29,24 @@ Text::~Text() = default; * @param gc The graphics context to draw on * @param area The area of the text to draw */ -void Text::draw(GraphicsContext *gc, Rectangle& area) { - - // Default Draw Operation - Widget::draw(gc, area); +void Text::draw(GraphicsContext* gc, Rectangle& area) { - // Get the absolute m_position of the text - Coordinates textCoordinates = absolute_coordinates(Coordinates(0, 0)); - Rectangle textPosition = position(); + // Default Draw Operation + Widget::draw(gc, area); - // Get the x and y m_position of the text - int32_t x = textCoordinates.first; - int32_t y = textCoordinates.second; + // Get the absolute m_position of the text + Coordinates textCoordinates = absolute_coordinates(Coordinates(0, 0)); + Rectangle textPosition = position(); - // Draw the background (as the text might not fill the entire area) - gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, y + area.top + area.height, background_colour); + // Get the x and y m_position of the text + int32_t x = textCoordinates.first; + int32_t y = textCoordinates.second; - // Draw the text - this->font.draw_text(x, y, foreground_colour, background_colour, gc, m_widget_text, area); + // Draw the background (as the text might not fill the entire area) + gc->fill_rectangle(x + area.left, y + area.top, x + area.left + area.width, y + area.top + area.height, background_colour); + // Draw the text + this->font.draw_text(x, y, foreground_colour, background_colour, gc, m_widget_text, area); } /** @@ -55,12 +55,8 @@ void Text::draw(GraphicsContext *gc, Rectangle& area) { */ void Text::update_text(const string& new_text) { - - // Set the text - m_widget_text.copy(new_text); - - // New text has been set so invalidate the widget - invalidate(); + // Set the text + m_widget_text.copy(new_text); + invalidate(); } - diff --git a/kernel/src/gui/window.cpp b/kernel/src/gui/window.cpp index b0496e89..8e5581f6 100644 --- a/kernel/src/gui/window.cpp +++ b/kernel/src/gui/window.cpp @@ -24,25 +24,26 @@ Window::Window(int32_t left, int32_t top, uint32_t width, uint32_t height, const m_resizer_bottom_right(this) { - // Set the sizing - m_min_width = 2 * frame_thickness; - m_min_height = 2 * frame_thickness + title_bar_height; + // Set the sizing + m_min_width = 2 * frame_thickness; + m_min_height = 2 * frame_thickness + title_bar_height; - // Set the colours - area_colour = Colour(0xff, 0xff, 0xff); // White - frame_border_colour = Colour(0x00, 0x00, 0x00); // Black - frame_colour = Colour(0x57,0x57,0x57); // Davy's Grey - m_title.foreground_colour = Colour(0xff, 0xff, 0xff); // White - m_title.background_colour = frame_colour; + // Set the colours + area_colour = Colour(0xff, 0xff, 0xff); // White + frame_border_colour = Colour(0x00, 0x00, 0x00); // Black + frame_colour = Colour(0x57, 0x57, 0x57); // Davy's Grey + m_title.foreground_colour = Colour(0xff, 0xff, 0xff); // White + m_title.background_colour = frame_colour; - // Add the title to the window - Window::add_child(&m_title); + // Add the title to the window + Window::add_child(&m_title); } -Window::Window(Widget *containedWidget, const string& title_text) -: CompositeWidget(0, 0, containedWidget->position().width + 2 * 5 + 2, containedWidget->position().height + 2 * 5 + 10 + 2), - m_title(0, -(10 + 5) + 2, containedWidget->position().width, 10 + 5 - 3,title_text), +Window::Window(Widget* containedWidget, const string& title_text) +: CompositeWidget(0, 0, containedWidget->position().width + 2 * 5 + 2, + containedWidget->position().height + 2 * 5 + 10 + 2), + m_title(0, -(10 + 5) + 2, containedWidget->position().width, 10 + 5 - 3, title_text), m_mover(this), m_resizer_top(this), m_resizer_bottom(this), @@ -53,21 +54,21 @@ Window::Window(Widget *containedWidget, const string& title_text) m_resizer_bottom_left(this), m_resizer_bottom_right(this) { - // Set the sizing - m_min_width = 2 * frame_thickness; - m_min_height = 2 * frame_thickness + title_bar_height; - // Set the colours - area_colour = Colour(0xff, 0xff, 0xff); // White - frame_border_colour = Colour(0x00, 0x00, 0x00); // Black - frame_colour = Colour(0x57,0x57,0x57); // Davy's Grey - m_title.foreground_colour = Colour(0xff, 0xff, 0xff); // White - m_title.background_colour = frame_colour; + // Set the sizing + m_min_width = 2 * frame_thickness; + m_min_height = 2 * frame_thickness + title_bar_height; - // Add the m_title to the window - Window::add_child(&m_title); - Window::add_child(containedWidget); + // Set the colours + area_colour = Colour(0xff, 0xff, 0xff); // White + frame_border_colour = Colour(0x00, 0x00, 0x00); // Black + frame_colour = Colour(0x57, 0x57, 0x57); // Davy's Grey + m_title.foreground_colour = Colour(0xff, 0xff, 0xff); // White + m_title.background_colour = frame_colour; + // Add the title to the window + Window::add_child(&m_title); + Window::add_child(containedWidget); } Window::~Window() = default; @@ -79,55 +80,49 @@ Window::~Window() = default; * @param y The y coordinate of the mouse. * @param button The button that is pressed. */ -MouseEventHandler* Window::on_mouse_button_pressed(uint32_t mouseX, uint32_t mouseY, uint8_t button){ +MouseEventHandler* Window::on_mouse_button_pressed(uint32_t mouseX, uint32_t mouseY, uint8_t button) { - // Pass the mouse event to the children - drivers::peripherals::MouseEventHandler* child_result = CompositeWidget::on_mouse_button_pressed(mouseX, mouseY, button); - Rectangle window_position = position(); + // Pass the mouse event to the children + drivers::peripherals::MouseEventHandler* child_result = CompositeWidget::on_mouse_button_pressed(mouseX, mouseY, button); + Rectangle window_position = position(); - // Bring the window to the front - bring_to_front(); + // Bring the window to the front + bring_to_front(); - // Convert the mouse coordinates to an int32_t - auto x = (int32_t) mouseX; - auto y = (int32_t) mouseY; + // Convert the mouse coordinates to an int32_t + auto x = (int32_t) mouseX; + auto y = (int32_t) mouseY; - if(x <= frame_thickness) - { - if(y <= frame_thickness) - return &m_resizer_top_left; + if (x <= frame_thickness) { + if (y <= frame_thickness) + return &m_resizer_top_left; - else if(y < window_position.height - frame_thickness) - return &m_resizer_left; + else if (y < window_position.height - frame_thickness) + return &m_resizer_left; - else - return &m_resizer_bottom_left; - } - else if(x < window_position.width - frame_thickness) - { - if(y <= frame_thickness) - return &m_resizer_top; + else + return &m_resizer_bottom_left; + } else if (x < window_position.width - frame_thickness) { + if (y <= frame_thickness) + return &m_resizer_top; - else if(y < frame_thickness + title_bar_height) - return &m_mover; + else if (y < frame_thickness + title_bar_height) + return &m_mover; - else if(y >= window_position.height - frame_thickness) - return &m_resizer_bottom; - } - else - { - if(y <= frame_thickness) - return &m_resizer_top_right; + else if (y >= window_position.height - frame_thickness) + return &m_resizer_bottom; + } else { + if (y <= frame_thickness) + return &m_resizer_top_right; - else if(y < window_position.height-frame_thickness) - return &m_resizer_right; + else if (y < window_position.height - frame_thickness) + return &m_resizer_right; - else - return &m_resizer_bottom_right; - } - - return child_result; + else + return &m_resizer_bottom_right; + } + return child_result; } /** @@ -135,50 +130,65 @@ MouseEventHandler* Window::on_mouse_button_pressed(uint32_t mouseX, uint32_t mou * * @param gc The graphics context to draw on. */ -void Window::draw_self(common::GraphicsContext* gc, common::Rectangle& area){ - - // Get the positioning of the window - Coordinates window_absolute_position = CompositeWidget::absolute_coordinates(Coordinates(0, 0)); - Rectangle windowPosition = this->position(); - int32_t window_x = window_absolute_position.first; - int32_t window_y = window_absolute_position.second; - - // Create an area for the window contents - Rectangle window_contents_area( frame_thickness, frame_thickness + title_bar_height, windowPosition.width - 2 * frame_thickness, windowPosition.height - 2 * frame_thickness - title_bar_height); - - // Draw the window contents if they are in the area to draw - if(window_contents_area.intersects(area)){ - Rectangle contents_drawable = window_contents_area.intersection(area); - gc->fill_rectangle(contents_drawable.left + window_x, contents_drawable.top + window_y, contents_drawable.left + contents_drawable.width + window_x, contents_drawable.top + contents_drawable.height + window_y,area_colour); - } - - // Draw the frame if it is in the area to draw - Rectangle window_frame_top_area(frame_thickness, 0, windowPosition.width - 2 * frame_thickness,frame_thickness + title_bar_height); - if(window_frame_top_area.intersects(area)){ - Rectangle frame_drawable = window_frame_top_area.intersection(area); - gc->fill_rectangle(frame_drawable.left + window_x, frame_drawable.top + window_y, frame_drawable.left + frame_drawable.width + window_x, frame_drawable.top + frame_drawable.height + window_y, frame_colour); - } - - // Draw the bottom of the window frame - Rectangle window_frame_bottom_area(frame_thickness, windowPosition.height - frame_thickness, windowPosition.width - 2* frame_thickness, frame_thickness); - if(window_frame_bottom_area.intersects(area)){ - Rectangle bottom_drawable = window_frame_bottom_area.intersection(area); - gc->fill_rectangle(window_x + bottom_drawable.left, window_y + bottom_drawable.top, window_x + bottom_drawable.left + bottom_drawable.width, window_y + bottom_drawable.top + bottom_drawable.height, frame_colour); - } - - // Draw the left of the window frame - Rectangle window_frame_left_area(0,0, frame_thickness, windowPosition.height); - if(window_frame_left_area.intersects(area)){ - Rectangle left_drawable = window_frame_left_area.intersection(area); - gc->fill_rectangle(window_x + left_drawable.left, window_y + left_drawable.top, window_x + left_drawable.left + left_drawable.width, window_y + left_drawable.top + left_drawable.height,frame_colour); - } - - // Draw the right of the window frame - Rectangle window_frame_right_area(windowPosition.width - frame_thickness, 0, frame_thickness, windowPosition.height); - if(window_frame_right_area.intersects(area)){ - Rectangle right_drawable = window_frame_right_area.intersection(area); - gc->fill_rectangle(window_x + right_drawable.left, window_y + right_drawable.top, window_x + right_drawable.left + right_drawable.width, window_y + right_drawable.top + right_drawable.height, frame_colour); - } +void Window::draw_self(common::GraphicsContext* gc, common::Rectangle& area) { + + // Get the positioning of the window + Coordinates window_absolute_position = CompositeWidget::absolute_coordinates(Coordinates(0, 0)); + Rectangle windowPosition = this->position(); + int32_t window_x = window_absolute_position.first; + int32_t window_y = window_absolute_position.second; + + // Create an area for the window contents + Rectangle window_contents_area(frame_thickness, frame_thickness + title_bar_height, + windowPosition.width - 2 * frame_thickness, + windowPosition.height - 2 * frame_thickness - title_bar_height); + + // Draw the window contents if they are in the area to draw + if (window_contents_area.intersects(area)) { + Rectangle contents_drawable = window_contents_area.intersection(area); + gc->fill_rectangle(contents_drawable.left + window_x, contents_drawable.top + window_y, + contents_drawable.left + contents_drawable.width + window_x, + contents_drawable.top + contents_drawable.height + window_y, area_colour); + } + + // Draw the frame if it is in the area to draw + Rectangle window_frame_top_area(frame_thickness, 0, windowPosition.width - 2 * frame_thickness, + frame_thickness + title_bar_height); + if (window_frame_top_area.intersects(area)) { + Rectangle frame_drawable = window_frame_top_area.intersection(area); + gc->fill_rectangle(frame_drawable.left + window_x, frame_drawable.top + window_y, + frame_drawable.left + frame_drawable.width + window_x, + frame_drawable.top + frame_drawable.height + window_y, frame_colour); + } + + // Draw the bottom of the window frame + Rectangle window_frame_bottom_area(frame_thickness, windowPosition.height - frame_thickness, + windowPosition.width - 2 * frame_thickness, frame_thickness); + if (window_frame_bottom_area.intersects(area)) { + Rectangle bottom_drawable = window_frame_bottom_area.intersection(area); + gc->fill_rectangle(window_x + bottom_drawable.left, window_y + bottom_drawable.top, + window_x + bottom_drawable.left + bottom_drawable.width, + window_y + bottom_drawable.top + bottom_drawable.height, frame_colour); + } + + // Draw the left of the window frame + Rectangle window_frame_left_area(0, 0, frame_thickness, windowPosition.height); + if (window_frame_left_area.intersects(area)) { + Rectangle left_drawable = window_frame_left_area.intersection(area); + gc->fill_rectangle(window_x + left_drawable.left, window_y + left_drawable.top, + window_x + left_drawable.left + left_drawable.width, + window_y + left_drawable.top + left_drawable.height, frame_colour); + } + + // Draw the right of the window frame + Rectangle window_frame_right_area(windowPosition.width - frame_thickness, 0, frame_thickness, + windowPosition.height); + if (window_frame_right_area.intersects(area)) { + Rectangle right_drawable = window_frame_right_area.intersection(area); + gc->fill_rectangle(window_x + right_drawable.left, window_y + right_drawable.top, + window_x + right_drawable.left + right_drawable.width, + window_y + right_drawable.top + right_drawable.height, frame_colour); + } } /** @@ -186,18 +196,18 @@ void Window::draw_self(common::GraphicsContext* gc, common::Rectangle& * * @param child The child to add. */ +void Window::add_child(Widget* child) { -void Window::add_child(Widget *child) { - - // If there is a child to add - if(child != nullptr){ + // If there is a child to add + if (child != nullptr) { - // Change the position of the child to be inside the window contents - Rectangle childPosition = child->position(); - child -> move(childPosition.left + frame_thickness + 1, childPosition.top + frame_thickness + title_bar_height + 1); + // Change the position of the child to be inside the window contents + Rectangle childPosition = child->position(); + child->move(childPosition.left + frame_thickness + 1, + childPosition.top + frame_thickness + title_bar_height + 1); - } + } - // Add the child to the window - CompositeWidget::add_child(child); + // Add the child to the window + CompositeWidget::add_child(child); } \ No newline at end of file diff --git a/kernel/src/hardwarecommunication/acpi.cpp b/kernel/src/hardwarecommunication/acpi.cpp index e976431f..b8af2c47 100644 --- a/kernel/src/hardwarecommunication/acpi.cpp +++ b/kernel/src/hardwarecommunication/acpi.cpp @@ -9,53 +9,52 @@ using namespace MaxOS::hardwarecommunication; using namespace MaxOS::system; using namespace MaxOS::memory; using namespace MaxOS::common; -AdvancedConfigurationAndPowerInterface::AdvancedConfigurationAndPowerInterface(system::Multiboot* multiboot) { - Logger::INFO() << "Setting up ACPI\n"; +AdvancedConfigurationAndPowerInterface::AdvancedConfigurationAndPowerInterface(system::Multiboot* multiboot) { - // If the new ACPI is not supported, panic - ASSERT(multiboot->new_acpi() != nullptr || multiboot->old_acpi() != nullptr, "No ACPI found!"); + Logger::INFO() << "Setting up ACPI\n"; - // Check if the new ACPI is supported - m_using_new_acpi = multiboot->old_acpi() == nullptr; - Logger::DEBUG() << "CPU Supports " << (m_using_new_acpi ? "New" : "Old") << " ACPI\n"; + // If the new ACPI is not supported, panic + ASSERT(multiboot->new_acpi() != nullptr || multiboot->old_acpi() != nullptr, "No ACPI found!"); + // Check if the new ACPI is supported + m_using_new_acpi = multiboot->old_acpi() == nullptr; + Logger::DEBUG() << "CPU Supports " << (m_using_new_acpi ? "New" : "Old") << " ACPI\n"; - if(m_using_new_acpi){ + if (m_using_new_acpi) { - // Get the RSDP & XSDT - m_rsdp2 = (RSDPDescriptor2*)(multiboot->new_acpi() + 1); - m_xsdt = (XSDT*) PhysicalMemoryManager::to_higher_region((uint64_t)m_rsdp2->xsdt_address); - }else{ + // Get the RSDP & XSDT + m_rsdp2 = (RSDPDescriptor2*) (multiboot->new_acpi() + 1); + m_xsdt = (XSDT*) PhysicalMemoryManager::to_higher_region((uint64_t) m_rsdp2->xsdt_address); + } else { - // Get the RSDP & RSDT - m_rsdp = (RSDPDescriptor*)(multiboot->old_acpi() + 1); - m_rsdt = (RSDT*) PhysicalMemoryManager::to_higher_region((uint64_t)m_rsdp->rsdt_address); - } + // Get the RSDP & RSDT + m_rsdp = (RSDPDescriptor*) (multiboot->old_acpi() + 1); + m_rsdt = (RSDT*) PhysicalMemoryManager::to_higher_region((uint64_t) m_rsdp->rsdt_address); + } - // Map the XSDT/RSDT - uint64_t physical_address = m_using_new_acpi ? m_rsdp2->xsdt_address : m_rsdp->rsdt_address; - auto virtual_address = (uint64_t)PhysicalMemoryManager::to_higher_region(physical_address); - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)PhysicalMemoryManager::align_direct_to_page(physical_address), (virtual_address_t*)virtual_address, Present | Write); - Logger::DEBUG() << "XSDT/RSDT: physical: 0x" << physical_address << ", virtual: 0x" << virtual_address << "\n"; + // Map the XSDT/RSDT + uint64_t physical_address = m_using_new_acpi ? m_rsdp2->xsdt_address : m_rsdp->rsdt_address; + auto virtual_address = (uint64_t) PhysicalMemoryManager::to_higher_region(physical_address); + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) PhysicalMemoryManager::align_direct_to_page(physical_address), (virtual_address_t*) virtual_address, Present | Write); + Logger::DEBUG() << "XSDT/RSDT: physical: 0x" << physical_address << ", virtual: 0x" << virtual_address << "\n"; - // Reserve the XSDT/RSDT - PhysicalMemoryManager::s_current_manager->reserve(m_using_new_acpi ? (uint64_t)m_rsdp2->xsdt_address : (uint64_t)m_rsdp->rsdt_address); + // Reserve the XSDT/RSDT + PhysicalMemoryManager::s_current_manager->reserve( m_using_new_acpi ? (uint64_t) m_rsdp2->xsdt_address : (uint64_t) m_rsdp->rsdt_address); - // Load the header - m_header = m_using_new_acpi ? &m_xsdt->header : &m_rsdt->header; + // Load the header + m_header = m_using_new_acpi ? &m_xsdt->header : &m_rsdt->header; - // Map the Tables - Logger::DEBUG() << "Mapping ACPI Tables\n"; - map_tables(m_using_new_acpi ? sizeof (uint64_t) : sizeof (uint32_t)); + // Map the Tables + Logger::DEBUG() << "Mapping ACPI Tables\n"; + map_tables(m_using_new_acpi ? sizeof(uint64_t) : sizeof(uint32_t)); - // Check if the checksum is valid - ASSERT(valid_checksum(), "ACPI: Invalid checksum!"); + // Check if the checksum is valid + ASSERT(valid_checksum(), "ACPI: Invalid checksum!"); } AdvancedConfigurationAndPowerInterface::~AdvancedConfigurationAndPowerInterface() = default; - /** * @brief Maps the tables into the higher half * @@ -63,60 +62,69 @@ AdvancedConfigurationAndPowerInterface::~AdvancedConfigurationAndPowerInterface( */ void AdvancedConfigurationAndPowerInterface::map_tables(uint8_t size_of_tables) { - for(uint32_t i = 0; i < (m_header->length - sizeof(ACPISDTHeader)) / size_of_tables; i++) { + for (uint32_t i = 0; i < (m_header->length - sizeof(ACPISDTHeader)) / size_of_tables; i++) { - // Get the address (aligned to page) - auto address = (uint64_t) (m_using_new_acpi ? m_xsdt->pointers[i] : m_rsdt->pointers[i]); - address = PhysicalMemoryManager::align_direct_to_page((size_t)address); + // Get the address (aligned to page) + auto address = (uint64_t) (m_using_new_acpi ? m_xsdt->pointers[i] : m_rsdt->pointers[i]); + address = PhysicalMemoryManager::align_direct_to_page((size_t) address); - // Map to the higher half - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)address, (void*)PhysicalMemoryManager::to_io_region(address), Present | Write); + // Map to the higher half + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) address, (void*) PhysicalMemoryManager::to_io_region(address), Present | Write); - // Reserve the memory - PhysicalMemoryManager::s_current_manager->reserve(address); - } + // Reserve the memory + PhysicalMemoryManager::s_current_manager->reserve(address); + } } +/** + * @brief Validates the checksum of a descriptor + * + * @param descriptor The descriptor to validate + * @param length The length of the descriptor + * @return True if the checksum is valid, false otherwise + */ +bool AdvancedConfigurationAndPowerInterface::validate(const char* descriptor, size_t length) { -bool AdvancedConfigurationAndPowerInterface::validate(const char*descriptor, size_t length) { - // Checksum - uint32_t sum = 0; - - // Calculate the checksum - for(uint32_t i = 0; i < length; i++) - sum += ((char*)descriptor)[i]; + // Checksum + uint32_t sum = 0; - // Check if the checksum is valid - return ((sum & 0xFF) == 0); + // Calculate the checksum + for (uint32_t i = 0; i < length; i++) + sum += ((char*) descriptor)[i]; + // Check if the checksum is valid + return ((sum & 0xFF) == 0); } +/** + * @brief Finds a table with the given signature + * + * @param signature The signature to search for + * @return The table with the given signature, or nullptr if not found + */ +ACPISDTHeader* AdvancedConfigurationAndPowerInterface::find(char const* signature) { + // Get the number of entries + size_t entries = (m_header->length - sizeof(ACPISDTHeader)) / sizeof(uint32_t); + if (m_using_new_acpi) entries = (m_header->length - sizeof(ACPISDTHeader)) / sizeof(uint64_t); -ACPISDTHeader* AdvancedConfigurationAndPowerInterface::find(char const *signature) { - - - // Get the number of entries - size_t entries = (m_header->length - sizeof(ACPISDTHeader)) / sizeof(uint32_t); - if(m_using_new_acpi) entries = (m_header->length - sizeof(ACPISDTHeader)) / sizeof(uint64_t); - - // Loop through all the entries - for (size_t i = 0; i < entries; ++i) { + // Loop through all the entries + for (size_t i = 0; i < entries; ++i) { - // Get the entry - auto* header = (ACPISDTHeader*) (m_using_new_acpi ? m_xsdt->pointers[i] : m_rsdt->pointers[i]); + // Get the entry + auto* header = (ACPISDTHeader*) (m_using_new_acpi ? m_xsdt->pointers[i] : m_rsdt->pointers[i]); - // Move the header to the higher half - header = (ACPISDTHeader*)PhysicalMemoryManager::to_io_region((uint64_t)header); + // Move the header to the higher half + header = (ACPISDTHeader*) PhysicalMemoryManager::to_io_region((uint64_t) header); - // Check if the signature matches - if(strncmp(header->signature, signature, 4) != 0) - return header; - } + // Check if the signature matches + if (strncmp(header->signature, signature, 4) != 0) + return header; + } - // Return null if no entry was found - return nullptr; + // Return null if no entry was found + return nullptr; } /** @@ -126,15 +134,14 @@ ACPISDTHeader* AdvancedConfigurationAndPowerInterface::find(char const *signatur */ bool AdvancedConfigurationAndPowerInterface::valid_checksum() { - // Get the information about the ACPI - char* check = m_using_new_acpi ? (char*)m_rsdp2 : (char*)m_rsdp; - uint32_t length = m_using_new_acpi ? sizeof(RSDPDescriptor2) : sizeof(RSDPDescriptor); - - // Calculate the checksum - uint8_t sum = 0; - for(uint32_t i = 0; i < length; i++) - sum += check[i]; + // Get the information about the ACPI + char* check = m_using_new_acpi ? (char*) m_rsdp2 : (char*) m_rsdp; + uint32_t length = m_using_new_acpi ? sizeof(RSDPDescriptor2) : sizeof(RSDPDescriptor); - return sum == 0; + // Calculate the checksum + uint8_t sum = 0; + for (uint32_t i = 0; i < length; i++) + sum += check[i]; + return sum == 0; } diff --git a/kernel/src/hardwarecommunication/apic.cpp b/kernel/src/hardwarecommunication/apic.cpp index 33631326..3e510755 100644 --- a/kernel/src/hardwarecommunication/apic.cpp +++ b/kernel/src/hardwarecommunication/apic.cpp @@ -10,294 +10,323 @@ using namespace MaxOS::hardwarecommunication; using namespace MaxOS::system; using namespace MaxOS::memory; -LocalAPIC::LocalAPIC() -{ - // Read information about the local APIC - uint64_t msr_info = CPU::read_msr(0x1B); - - // Get the APIC base address - m_apic_base = msr_info & 0xFFFFF000; +LocalAPIC::LocalAPIC() { - // Read if the APIC supports x2APIC - uint32_t ignored, xleaf, x2leaf; - CPU::cpuid(0x01, &ignored, &ignored, &x2leaf, &xleaf); + // Get the APIC base address + uint64_t msr_info = CPU::read_msr(0x1B); + m_apic_base = msr_info & 0xFFFFF000; + PhysicalMemoryManager::s_current_manager->reserve(m_apic_base); - if(x2leaf & (1 << 21)) { + // Check if the APIC supports x2APIC + uint32_t ignored, xleaf, x2leaf; + CPU::cpuid(0x01, &ignored, &ignored, &x2leaf, &xleaf); - // Enable x2APIC - m_x2apic = true; - msr_info |= (1 << 10); - CPU::write_msr(0x1B, msr_info); - Logger::DEBUG() << "CPU supports x2APIC\n"; + if (x2leaf & (1 << 21)) { - } else if (xleaf & (1 << 9)) { + // Enable x2APIC + m_x2apic = true; + msr_info |= (1 << 10); + CPU::write_msr(0x1B, msr_info); + Logger::DEBUG() << "CPU supports x2APIC\n"; - m_x2apic = false; - Logger::DEBUG() << "CPU supports xAPIC\n"; + } else if (xleaf & (1 << 9)) { - // Map the APIC base address to the higher half - m_apic_base_high = (uint64_t)PhysicalMemoryManager::to_io_region(m_apic_base); - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)m_apic_base, (virtual_address_t*)m_apic_base_high, Write | Present); - Logger::DEBUG() << "APIC Base: phy=0x" << m_apic_base << ", virt=0x" << m_apic_base_high << "\n"; + m_x2apic = false; + Logger::DEBUG() << "CPU supports xAPIC\n"; - } else { - ASSERT(false, "CPU does not support xAPIC"); - } + // Map the APIC base address to the higher half + m_apic_base_high = (uint64_t) PhysicalMemoryManager::to_io_region(m_apic_base); + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) m_apic_base, + (virtual_address_t*) m_apic_base_high, Write | Present); + Logger::DEBUG() << "APIC Base: phy=0x" << m_apic_base << ", virt=0x" << m_apic_base_high << "\n"; - // Get information about the APIC - uint32_t spurious_vector = read(0xF0); - bool is_enabled = msr_info & (1 << 11); - bool is_bsp = msr_info & (1 << 8); - Logger::DEBUG() << "APIC: boot processor: " << (is_bsp ? "Yes" : "No") << ", enabled (globally): " << (is_enabled ? "Yes" : "No") << " Spurious Vector: 0x" << (uint64_t)(spurious_vector & 0xFF) << "\n"; + } else { + ASSERT(false, "CPU does not support xAPIC"); + } + // Get information about the APIC + uint32_t spurious_vector = read(0xF0); + bool is_enabled = msr_info & (1 << 11); + bool is_bsp = msr_info & (1 << 8); + Logger::DEBUG() << "APIC: boot processor: " << (is_bsp ? "Yes" : "No") << ", enabled (globally): " << (is_enabled ? "Yes" : "No") << " Spurious Vector: 0x" << (uint64_t) (spurious_vector & 0xFF) << "\n"; - if(!is_enabled) { - Logger::WARNING() << "APIC is not enabled\n"; - return; - } - // Enable the APIC - write(0xF0, (1 << 8) | 0x100); - Logger::DEBUG() << "APIC Enabled\n"; + if (!is_enabled) { + Logger::WARNING() << "APIC is not enabled\n"; + return; + } - // Reserve the APIC base - PhysicalMemoryManager::s_current_manager->reserve(m_apic_base); - - // Read the APIC version - uint32_t version = read(0x30); - Logger::DEBUG() << "APIC Version: 0x" << (uint64_t)(version & 0xFF) << "\n"; + // Enable the APIC + write(0xF0, (1 << 8) | 0x100); + Logger::DEBUG() << "APIC Enabled\n"; } LocalAPIC::~LocalAPIC() = default; -uint32_t LocalAPIC::read(uint32_t reg) const{ - - // If x2APIC is enabled, use the x2APIC MSR - if(m_x2apic) { - return (uint32_t)CPU::read_msr((reg >> 4) + 0x800); - } else { - return (*(volatile uint32_t*)((uintptr_t)m_apic_base_high + reg)); +/** + * @brief Read a value from the apic register using the MSR or memory I/O depending on the local apic version + * + * @param reg The register to read + * @return The value of the register + */ +uint32_t LocalAPIC::read(uint32_t reg) const { - } + // If x2APIC is enabled I/O is done through the MSR + if (m_x2apic) + return (uint32_t) CPU::read_msr((reg >> 4) + 0x800); + return *(volatile uint32_t*) ((uintptr_t) m_apic_base_high + reg); } +/** + * @brief Write a value to the apic register using the MSR or memory I/O depending on the local apic version + * + * @param reg The register to write to + * @param value The value to write + */ void LocalAPIC::write(uint32_t reg, uint32_t value) const { - // If x2APIC is enabled, use the x2APIC MSR - if(m_x2apic) { - CPU::write_msr((reg >> 4) + 0x800, value); - } else { - (*(volatile uint32_t*)((uintptr_t)m_apic_base_high + reg)) = value; - } + // If x2APIC is enabled I/O is done through the MSR + if (m_x2apic) + CPU::write_msr((reg >> 4) + 0x800, value); + + // Default to memory I/O + *(volatile uint32_t*) ((uintptr_t) m_apic_base_high + reg) = value; } +/** + * @brief Get the id of the local apic + * + * @return The id of the local apic + */ uint32_t LocalAPIC::id() const { - // Read the id - uint32_t id = read(0x20); - - // Return the id - return m_x2apic ? id : (id >> 24); + // Read the id + uint32_t id = read(0x20); + // Return the id + return m_x2apic ? id : (id >> 24); } +/** + * @brief Acknowledge that the interrupt has been handled, allowing the PIC to process the next interrupt + */ void LocalAPIC::send_eoi() const { - // Send the EOI - write(0xB0, 0); + write(0xB0, 0); } IOAPIC::IOAPIC(AdvancedConfigurationAndPowerInterface* acpi) : m_acpi(acpi) { - - // Get the information about the IO APIC - m_madt = (MADT*)m_acpi->find("APIC"); - MADT_Item* io_apic_item = get_madt_item(1, 0); - - // Get the IO APIC - auto* io_apic = (MADT_IOAPIC*)PhysicalMemoryManager::to_io_region((uint64_t)io_apic_item + sizeof(MADT_Item)); - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)io_apic_item, (virtual_address_t*)(io_apic - sizeof(MADT_Item)), Present | Write); - - - // Map the IO APIC address to the higher half - m_address = io_apic->io_apic_address; - m_address_high = (uint64_t)PhysicalMemoryManager::to_io_region(m_address); - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)m_address, (virtual_address_t*)m_address_high, Present | Write); - Logger::DEBUG() << "IO APIC Address: phy=0x" << m_address << ", virt=0x" << m_address_high << "\n"; - - // Get the IO APIC version and max redirection entry - m_version = read(0x1); - m_max_redirect_entry = (uint8_t)(m_version >> 16); - - // Log the IO APIC information - Logger::DEBUG() << "IO APIC Version: 0x" << (uint64_t)(m_version & 0xFF) << "\n"; - Logger::DEBUG() << "IO APIC Max Redirection Entry: 0x" << (uint64_t)m_max_redirect_entry << "\n"; - - // Get the source override item - MADT_Item* source_override_item = get_madt_item(2, m_override_array_size); - - // Loop through the source override items - uint32_t total_length = sizeof(MADT); - while (total_length < m_madt->header.length && m_override_array_size < 0x10){ // 0x10 is the max items - - // Increment the total length - total_length += source_override_item->length; - - // If there is an override, populate the array - if(source_override_item != nullptr && source_override_item->type == 2) { - - // Get the override and populate the array - auto* override = (Override *)(source_override_item + 1); - m_override_array[m_override_array_size].bus = override->bus; - m_override_array[m_override_array_size].source = override->source; - m_override_array[m_override_array_size].global_system_interrupt = override->global_system_interrupt; - m_override_array[m_override_array_size].flags = override->flags; - - // Increment the override array size - m_override_array_size++; - } - - // Get the next item - source_override_item = get_madt_item(2, m_override_array_size); - - // If there is no next item then break - if(source_override_item == nullptr) - break; - } - - // Log how many overrides were found - Logger::DEBUG() << "IO APIC Source Overrides: 0x" << m_override_array_size << "\n"; + // Get the information about the IO APIC + m_madt = (MADT*) m_acpi->find("APIC"); + MADT_Item* io_apic_item = get_madt_item(1, 0); + + // Get the IO APIC + auto* io_apic = (MADT_IOAPIC*) PhysicalMemoryManager::to_io_region((uint64_t) io_apic_item + sizeof(MADT_Item)); + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) io_apic_item, (virtual_address_t*) (io_apic - sizeof(MADT_Item)), Present | Write); + + // Map the IO APIC address to the higher half + m_address = io_apic->io_apic_address; + m_address_high = (uint64_t) PhysicalMemoryManager::to_io_region(m_address); + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) m_address, (virtual_address_t*) m_address_high, Present | Write); + Logger::DEBUG() << "IO APIC Address: phy=0x" << m_address << ", virt=0x" << m_address_high << "\n"; + + // Get the IO APIC version and max redirection entry + m_version = read(0x1); + m_max_redirect_entry = (uint8_t) (m_version >> 16); + + // Log the IO APIC information + Logger::DEBUG() << "IO APIC Version: 0x" << (uint64_t) (m_version & 0xFF) << "\n"; + Logger::DEBUG() << "IO APIC Max Redirection Entry: 0x" << (uint64_t) m_max_redirect_entry << "\n"; + + // Get the source override item + MADT_Item* source_override_item = get_madt_item(2, m_override_array_size); + uint32_t total_length = sizeof(MADT); + while (total_length < m_madt->header.length && m_override_array_size < 0x10) { // 0x10 is the max items + + total_length += source_override_item->length; + + // If there is an override, populate the array + if (source_override_item != nullptr && source_override_item->type == 2) { + + // Get the override and populate the array + auto* override = (Override*) (source_override_item + 1); + m_override_array[m_override_array_size].bus = override->bus; + m_override_array[m_override_array_size].source = override->source; + m_override_array[m_override_array_size].global_system_interrupt = override->global_system_interrupt; + m_override_array[m_override_array_size].flags = override->flags; + m_override_array_size++; + } + + // Get the next item + source_override_item = get_madt_item(2, m_override_array_size); + if (source_override_item == nullptr) + break; + } + + Logger::DEBUG() << "IO APIC Source Overrides: 0x" << m_override_array_size << "\n"; } IOAPIC::~IOAPIC() = default; -MADT_Item *IOAPIC::get_madt_item(uint8_t type, uint8_t index) { - - // The item starts at the start of the MADT - auto* item = (MADT_Item*)((uint64_t)m_madt + sizeof(MADT)); - uint64_t total_length = 0; - uint8_t current_index = 0; - - // Loop through the items - while (total_length + sizeof(MADT) < m_madt->header.length && current_index <= index) { - - // Check if the item is the correct type - if(item->type == type) { - - // Check if the item is the correct index - if(current_index == index) { - return item; - } - - // Increment the index - current_index++; - } - - // Increment the total length - total_length += item->length; - - // Increment the item - item = (MADT_Item*)((uint64_t)item + item->length); - } - - // Return null if the item was not found - return nullptr; +/** + * @brief Get an item in the MADT + * + * @param type The type of item + * @param index The index of the item + * @return The item or null if not found + */ +MADT_Item* IOAPIC::get_madt_item(uint8_t type, uint8_t index) { + + // The item starts at the start of the MADT + auto* item = (MADT_Item*) ((uint64_t) m_madt + sizeof(MADT)); + uint64_t total_length = 0; + uint8_t current_index = 0; + + // Loop through the items + while (total_length + sizeof(MADT) < m_madt->header.length && current_index <= index) { + + // Correct type + if (item->type == type) { + + // Correct index means found + if (current_index == index) + return item; + + // Right type wrong index so move on + current_index++; + } + + // Increment the total length + total_length += item->length; + item = (MADT_Item*) ((uint64_t) item + item->length); + } + + // No item found + return nullptr; } +/** + * @brief Read a value from a IO Apic register + * + * @param reg The register to read from + * @return The value at the register + */ uint32_t IOAPIC::read(uint32_t reg) const { - // Write the register - *(volatile uint32_t*)(m_address_high + 0x00) = reg; - - // Return the value - return *(volatile uint32_t*)(m_address_high + 0x10); - - + // Tell the APIC what register to read from + *(volatile uint32_t*) (m_address_high + 0x00) = reg; + // Read the value + return *(volatile uint32_t*) (m_address_high + 0x10); } +/** + * @brief Write a value to an IO Apic register + * + * @param reg The register to write to + * @param value The value to set the register to + * @return The value at the register + */ void IOAPIC::write(uint32_t reg, uint32_t value) const { - // Write the register - *(volatile uint32_t*)(m_address_high + 0x00) = reg; + // Write the register + *(volatile uint32_t*) (m_address_high + 0x00) = reg; - // Write the value - *(volatile uint32_t*)(m_address_high + 0x10) = value; + // Write the value + *(volatile uint32_t*) (m_address_high + 0x10) = value; } -void IOAPIC::read_redirect(uint8_t index, RedirectionEntry *entry) { - - // If the index is out of bounds, return - if(index < 0x10 || index > 0x3F) - return; - - // Low and high registers - uint32_t low = read(index); - uint32_t high = read(index + 1); - - // Set the entry - entry->raw = ((uint64_t)high << 32) | ((uint64_t)low); - +/** + * @brief Read a redirect entry into a buffer + * + * @param index The index of the entry + * @param entry The buffer to read into + */ +void IOAPIC::read_redirect(uint8_t index, RedirectionEntry* entry) { + + // Check bounds + if (index < 0x10 || index > 0x3F) + return; + + // Read the entry chunks into the buffer (struct is auto extracted from the raw information) + uint32_t low = read(index); + uint32_t high = read(index + 1); + entry->raw = ((uint64_t) high << 32) | ((uint64_t) low); } -void IOAPIC::write_redirect(uint8_t index, RedirectionEntry *entry) { - - // If the index is out of bounds, return - if(index < 0x10 || index > 0x3F) - return; - - // Low and high registers - auto low = (uint32_t)entry->raw; - auto high = (uint32_t)(entry->raw >> 32); - - // Set the entry - write(index, low); - write(index + 1, high); +/** + * @brief Write a redirect entry from a buffer + * + * @param index The index of the entry + * @param entry The buffer to write into + */ +void IOAPIC::write_redirect(uint8_t index, RedirectionEntry* entry) { + + // Check bounds + if (index < 0x10 || index > 0x3F) + return; + + // Break the entry down into 32bit chunks + auto low = (uint32_t) entry->raw; + auto high = (uint32_t) (entry->raw >> 32); + + // Set the entry + write(index, low); + write(index + 1, high); } -void IOAPIC::set_redirect(interrupt_redirect_t *redirect) { +/** + * @brief Redirect a system interrupt to a different IRQ + * + * @param redirect The redirection entry + */ +void IOAPIC::set_redirect(interrupt_redirect_t* redirect) { - // Create the redirection entry - RedirectionEntry entry = {}; - entry.raw = redirect->flags | (redirect -> interrupt & 0xFF); - entry.destination = redirect->destination; - entry.mask = redirect->mask; + // Create the redirection entry + RedirectionEntry entry = {}; + entry.raw = redirect->flags | (redirect->interrupt & 0xFF); + entry.destination = redirect->destination; + entry.mask = redirect->mask; - // Check if a global system interrupt is used - for (uint8_t i = 0; i < m_override_array_size; i++) { + // Check if a global system interrupt is used + for (uint8_t i = 0; i < m_override_array_size; i++) { - if (m_override_array[i].source != redirect->type) - continue; + if (m_override_array[i].source != redirect->type) + continue; - // Set the lower 4 bits of the pin - entry.pin_polarity = ((m_override_array[i].flags & 0b11) == 2) ? 0b1 : 0b0; - entry.pin_polarity = (((m_override_array[i].flags >> 2) & 0b11) == 2) ? 0b1 : 0b0; + // Set the lower 4 bits of the pin + entry.pin_polarity = ((m_override_array[i].flags & 0b11) == 2) ? 0b1 : 0b0; + entry.pin_polarity = (((m_override_array[i].flags >> 2) & 0b11) == 2) ? 0b1 : 0b0; - // Set the trigger mode - entry.trigger_mode = (((m_override_array[i].flags >> 2) & 0b11) == 2); + // Set the trigger mode + entry.trigger_mode = (((m_override_array[i].flags >> 2) & 0b11) == 2); + break; - break; - - } - - // Write the redirect - write_redirect(redirect->index, &entry); + } + // Write the redirect + write_redirect(redirect->index, &entry); } -void IOAPIC::set_redirect_mask(uint8_t index, bool mask) { - // Read the current entry - RedirectionEntry entry = {}; - read_redirect(index, &entry); +/** + * @brief Enables/Disables an interrupt redirect by masking it + * + * @param index The index of the redirect mask + * @param mask True = masked = disabled, False = unmasked = enabled + */ +void IOAPIC::set_redirect_mask(uint8_t index, bool mask) { - // Set the mask - entry.mask = mask; + // Read the current entry + RedirectionEntry entry = {}; + read_redirect(index, &entry); - // Write the entry - write_redirect(index, &entry); + // Set the mask + entry.mask = mask; + write_redirect(index, &entry); } AdvancedProgrammableInterruptController::AdvancedProgrammableInterruptController(AdvancedConfigurationAndPowerInterface* acpi) @@ -307,62 +336,72 @@ AdvancedProgrammableInterruptController::AdvancedProgrammableInterruptController m_pic_slave_data_port(0xA1) { - Logger::INFO() << "Setting up APIC\n"; - - // Init the Local APIC - Logger::DEBUG() << "Initialising Local APIC\n"; - m_local_apic = new LocalAPIC(); + // Register this APIC + Logger::INFO() << "Setting up APIC\n"; + InterruptManager::active_interrupt_manager()->set_apic(this); - // Disable the old PIC - Logger::DEBUG() << "Disabling PIC\n"; - disable_pic(); + // Init the Local APIC + Logger::DEBUG() << "Initialising Local APIC\n"; + m_local_apic = new LocalAPIC(); - // Init the IO APIC - Logger::DEBUG() << "Initialising IO APIC\n"; - m_io_apic = new IOAPIC(acpi); - - // Register the APIC - InterruptManager::active_interrupt_manager()->set_apic(this); + // Disable the legacy mode PIC + Logger::DEBUG() << "Disabling PIC\n"; + disable_pic(); + // Init the IO APIC + Logger::DEBUG() << "Initialising IO APIC\n"; + m_io_apic = new IOAPIC(acpi); } -AdvancedProgrammableInterruptController::~AdvancedProgrammableInterruptController() -{ - - // Free the memory - delete m_local_apic; - delete m_io_apic; +AdvancedProgrammableInterruptController::~AdvancedProgrammableInterruptController() { + // Free the memory + delete m_local_apic; + delete m_io_apic; } +/** + * @brief Disable the legacy PIC to prevent it from sending interrupts + */ void AdvancedProgrammableInterruptController::disable_pic() { - // Initialise the PIC - m_pic_master_command_port.write(0x11); - m_pic_slave_command_port.write(0x11); - - // Set the offsets - m_pic_master_data_port.write(0x20); - m_pic_slave_data_port.write(0x28); + // Initialise the PIC + m_pic_master_command_port.write(0x11); + m_pic_slave_command_port.write(0x11); - // Set the slave/master relationships - m_pic_master_data_port.write(0x04); - m_pic_slave_data_port.write(0x02); + // Set the offsets + m_pic_master_data_port.write(0x20); + m_pic_slave_data_port.write(0x28); - // Set the modes (8086/8086) - m_pic_master_data_port.write(0x01); - m_pic_slave_data_port.write(0x01); + // Set the slave/master relationships + m_pic_master_data_port.write(0x04); + m_pic_slave_data_port.write(0x02); - // Mask the interrupts - m_pic_master_data_port.write(0xFF); - m_pic_slave_data_port.write(0xFF); + // Set the modes (8086/8086) + m_pic_master_data_port.write(0x01); + m_pic_slave_data_port.write(0x01); + // Mask the interrupts + m_pic_master_data_port.write(0xFF); + m_pic_slave_data_port.write(0xFF); } -LocalAPIC *AdvancedProgrammableInterruptController::local_apic() const -{ - return m_local_apic; -} -IOAPIC* AdvancedProgrammableInterruptController::io_apic() const -{ - return m_io_apic; + +/** + * @brief Get the local apic + * + * @return The local apic + */ +LocalAPIC* AdvancedProgrammableInterruptController::local_apic() const { + + return m_local_apic; } + +/** + * @brief Get the io apic + * + * @return The io apic + */ +IOAPIC* AdvancedProgrammableInterruptController::io_apic() const { + + return m_io_apic; +} \ No newline at end of file diff --git a/kernel/src/hardwarecommunication/interrupts.cpp b/kernel/src/hardwarecommunication/interrupts.cpp index 18466634..38f65b72 100644 --- a/kernel/src/hardwarecommunication/interrupts.cpp +++ b/kernel/src/hardwarecommunication/interrupts.cpp @@ -10,51 +10,43 @@ using namespace MaxOS::common; using namespace MaxOS::hardwarecommunication; using namespace MaxOS::system; - -///__Handler__ - InterruptHandler::InterruptHandler(uint8_t interrupt_number, int64_t redirect, uint64_t redirect_index) : m_interrupt_number(interrupt_number) { - // Get the interrupt manager - InterruptManager* interrupt_manager = InterruptManager::s_active_interrupt_manager; - - // If there is no interrupt manager, nothing can be done - ASSERT(interrupt_manager != nullptr, "No active interrupt manager"); - - // Set the handler in the array - interrupt_manager -> set_interrupt_handler(m_interrupt_number, this); - - // If there is a redirect, set it - if(redirect == -1) return; - - // Get the IO-APIC - IOAPIC* io_apic = interrupt_manager -> active_apic() -> io_apic(); - - // Register the driver - interrupt_redirect_t mouseRedirect = { - .type = (uint8_t)redirect, - .index = (uint8_t)redirect_index, - .interrupt = m_interrupt_number, - .destination = 0x00, - .flags = 0x00, - .mask = false, - }; - io_apic -> set_redirect(&mouseRedirect); + // Get the interrupt manager + InterruptManager* interrupt_manager = InterruptManager::s_active_interrupt_manager; + ASSERT(interrupt_manager != nullptr, "No active interrupt manager"); + + // Register the handler + interrupt_manager->set_interrupt_handler(m_interrupt_number, this); + + // Not all interrupts need redirecting + if (redirect == -1) + return; + + // Redirect a legacy interrupt to an interrupt the kernel expects + IOAPIC* io_apic = interrupt_manager->active_apic()->io_apic(); + interrupt_redirect_t temp = { + .type = (uint8_t) redirect, + .index = (uint8_t) redirect_index, + .interrupt = m_interrupt_number, + .destination = 0x00, + .flags = 0x00, + .mask = false, + }; + io_apic->set_redirect(&temp); } -InterruptHandler::~InterruptHandler(){ - - // Get the interrupt manager - InterruptManager* interrupt_manager = InterruptManager::s_active_interrupt_manager; +InterruptHandler::~InterruptHandler() { - // If there is no interrupt manager, no need to remove the handler - if(interrupt_manager == nullptr) return; + // Get the interrupt manager + InterruptManager* interrupt_manager = InterruptManager::s_active_interrupt_manager; + if (interrupt_manager == nullptr) return; - // Remove the handler - interrupt_manager -> set_interrupt_handler(m_interrupt_number, nullptr); + // Remove the handler + interrupt_manager->set_interrupt_handler(m_interrupt_number, nullptr); } /** @@ -66,97 +58,88 @@ void InterruptHandler::handle_interrupt() { /** * @brief Handles an interrupt and returns the status + * * @param status The status of the CPU * @return The status of the CPU */ -system::cpu_status_t* InterruptHandler::handle_interrupt(system::cpu_status_t *status) { +system::cpu_status_t* InterruptHandler::handle_interrupt(system::cpu_status_t* status) { - // For handlers that don't care about the status - handle_interrupt(); - - // Return the status - return status; + // For handlers that don't care about the status + handle_interrupt(); + // Return the default status + return status; } -///__Manger__ - - - -InterruptManager::InterruptManager() -{ - - Logger::INFO() << "Setting up Interrupt Manager\n"; - - // Full the table of interrupts with 0 - for(auto& descriptor : s_interrupt_descriptor_table) { - descriptor.address_low_bits = 0; - descriptor.address_mid_bits = 0; - descriptor.address_high_bits = 0; - descriptor.segment_selector = 0; - descriptor.ist = 0; - descriptor.flags = 0; - } - - //Set Up the base interrupts - set_interrupt_descriptor_table_entry(0x00, &HandleException0x00, 0); // Division by zero - set_interrupt_descriptor_table_entry(0x01, &HandleException0x01, 0); // Debug - set_interrupt_descriptor_table_entry(0x02, &HandleException0x02, 0); // Non-maskable interrupt - set_interrupt_descriptor_table_entry(0x03, &HandleException0x03, 0); // Breakpoint - set_interrupt_descriptor_table_entry(0x04, &HandleException0x04, 0); // Overflow - set_interrupt_descriptor_table_entry(0x05, &HandleException0x05, 0); // Bound Range Exceeded - set_interrupt_descriptor_table_entry(0x06, &HandleException0x06, 0); // Invalid Opcode - set_interrupt_descriptor_table_entry(0x06, &HandleException0x07, 0); // Device Not Available - set_interrupt_descriptor_table_entry(0x08, &HandleInterruptError0x08, 0); // Double Fault - set_interrupt_descriptor_table_entry(0x09, &HandleException0x09, 0); // Coprocessor Segment Overrun - set_interrupt_descriptor_table_entry(0x0A, &HandleInterruptError0x0A, 0); // Invalid TSS - set_interrupt_descriptor_table_entry(0x0B, &HandleInterruptError0x0B, 0); // Segment Not Present - set_interrupt_descriptor_table_entry(0x0C, &HandleInterruptError0x0C, 0); // Stack-Segment Fault - set_interrupt_descriptor_table_entry(0x0D, &HandleInterruptError0x0D, 0); // General Protection Fault - set_interrupt_descriptor_table_entry(0x0E, &HandleInterruptError0x0E, 0); // Page Fault - set_interrupt_descriptor_table_entry(0x0F, &HandleException0x0F, 0); // Reserved - set_interrupt_descriptor_table_entry(0x10, &HandleException0x10, 0); // x87 Floating-Point Exception - set_interrupt_descriptor_table_entry(0x11, &HandleInterruptError0x11, 0); // Alignment Check - set_interrupt_descriptor_table_entry(0x12, &HandleException0x12, 0); // Machine Check - set_interrupt_descriptor_table_entry(0x13, &HandleException0x13, 0); // SIMD Floating-Point Exception - set_interrupt_descriptor_table_entry(0x14, &HandleException0x14, 0); // Reserved: Virtualization Exception - set_interrupt_descriptor_table_entry(0x15, &HandleException0x15, 0); // Reserved - set_interrupt_descriptor_table_entry(0x16, &HandleException0x16, 0); // Reserved - set_interrupt_descriptor_table_entry(0x17, &HandleException0x17, 0); // Reserved - set_interrupt_descriptor_table_entry(0x18, &HandleException0x18, 0); // Reserved - set_interrupt_descriptor_table_entry(0x19, &HandleException0x19, 0); // Reserved - set_interrupt_descriptor_table_entry(0x1A, &HandleException0x1A, 0); // Reserved - set_interrupt_descriptor_table_entry(0x1B, &HandleException0x1B, 0); // Reserved - set_interrupt_descriptor_table_entry(0x1C, &HandleException0x1C, 0); // Reserved - set_interrupt_descriptor_table_entry(0x1D, &HandleException0x1D, 0); // Reserved - set_interrupt_descriptor_table_entry(0x1E, &HandleException0x1E, 0); // Security Exception - set_interrupt_descriptor_table_entry(0x1F, &HandleException0x1F, 0); // Reserved - - // Set up the hardware interrupts - set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x00, &HandleInterruptRequest0x00, 0); // APIC Timer Interrupt - set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x01, &HandleInterruptRequest0x01, 0); // Keyboard Interrupt - set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x02, &HandleInterruptRequest0x02, 0); // PIT Interrupt - set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x0C, &HandleInterruptRequest0x0C, 0); // Mouse Interrupt - - // Set up the system call interrupt - set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x60, &HandleInterruptRequest0x60, 3); // System Call Interrupt - Privilege Level 3 so that user space can call it - - //Tell the processor to use the IDT - IDTR idt = {}; - idt.limit = 256 * sizeof(InterruptDescriptor) - 1; - idt.base = (uint64_t)s_interrupt_descriptor_table; - asm volatile("lidt %0" : : "m" (idt)); - - // Set the active interrupt manager - s_active_interrupt_manager = this; +InterruptManager::InterruptManager() { + + Logger::INFO() << "Setting up Interrupt Manager\n"; + s_active_interrupt_manager = this; + + // Clear the table + for (auto& descriptor: s_interrupt_descriptor_table) { + descriptor.address_low_bits = 0; + descriptor.address_mid_bits = 0; + descriptor.address_high_bits = 0; + descriptor.segment_selector = 0; + descriptor.ist = 0; + descriptor.flags = 0; + } + + //Set Up the base interrupts + set_interrupt_descriptor_table_entry(0x00, &HandleException0x00, 0); // Division by zero + set_interrupt_descriptor_table_entry(0x01, &HandleException0x01, 0); // Debug + set_interrupt_descriptor_table_entry(0x02, &HandleException0x02, 0); // Non-maskable interrupt + set_interrupt_descriptor_table_entry(0x03, &HandleException0x03, 0); // Breakpoint + set_interrupt_descriptor_table_entry(0x04, &HandleException0x04, 0); // Overflow + set_interrupt_descriptor_table_entry(0x05, &HandleException0x05, 0); // Bound Range Exceeded + set_interrupt_descriptor_table_entry(0x06, &HandleException0x06, 0); // Invalid Opcode + set_interrupt_descriptor_table_entry(0x06, &HandleException0x07, 0); // Device Not Available + set_interrupt_descriptor_table_entry(0x08, &HandleInterruptError0x08, 0); // Double Fault + set_interrupt_descriptor_table_entry(0x09, &HandleException0x09, 0); // Coprocessor Segment Overrun + set_interrupt_descriptor_table_entry(0x0A, &HandleInterruptError0x0A, 0); // Invalid TSS + set_interrupt_descriptor_table_entry(0x0B, &HandleInterruptError0x0B, 0); // Segment Not Present + set_interrupt_descriptor_table_entry(0x0C, &HandleInterruptError0x0C, 0); // Stack-Segment Fault + set_interrupt_descriptor_table_entry(0x0D, &HandleInterruptError0x0D, 0); // General Protection Fault + set_interrupt_descriptor_table_entry(0x0E, &HandleInterruptError0x0E, 0); // Page Fault + set_interrupt_descriptor_table_entry(0x0F, &HandleException0x0F, 0); // Reserved + set_interrupt_descriptor_table_entry(0x10, &HandleException0x10, 0); // x87 Floating-Point Exception + set_interrupt_descriptor_table_entry(0x11, &HandleInterruptError0x11, 0); // Alignment Check + set_interrupt_descriptor_table_entry(0x12, &HandleException0x12, 0); // Machine Check + set_interrupt_descriptor_table_entry(0x13, &HandleException0x13, 0); // SIMD Floating-Point Exception + set_interrupt_descriptor_table_entry(0x14, &HandleException0x14, 0); // Reserved: Virtualization Exception + set_interrupt_descriptor_table_entry(0x15, &HandleException0x15, 0); // Reserved + set_interrupt_descriptor_table_entry(0x16, &HandleException0x16, 0); // Reserved + set_interrupt_descriptor_table_entry(0x17, &HandleException0x17, 0); // Reserved + set_interrupt_descriptor_table_entry(0x18, &HandleException0x18, 0); // Reserved + set_interrupt_descriptor_table_entry(0x19, &HandleException0x19, 0); // Reserved + set_interrupt_descriptor_table_entry(0x1A, &HandleException0x1A, 0); // Reserved + set_interrupt_descriptor_table_entry(0x1B, &HandleException0x1B, 0); // Reserved + set_interrupt_descriptor_table_entry(0x1C, &HandleException0x1C, 0); // Reserved + set_interrupt_descriptor_table_entry(0x1D, &HandleException0x1D, 0); // Reserved + set_interrupt_descriptor_table_entry(0x1E, &HandleException0x1E, 0); // Security Exception + set_interrupt_descriptor_table_entry(0x1F, &HandleException0x1F, 0); // Reserved + + // Set up the hardware interrupts + set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x00, &HandleInterruptRequest0x00, 0); // APIC Timer Interrupt + set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x01, &HandleInterruptRequest0x01, 0); // Keyboard Interrupt + set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x02, &HandleInterruptRequest0x02, 0); // PIT Interrupt + set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x0C, &HandleInterruptRequest0x0C, 0); // Mouse Interrupt + + // Set up the system call interrupt + set_interrupt_descriptor_table_entry(s_hardware_interrupt_offset + 0x60, &HandleInterruptRequest0x60, 3); // System Call Interrupt - Privilege Level 3 so that user space can call it + + // Tell the processor to use the IDT + IDTR idt = {}; + idt.limit = 256 * sizeof(InterruptDescriptor) - 1; + idt.base = (uint64_t) s_interrupt_descriptor_table; + asm volatile("lidt %0" : : "m" (idt)); } -InterruptManager::~InterruptManager() -{ - deactivate(); +InterruptManager::~InterruptManager() { + deactivate(); } - /** * @brief Sets an entry in the Interrupt Descriptor Table * @@ -166,56 +149,55 @@ InterruptManager::~InterruptManager() * @param descriptor_privilege_level Descriptor Privilege Level * @param descriptor_type Descriptor Type */ -void InterruptManager::set_interrupt_descriptor_table_entry(uint8_t interrupt, void (*handler)(), uint8_t descriptor_privilege_level) -{ +void InterruptManager::set_interrupt_descriptor_table_entry(uint8_t interrupt, void (* handler)(), uint8_t descriptor_privilege_level) { - // Get the address of the handler and the entry in the IDT - auto handler_address = (uint64_t)handler; - InterruptDescriptor* interrupt_descriptor = &s_interrupt_descriptor_table[interrupt]; + // Get the address of the handler and the entry in the IDT + auto handler_address = (uint64_t) handler; + InterruptDescriptor* interrupt_descriptor = &s_interrupt_descriptor_table[interrupt]; - // Set the handler address - interrupt_descriptor->address_low_bits = handler_address & 0xFFFF; - interrupt_descriptor->address_mid_bits = (handler_address >> 16) & 0xFFFF; - interrupt_descriptor->address_high_bits = (handler_address >> 32) & 0xFFFFFFFF; + // Set the handler address + interrupt_descriptor->address_low_bits = handler_address & 0xFFFF; + interrupt_descriptor->address_mid_bits = (handler_address >> 16) & 0xFFFF; + interrupt_descriptor->address_high_bits = (handler_address >> 32) & 0xFFFFFFFF; - // Set the kernel code segment offset - interrupt_descriptor->segment_selector = 0x08; + // Set the kernel code segment offset + interrupt_descriptor->segment_selector = 0x08; - // Disable IST - interrupt_descriptor->ist = 0; + // Disable IST + interrupt_descriptor->ist = 0; - // Set the flags (Trap Gate, Present and the Descriptor Privilege Level) - interrupt_descriptor->flags = 0b1110 | ((descriptor_privilege_level & 0b11) << 5) | (1 << 7); + // Set the flags (Trap Gate, Present and the Descriptor Privilege Level) + interrupt_descriptor->flags = 0b1110 | ((descriptor_privilege_level & 0b11) << 5) | (1 << 7); } - /** * @brief Activates the interrupt manager and starts interrupts (also deactivates the current interrupt manager) */ void InterruptManager::activate() { - Logger::INFO() << "Activating Interrupts \n"; + Logger::INFO() << "Activating Interrupts \n"; - // Deactivate the current interrupt manager - if(s_active_interrupt_manager != nullptr) - s_active_interrupt_manager->deactivate(); + // Deactivate the current (old) interrupt manager + if (s_active_interrupt_manager != nullptr) + s_active_interrupt_manager->deactivate(); - // Set the current interrupt manager and start interrupts - s_active_interrupt_manager = this; - asm("sti"); + // Set the current interrupt manager and start interrupts + s_active_interrupt_manager = this; + asm("sti"); } /** * @brief Deactivates the interrupt manager and stops interrupts */ -void InterruptManager::deactivate() -{ +void InterruptManager::deactivate() { - // If this is the active interrupt manager, deactivate it and stop interrupts - if(s_active_interrupt_manager == this){ - s_active_interrupt_manager = nullptr; - asm("cli"); - } + // Cant deactivate if it isn't the system one + if (s_active_interrupt_manager != nullptr) + return; + + // Prevent interrupts from firing when nothing is set up to handle them + asm("cli"); + s_active_interrupt_manager = nullptr; } /** @@ -224,30 +206,30 @@ void InterruptManager::deactivate() * @param status The current cpu status * @return The updated cpu status */ -system::cpu_status_t* InterruptManager::HandleInterrupt(system::cpu_status_t *status) { +system::cpu_status_t* InterruptManager::HandleInterrupt(system::cpu_status_t* status) { - // Fault Handlers - switch (status->interrupt_number) { + // Default Fault Handlers + switch (status->interrupt_number) { - case 0x7: - Logger::ERROR() << "Device Not Available: FPU Not Enabled\n"; - CPU::prepare_for_panic(status); - CPU::PANIC("See above message for more information", status); - break; + case 0x7: + Logger::ERROR() << "Device Not Available: FPU Not Enabled\n"; + CPU::prepare_for_panic(status); + CPU::PANIC("See above message for more information", status); + break; - case 0x0D: - return general_protection_fault(status); + case 0x0D: + return general_protection_fault(status); - case 0x0E: - return page_fault(status); - } + case 0x0E: + return page_fault(status); + } - // If there is an interrupt manager handle interrupt - if(s_active_interrupt_manager != nullptr) - return s_active_interrupt_manager->handle_interrupt_request(status); + // If there is an interrupt manager handle interrupt + if (s_active_interrupt_manager != nullptr) + return s_active_interrupt_manager->handle_interrupt_request(status); - // CPU Can continue - return status; + // CPU Can continue + return status; } /** @@ -256,7 +238,8 @@ system::cpu_status_t* InterruptManager::HandleInterrupt(system::cpu_status_t *st * @return The offset of the hardware interrupt */ uint16_t InterruptManager::hardware_interrupt_offset() { - return s_hardware_interrupt_offset; + + return s_hardware_interrupt_offset; } /** @@ -265,8 +248,9 @@ uint16_t InterruptManager::hardware_interrupt_offset() { * @param interrupt The interrupt number * @param handler The interrupt handler */ -void InterruptManager::set_interrupt_handler(uint8_t interrupt, InterruptHandler *handler) { - m_interrupt_handlers[interrupt] = handler; +void InterruptManager::set_interrupt_handler(uint8_t interrupt, InterruptHandler* handler) { + + m_interrupt_handlers[interrupt] = handler; } /** @@ -275,70 +259,100 @@ void InterruptManager::set_interrupt_handler(uint8_t interrupt, InterruptHandler * @param interrupt The interrupt number */ void InterruptManager::remove_interrupt_handler(uint8_t interrupt) { - m_interrupt_handlers[interrupt] = nullptr; + + m_interrupt_handlers[interrupt] = nullptr; } +/** + * @brief Handles the interrupt request by passing it to the appropriate handler + * + * @param status The current cpu status + * @return The updated cpu status + */ cpu_status_t* InterruptManager::handle_interrupt_request(cpu_status_t* status) { - // Where to go afterward - cpu_status_t* new_status = status; - - // If there is an interrupt manager, handle the interrupt - if(m_interrupt_handlers[status -> interrupt_number] != nullptr) - new_status = m_interrupt_handlers[status -> interrupt_number]->handle_interrupt(status); - else - Logger::WARNING() << "Interrupt " << (int)status->interrupt_number << " not handled\n"; + // Where to go afterward + cpu_status_t* new_status = status; - // Send the EOI to the APIC - if(s_hardware_interrupt_offset <= status->interrupt_number && status->interrupt_number < s_hardware_interrupt_offset + 16) - m_apic->local_apic()->send_eoi(); + // If there is an interrupt manager, handle the interrupt + if (m_interrupt_handlers[status->interrupt_number] != nullptr) + new_status = m_interrupt_handlers[status->interrupt_number]->handle_interrupt(status); + else + Logger::WARNING() << "Interrupt " << (int) status->interrupt_number << " not handled\n"; - // Return the status - return new_status; -} + // Send the EOI to the APIC + if (s_hardware_interrupt_offset <= status->interrupt_number && status->interrupt_number < s_hardware_interrupt_offset + 16) + m_apic->local_apic()->send_eoi(); -void InterruptManager::set_apic(AdvancedProgrammableInterruptController *apic) { - m_apic = apic; + // Return the status + return new_status; } -cpu_status_t* InterruptManager::page_fault(system::cpu_status_t *status) { - bool present = (status ->error_code & 0x1) != 0; // Bit 0: Page present flag - bool write = (status ->error_code & 0x2) != 0; // Bit 1: Write operation flag - bool user_mode = (status ->error_code & 0x4) != 0; // Bit 2: User mode flag - bool reserved_write = (status ->error_code & 0x8) != 0; // Bit 3: Reserved bit write flag - bool instruction_fetch = (status ->error_code & 0x10) != 0; // Bit 4: Instruction fetch flag (on some CPUs) - uint64_t faulting_address; - asm volatile("movq %%cr2, %0" : "=r" (faulting_address)); - - cpu_status_t* can_avoid = CPU::prepare_for_panic(status); - if(can_avoid != nullptr) - return can_avoid; +/** + * @brief Sets the APIC + * + * @param apic The APIC + */ +void InterruptManager::set_apic(AdvancedProgrammableInterruptController* apic) { - Logger::ERROR() << "Page Fault: (0x" << faulting_address << "): present: " << (present ? "Yes" : "No") << ", write: " << (write ? "Yes" : "No") << ", user-mode: " << (user_mode ? "Yes" : "No") << ", reserved write: " << (reserved_write ? "Yes" : "No") << ", instruction fetch: " << (instruction_fetch ? "Yes" : "No") << "\n"; - CPU::PANIC("See above message for more information", status); + m_apic = apic; +} - // Probably should never get here - return status; +/** + * @brief Handles a page fault + * + * @param status The cpu status + * @return The updated cpu status (won't return if it panics) + */ +cpu_status_t* InterruptManager::page_fault(system::cpu_status_t* status) { + + // Extract the information about the fault + bool present = (status->error_code & 0x1) != 0; + bool write = (status->error_code & 0x2) != 0; + bool user_mode = (status->error_code & 0x4) != 0; + bool reserved_write = (status->error_code & 0x8) != 0; + bool instruction_fetch = (status->error_code & 0x10) != 0; + uint64_t faulting_address; + asm volatile("movq %%cr2, %0" : "=r" (faulting_address)); + + // Try kill the process so the system doesn't die + cpu_status_t* can_avoid = CPU::prepare_for_panic(status); + if (can_avoid != nullptr) + return can_avoid; + + // Cant avoid it so halt the kernel + Logger::ERROR() << "Page Fault: (0x" << faulting_address << "): present: " << (present ? "Yes" : "No") + << ", write: " << (write ? "Yes" : "No") << ", user-mode: " << (user_mode ? "Yes" : "No") + << ", reserved write: " << (reserved_write ? "Yes" : "No") << ", instruction fetch: " + << (instruction_fetch ? "Yes" : "No") << "\n"; + CPU::PANIC("See above message for more information", status); + + // Probably should never get here + return status; } /** * @brief Handles a general protection fault + * * @param status The cpu status + * @return The updated cpu status (won't return if it panics) */ -cpu_status_t* InterruptManager::general_protection_fault(system::cpu_status_t *status) { - uint64_t error_code = status->error_code; +cpu_status_t* InterruptManager::general_protection_fault(system::cpu_status_t* status) { + + uint64_t error_code = status->error_code; - // Try to avoid the panic - cpu_status_t* can_avoid = CPU::prepare_for_panic(status); - if(can_avoid != nullptr) - return can_avoid; + // Try to avoid the panic + cpu_status_t* can_avoid = CPU::prepare_for_panic(status); + if (can_avoid != nullptr) + return can_avoid; - // Have to panic - Logger::ERROR() << "General Protection Fault: (0x" << status->rip << "): " << (error_code & 0x1 ? "Protection-Exception" : "Not a Protection Exception") << "\n"; - CPU::PANIC("See above message for more information", status); + // Have to panic + Logger::ERROR() << "General Protection Fault: (0x" << status->rip << "): " + << (error_code & 0x1 ? "Protection-Exception" : "Not a Protection Exception") << "\n"; + CPU::PANIC("See above message for more information", status); - // Probably should never get here - return status; + // Probably should never get here + return status; } /** @@ -347,7 +361,8 @@ cpu_status_t* InterruptManager::general_protection_fault(system::cpu_status_t *s * @return The APIC */ AdvancedProgrammableInterruptController* InterruptManager::active_apic() { - return m_apic; + + return m_apic; } /** @@ -355,6 +370,7 @@ AdvancedProgrammableInterruptController* InterruptManager::active_apic() { * * @return The active interrupt manager */ -InterruptManager *InterruptManager::active_interrupt_manager() { - return s_active_interrupt_manager; +InterruptManager* InterruptManager::active_interrupt_manager() { + + return s_active_interrupt_manager; } diff --git a/kernel/src/hardwarecommunication/pci.cpp b/kernel/src/hardwarecommunication/pci.cpp index ae15f1fd..a403e197 100644 --- a/kernel/src/hardwarecommunication/pci.cpp +++ b/kernel/src/hardwarecommunication/pci.cpp @@ -2,20 +2,21 @@ // Created by 98max on 12/10/2022. // #include -#include -#include #include +// Divers that need PCI descriptors +#include +#include +#include using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::hardwarecommunication; +using namespace MaxOS::memory; using namespace MaxOS::drivers; using namespace MaxOS::drivers::ethernet; using namespace MaxOS::drivers::video; -using namespace MaxOS::memory; - -///__DESCRIPTOR___ +using namespace MaxOS::drivers::disk; PeripheralComponentInterconnectDeviceDescriptor::PeripheralComponentInterconnectDeviceDescriptor() = default; @@ -23,56 +24,67 @@ PeripheralComponentInterconnectDeviceDescriptor::~PeripheralComponentInterconnec /** * @brief Get the type of the device + * * @return Type of the device as a string (or Unknown if the type is not known) */ string PeripheralComponentInterconnectDeviceDescriptor::get_type() const { - switch (class_id) - { - case 0x00: return (subclass_id == 0x01) ? "VGA" : "Legacy"; - case 0x01: - switch(subclass_id) - { - case 0x01: return "IDE interface"; - case 0x06: return "SATA controller"; - default: return "Storage"; - } - case 0x02: return "Network"; - case 0x03: return "Display"; - case 0x04: - switch(subclass_id) - { - case 0x00: return "Video"; - case 0x01: - case 0x03: return "Audio"; - default: return "Multimedia"; - } - case 0x06: - switch(subclass_id) - { - case 0x00: return "Host bridge"; - case 0x01: return "ISA bridge"; - case 0x04: return "PCI bridge"; - default: return "Bridge"; - } - case 0x07: - switch(subclass_id) - { - case 0x00: return "Serial controller"; - case 0x80: return "Communication controller"; - } - break; - case 0x0C: - switch(subclass_id) - { - case 0x03: return "USB"; - case 0x05: return "System Management Bus"; - } - break; - } - return "Unknown"; -} -///__CONTROLLER___ + switch (class_id) { + case 0x00: + return (subclass_id == 0x01) ? "VGA" : "Legacy"; + case 0x01: + switch (subclass_id) { + case 0x01: + return "IDE interface"; + case 0x06: + return "SATA controller"; + default: + return "Storage"; + } + case 0x02: + return "Network"; + case 0x03: + return "Display"; + case 0x04: + switch (subclass_id) { + case 0x00: + return "Video"; + case 0x01: + case 0x03: + return "Audio"; + default: + return "Multimedia"; + } + case 0x06: + switch (subclass_id) { + case 0x00: + return "Host bridge"; + case 0x01: + return "ISA bridge"; + case 0x04: + return "PCI bridge"; + default: + return "Bridge"; + } + case 0x07: + switch (subclass_id) { + case 0x00: + return "Serial controller"; + case 0x80: + return "Communication controller"; + } + break; + case 0x0C: + switch (subclass_id) { + case 0x03: + return "USB"; + case 0x05: + return "System Management Bus"; + } + break; + } + return "Unknown"; +} PeripheralComponentInterconnectController::PeripheralComponentInterconnectController() : m_data_port(0xCFC), @@ -90,22 +102,23 @@ PeripheralComponentInterconnectController::~PeripheralComponentInterconnectContr * @param bus Bus number * @param device Device number * @param function Function number - * @param registeroffset Register offset + * @param register_offset Register offset * @return data from the PCI Controller */ -uint32_t PeripheralComponentInterconnectController::read(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset) { +uint32_t PeripheralComponentInterconnectController::read(uint16_t bus, uint16_t device, uint16_t function, + uint32_t register_offset) { - // Calculate the id - uint32_t id = 0x1 << 31 - | ((bus & 0xFF) << 16) - | ((device & 0x1F) << 11) - | ((function & 0x07) << 8) - | (registeroffset & 0xFC); - m_command_port.write(id); + // Calculate the id + uint32_t id = 0x1 << 31 + | ((bus & 0xFF) << 16) + | ((device & 0x1F) << 11) + | ((function & 0x07) << 8) + | (register_offset & 0xFC); + m_command_port.write(id); - // read the data from the port - uint32_t result = m_data_port.read(); - return result >> (8* (registeroffset % 4)); + // Read the data from the port + uint32_t result = m_data_port.read(); + return result >> (8 * (register_offset % 4)); } @@ -115,21 +128,22 @@ uint32_t PeripheralComponentInterconnectController::read(uint16_t bus, uint16_t * @param bus Bus number * @param device Device number * @param function Function number - * @param registeroffset Register offset + * @param register_offset Register offset * @param value Value to write */ -void PeripheralComponentInterconnectController::write(uint16_t bus, uint16_t device, uint16_t function, uint32_t registeroffset, uint32_t value) { - - // Calculate the id - uint32_t id = 0x1 << 31 - | ((bus & 0xFF) << 16) - | ((device & 0x1F) << 11) - | ((function & 0x07) << 8) - | (registeroffset & 0xFC); - m_command_port.write(id); - - // write the data to the port - m_data_port.write(value); +void PeripheralComponentInterconnectController::write(uint16_t bus, uint16_t device, uint16_t function, + uint32_t register_offset, uint32_t value) { + + // Calculate the id + uint32_t id = 0x1 << 31 + | ((bus & 0xFF) << 16) + | ((device & 0x1F) << 11) + | ((function & 0x07) << 8) + | (register_offset & 0xFC); + m_command_port.write(id); + + // Write the data to the port + m_data_port.write(value); } /** @@ -141,7 +155,7 @@ void PeripheralComponentInterconnectController::write(uint16_t bus, uint16_t dev */ bool PeripheralComponentInterconnectController::device_has_functions(uint16_t bus, uint16_t device) { - return read(bus, device, 0, 0x0E) & (1<<7); + return read(bus, device, 0, 0x0E) & (1 << 7); } /** @@ -151,45 +165,44 @@ bool PeripheralComponentInterconnectController::device_has_functions(uint16_t bu * @param interrupt_manager Interrupt manager * @return Driver for the device */ -void PeripheralComponentInterconnectController::select_drivers(DriverSelectorEventHandler *handler) -{ - for (int bus = 0; bus < 8; ++bus) { - for (int device = 0; device < 32; ++device) { - - int numFunctions = (device_has_functions(bus, device)) ? 8 : 1; - - for (int function = 0; function < numFunctions; ++function) { - - // Get the device descriptor, if the vendor id is 0x0000 or 0xFFFF, the device is not present/ready - PeripheralComponentInterconnectDeviceDescriptor deviceDescriptor = get_device_descriptor(bus, device, function); - if(deviceDescriptor.vendor_id == 0x0000 || deviceDescriptor.vendor_id == 0x0001 || deviceDescriptor.vendor_id == 0xFFFF) - continue; - - - // Get port number - for(int barNum = 5; barNum >= 0; barNum--){ - BaseAddressRegister bar = get_base_address_register(bus, device, function, barNum); - if(bar.address && (bar.type == BaseAddressRegisterType::InputOutput)) - deviceDescriptor.port_base = (uint64_t )bar.address; - } - - // write to the debug stream - Logger::DEBUG() << "DEVICE FOUND: " << deviceDescriptor.get_type() << " - "; - - // Select the driver and print information about the device - Driver* driver = get_driver(deviceDescriptor); - if(driver != nullptr){ - handler->on_driver_selected(driver); - Logger::Out() << driver->vendor_name() << " " << driver->device_name(); - }else{ - list_known_device(deviceDescriptor); - } - - // New line - Logger::Out() << "\n"; - } - } - } +void PeripheralComponentInterconnectController::select_drivers(DriverSelectorEventHandler* handler) { + + for (int bus = 0; bus < 8; ++bus) { + for (int device = 0; device < 32; ++device) { + + int numFunctions = (device_has_functions(bus, device)) ? 8 : 1; + + for (int function = 0; function < numFunctions; ++function) { + + // Get the device descriptor, if the vendor id is 0x0000 or 0xFFFF, the device is not present/ready + PeripheralComponentInterconnectDeviceDescriptor deviceDescriptor = get_device_descriptor(bus, device, + function); + if (deviceDescriptor.vendor_id == 0x0000 || deviceDescriptor.vendor_id == 0x0001 || + deviceDescriptor.vendor_id == 0xFFFF) + continue; + + // Get the earliest port number + for (int barNum = 5; barNum >= 0; barNum--) { + BaseAddressRegister bar = get_base_address_register(bus, device, function, barNum); + if (bar.address && (bar.type == BaseAddressRegisterType::InputOutput)) + deviceDescriptor.port_base = (uint64_t) bar.address; + } + + Logger::DEBUG() << "DEVICE FOUND: " << deviceDescriptor.get_type() << " - "; + + // Select the driver and print information about the device + Driver* driver = get_driver(deviceDescriptor); + if (driver != nullptr) { + handler->on_driver_selected(driver); + Logger::Out() << driver->vendor_name() << " " << driver->device_name(); + } else { + list_known_device(deviceDescriptor); + } + + Logger::Out() << "\n"; + } + } + } } /** @@ -201,23 +214,24 @@ void PeripheralComponentInterconnectController::select_drivers(DriverSelectorEve * @return Device descriptor */ PeripheralComponentInterconnectDeviceDescriptor PeripheralComponentInterconnectController::get_device_descriptor(uint16_t bus, uint16_t device, uint16_t function) { - PeripheralComponentInterconnectDeviceDescriptor result; - result.bus = bus; - result.device = device; - result.function = function; + PeripheralComponentInterconnectDeviceDescriptor result; + + result.bus = bus; + result.device = device; + result.function = function; - result.vendor_id = read(bus, device, function, 0x00); - result.device_id = read(bus, device, function, 0x02); + result.vendor_id = read(bus, device, function, 0x00); + result.device_id = read(bus, device, function, 0x02); - result.class_id = read(bus, device, function, 0x0B); - result.subclass_id = read(bus, device, function, 0x0A); - result.interface_id = read(bus, device, function, 0x09); + result.class_id = read(bus, device, function, 0x0B); + result.subclass_id = read(bus, device, function, 0x0A); + result.interface_id = read(bus, device, function, 0x09); - result.revision = read(bus, device, function, 0x8); - result.interrupt = read(bus, device, function, 0x3C); + result.revision = read(bus, device, function, 0x8); + result.interrupt = read(bus, device, function, 0x3C); - return result; + return result; } /** @@ -229,197 +243,177 @@ PeripheralComponentInterconnectDeviceDescriptor PeripheralComponentInterconnectC */ Driver* PeripheralComponentInterconnectController::get_driver(PeripheralComponentInterconnectDeviceDescriptor dev) { - switch (dev.vendor_id) - { - case 0x1022: //AMD - { - switch (dev.device_id) - { - case 0x2000: - { - return new AMD_AM79C973(&dev); - - } - default: - break; - } - break; - } - case 0x8086: //Intel - { - switch (dev.device_id) - { - case 0x100E: //i217 (Ethernet Controller) - { - return new intel_i217(&dev); - } - default: - break; - } - break; - }//End Intel - } - - //If there is no driver for the particular device, go into generic devices - switch (dev.class_id) - { - case 0x03: //Graphics - { - - switch (dev.subclass_id) - { - case 0x00: //VGA - { - return new VideoGraphicsArray(); - } - } - break; - } - } - - return nullptr; + switch (dev.vendor_id) { + case 0x1022: //AMD + { + switch (dev.device_id) { + case 0x2000: { + return new AMD_AM79C973(&dev); + + } + default: + break; + } + break; + } + case 0x8086: //Intel + { + switch (dev.device_id) { + + case 0x100E: //i217 (Ethernet Controller) + { + return new intel_i217(&dev); + } + + case 0x7010: // PIIX4 (IDE Controller) + { + return new IntegratedDriveElectronicsController(&dev); + } + + default: + break; + } + break; + }//End Intel + } + + //If there is no driver for the particular device, go into generic devices + switch (dev.class_id) { + case 0x03: //Graphics + { + + switch (dev.subclass_id) { + case 0x00: //VGA + { + return new VideoGraphicsArray(); + } + } + break; + } + } + + return nullptr; } -void PeripheralComponentInterconnectController::list_known_device(const PeripheralComponentInterconnectDeviceDescriptor& dev) { - switch (dev.vendor_id) - { - case 0x1022: - { - // The vendor is AMD - Logger::Out() << "AMD "; - - // List the device - switch (dev.device_id) - { - default: - Logger::Out() << "0x%x" << dev.device_id; - break; - } - break; - } - - case 0x106B: - { - // The vendor is Apple - Logger::Out() << "Apple "; - - // List the device - switch (dev.device_id) - { - case 0x003F: - { - Logger::Out() << "KeyLargo/Intrepid USB"; - break; - } - - default: - Logger::Out() << "0x%x" << dev.device_id; - break; - } - break; - } - - case 1234: - { - // The vendor is QEMU - Logger::Out() << "QEMU "; - - // List the device - switch (dev.device_id) - { - - case 0x1111: - { - Logger::Out() << "Virtual Video Controller"; - break; - } - } - break; - } - - case 0x8086: - { - // The vendor is Intel - Logger::Out() << "Intel "; - - // List the device - switch (dev.device_id) - { - - case 0x1237: - { - Logger::Out() << "440FX"; - break; - } - - case 0x2415: - { - Logger::Out() << "AC'97"; - break; - } - - case 0x7000: - { - Logger::Out() << "PIIX3"; - break; - - } - - case 0x7010: - { - Logger::Out() << "PIIX4"; - break; - - } - - case 0x7111: - { - Logger::Out() << "PIIX3 ACPI"; - break; - } - - case 0x7113: - { - Logger::Out() << "PIIX4 ACPI"; - break; - } - - default: - Logger::Out() << "0x%x" << dev.device_id; - break; - - } - break; - } - - case 0x80EE: { - - // The vendor is VirtualBox - Logger::Out() << "VirtualBox "; - - // List the device - switch (dev.device_id) { - - case 0xBEEF: { - Logger::Out() << "Graphics Adapter"; - break; - } - - case 0xCAFE: { - Logger::Out() << "Guest Service"; - break; - } - } - break; - } - - // Unknown - default: - Logger::Out() << "Unknown (0x" << dev.vendor_id << ":0x" << dev.device_id << ")"; - break; - - } +void PeripheralComponentInterconnectController::list_known_device( + const PeripheralComponentInterconnectDeviceDescriptor& dev) { + + switch (dev.vendor_id) { + case 0x1022: { + // The vendor is AMD + Logger::Out() << "AMD "; + + // List the device + switch (dev.device_id) { + default: + Logger::Out() << "0x%x" << dev.device_id; + break; + } + break; + } + + case 0x106B: { + // The vendor is Apple + Logger::Out() << "Apple "; + + // List the device + switch (dev.device_id) { + case 0x003F: { + Logger::Out() << "KeyLargo/Intrepid USB"; + break; + } + + default: + Logger::Out() << "0x%x" << dev.device_id; + break; + } + break; + } + + case 1234: { + // The vendor is QEMU + Logger::Out() << "QEMU "; + + // List the device + switch (dev.device_id) { + + case 0x1111: { + Logger::Out() << "Virtual Video Controller"; + break; + } + } + break; + } + + case 0x8086: { + // The vendor is Intel + Logger::Out() << "Intel "; + + // List the device + switch (dev.device_id) { + + case 0x1237: { + Logger::Out() << "440FX"; + break; + } + + case 0x2415: { + Logger::Out() << "AC'97"; + break; + } + + case 0x7000: { + Logger::Out() << "PIIX3"; + break; + + } + + case 0x7111: { + Logger::Out() << "PIIX3 ACPI"; + break; + } + + case 0x7113: { + Logger::Out() << "PIIX4 ACPI"; + break; + } + + default: + Logger::Out() << "0x%x" << dev.device_id; + break; + + } + break; + } + + case 0x80EE: { + + // The vendor is VirtualBox + Logger::Out() << "VirtualBox "; + + // List the device + switch (dev.device_id) { + + case 0xBEEF: { + Logger::Out() << "Graphics Adapter"; + break; + } + + case 0xCAFE: { + Logger::Out() << "Guest Service"; + break; + } + } + break; + } + + // Unknown + default: + Logger::Out() << "Unknown (0x" << dev.vendor_id << ":0x" << dev.device_id << ")"; + break; + + } } /** @@ -433,25 +427,25 @@ void PeripheralComponentInterconnectController::list_known_device(const Peripher */ BaseAddressRegister PeripheralComponentInterconnectController::get_base_address_register(uint16_t bus, uint16_t device, uint16_t function, uint16_t bar) { - BaseAddressRegister result{}; + BaseAddressRegister result{}; - // only types 0x00 (normal devices) and 0x01 (PCI-to-PCI bridges) are supported: - uint32_t headerType = read(bus, device, function, 0x0E); - if (headerType & 0x3F) - return result; + // Only types 0x00 (normal devices) and 0x01 (PCI-to-PCI bridges) are supported: + uint32_t headerType = read(bus, device, function, 0x0E); + if (headerType & 0x3F) + return result; - // read the base address register - uint64_t bar_value = read(bus, device, function, 0x10 + 4 * bar); - result.type = (bar_value & 0x1) ? BaseAddressRegisterType::InputOutput : BaseAddressRegisterType::MemoryMapped; - result.address = (uint8_t*) (bar_value & ~0xF); + // read the base address register + uint64_t bar_value = read(bus, device, function, 0x10 + 4 * bar); + result.type = (bar_value & 0x1) ? BaseAddressRegisterType::InputOutput : BaseAddressRegisterType::MemoryMapped; + result.address = (uint8_t*) (bar_value & ~0xF); - // read the size of the base address register - write(bus, device, function, 0x10 + 4 * bar, 0xFFFFFFF0 | (int)result.type); - result.size = read(bus, device, function, 0x10 + 4 * bar); - result.size = (~result.size | 0xF) + 1; + // Read the size of the base address register + write(bus, device, function, 0x10 + 4 * bar, 0xFFFFFFF0 | (int) result.type); + result.size = read(bus, device, function, 0x10 + 4 * bar); + result.size = (~result.size | 0xF) + 1; - // Restore the original value of the base address register - write(bus, device, function, 0x10 + 4 * bar, bar_value); + // Restore the original value of the base address register + write(bus, device, function, 0x10 + 4 * bar, bar_value); - return result; + return result; } \ No newline at end of file diff --git a/kernel/src/hardwarecommunication/port.cpp b/kernel/src/hardwarecommunication/port.cpp index c5dbe5be..1ad2f427 100644 --- a/kernel/src/hardwarecommunication/port.cpp +++ b/kernel/src/hardwarecommunication/port.cpp @@ -11,11 +11,10 @@ Port::Port(uint16_t port_number) { } -Port::~Port()= default; +Port::~Port() = default; Port8Bit::Port8Bit(uint16_t port_number) -: Port(port_number) -{ +: Port(port_number) { } Port8Bit::~Port8Bit() = default; @@ -25,8 +24,9 @@ Port8Bit::~Port8Bit() = default; * * @param data the byte to write */ -void Port8Bit::write(uint8_t data){ - asm volatile("outb %0, %1" : : "a" (data), "Nd" (m_port_number)); +void Port8Bit::write(uint8_t data) { + + asm volatile("outb %0, %1" : : "a" (data), "Nd" (m_port_number)); } /** @@ -34,15 +34,15 @@ void Port8Bit::write(uint8_t data){ * * @return the byte read */ -uint8_t Port8Bit::read(){ - uint8_t result; - asm volatile("inb %1, %0" : "=a" (result) : "Nd" (m_port_number)); - return result; +uint8_t Port8Bit::read() { + + uint8_t result; + asm volatile("inb %1, %0" : "=a" (result) : "Nd" (m_port_number)); + return result; } Port8BitSlow::Port8BitSlow(uint16_t port_number) -: Port8Bit(port_number) -{ +: Port8Bit(port_number) { } Port8BitSlow::~Port8BitSlow() = default; @@ -52,14 +52,14 @@ Port8BitSlow::~Port8BitSlow() = default; * * @param data the byte to write */ -void Port8BitSlow::write(uint8_t data){ - asm volatile("outb %0, %1\njmp 1f\n1: jmp 1f\n1:" : : "a" (data), "Nd" (m_port_number)); +void Port8BitSlow::write(uint8_t data) { + + asm volatile("outb %0, %1\njmp 1f\n1: jmp 1f\n1:" : : "a" (data), "Nd" (m_port_number)); } Port16Bit::Port16Bit(uint16_t port_number) -: Port(port_number) -{ +: Port(port_number) { } Port16Bit::~Port16Bit() = default; @@ -69,8 +69,9 @@ Port16Bit::~Port16Bit() = default; * * @param data the word to write */ -void Port16Bit::write(uint16_t data){ - asm volatile("outw %0, %1" : : "a" (data), "Nd" (m_port_number)); +void Port16Bit::write(uint16_t data) { + + asm volatile("outw %0, %1" : : "a" (data), "Nd" (m_port_number)); } /** @@ -78,16 +79,16 @@ void Port16Bit::write(uint16_t data){ * * @return the word read */ -uint16_t Port16Bit::read(){ - uint16_t result; - asm volatile("inw %1, %0" : "=a" (result) : "Nd" (m_port_number)); - return result; +uint16_t Port16Bit::read() { + + uint16_t result; + asm volatile("inw %1, %0" : "=a" (result) : "Nd" (m_port_number)); + return result; } Port32Bit::Port32Bit(uint16_t port_number) -: Port(port_number) -{ +: Port(port_number) { } Port32Bit::~Port32Bit() = default; @@ -97,8 +98,9 @@ Port32Bit::~Port32Bit() = default; * * @param data the double word to write */ -void Port32Bit::write(uint32_t data){ - asm volatile("outl %0, %1" : : "a" (data), "Nd" (m_port_number)); +void Port32Bit::write(uint32_t data) { + + asm volatile("outl %0, %1" : : "a" (data), "Nd" (m_port_number)); } /** @@ -106,8 +108,9 @@ void Port32Bit::write(uint32_t data){ * * @return the double word read */ -uint32_t Port32Bit::read(){ - uint32_t result; - asm volatile("inl %1, %0" : "=a" (result) : "Nd" (m_port_number)); - return result; +uint32_t Port32Bit::read() { + + uint32_t result; + asm volatile("inl %1, %0" : "=a" (result) : "Nd" (m_port_number)); + return result; } \ No newline at end of file diff --git a/kernel/src/kernel.cpp b/kernel/src/kernel.cpp index 03ab1523..f2c9a988 100644 --- a/kernel/src/kernel.cpp +++ b/kernel/src/kernel.cpp @@ -1,35 +1,19 @@ -//Common #include #include - -//Hardware com #include -#include -#include - -//Drivers #include #include #include -#include -#include #include - -//GUI #include - -//PROCESS #include - -//SYSTEM #include #include #include - -//MEMORY #include #include - +#include +#include using namespace MaxOS; using namespace MaxOS::common; @@ -43,53 +27,63 @@ using namespace MaxOS::gui; using namespace MaxOS::processes; using namespace MaxOS::system; using namespace MaxOS::memory; +using namespace MaxOS::filesystem; extern "C" void call_constructors(); -extern "C" [[noreturn]] void kernel_main(unsigned long addr, unsigned long magic) -{ - call_constructors(); - - // Initialise the logger - Logger logger; - SerialConsole serial_console(&logger); - Logger::INFO() << "MaxOS Booted Successfully \n"; - - Logger::HEADER() << "Stage {1}: System Initialisation\n"; - Multiboot multiboot(addr, magic); - GlobalDescriptorTable gdt; - InterruptManager interrupts; - - Logger::HEADER() << "Stage {1.1}: Memory Initialisation\n"; - PhysicalMemoryManager pmm(&multiboot); - VirtualMemoryManager vmm; - MemoryManager memoryManager(&vmm); - - Logger::HEADER() << "Stage {1.2}: Console Initialisation\n"; - VideoElectronicsStandardsAssociation vesa(multiboot.framebuffer()); - VESABootConsole console(&vesa); - - Logger::HEADER() << "Stage {2}: Hardware Initialisation\n"; - DriverManager driver_manager; - CPU cpu(&gdt, &multiboot); - Clock kernel_clock(cpu.apic, 1); - driver_manager.add_driver(&kernel_clock); - driver_manager.find_drivers(); - uint32_t reset_wait_time = driver_manager.reset_devices(); - - Logger::HEADER() << "Stage {3}: Device Finalisation\n"; - interrupts.activate(); - kernel_clock.calibrate(); - kernel_clock.delay(reset_wait_time); - driver_manager.initialise_drivers(); - driver_manager.activate_drivers(); - - Logger::HEADER() << "Stage {4}: System Finalisation\n"; - Scheduler scheduler(multiboot); - SyscallManager syscalls; - console.finish(); - scheduler.activate(); - - // Idle loop (read Idle.md) - while (true) - asm("nop"); -} \ No newline at end of file +extern "C" [[noreturn]] void kernel_main(unsigned long addr, unsigned long magic) { + + call_constructors(); + + // Initialise the logger + Logger logger; + SerialConsole serial_console(&logger); + Logger::INFO() << "MaxOS Booted Successfully \n"; + + Logger::HEADER() << "Stage {1}: System Initialisation\n"; + Multiboot multiboot(addr, magic); + GlobalDescriptorTable gdt; + InterruptManager interrupts; + + Logger::HEADER() << "Stage {1.1}: Memory Initialisation\n"; + PhysicalMemoryManager pmm(&multiboot); + VirtualMemoryManager vmm; + MemoryManager memoryManager(&vmm); + + Logger::HEADER() << "Stage {1.2}: Console Initialisation\n"; + VideoElectronicsStandardsAssociation vesa(multiboot.framebuffer()); + VESABootConsole console(&vesa); + + Logger::HEADER() << "Stage {2}: Hardware Initialisation\n"; + VirtualFileSystem vfs; + CPU cpu(&gdt, &multiboot); + Clock kernel_clock(cpu.apic, 1); + DriverManager driver_manager; + driver_manager.add_driver(&kernel_clock); + driver_manager.find_drivers(); + uint32_t reset_wait_time = driver_manager.reset_devices(); + + Logger::HEADER() << "Stage {3}: Device Finalisation\n"; + interrupts.activate(); + kernel_clock.calibrate(); + kernel_clock.delay(reset_wait_time); + driver_manager.initialise_drivers(); + driver_manager.activate_drivers(); + + Logger::HEADER() << "Stage {4}: System Finalisation\n"; + Scheduler scheduler(multiboot); + VFSResourceRegistry vfs_registry(&vfs); + SyscallManager syscalls; + console.finish(); + scheduler.activate(); + + // Idle loop (read Idle.md) + while (true) + asm("nop"); +} + +// TODO: +// - SMP +// - Test suite of common functions & other statics (paths) +// - Class & Struct docstrings +// - Logo on fail in center +// - Sanitize syscall input \ No newline at end of file diff --git a/kernel/src/linker.ld b/kernel/src/linker.ld index df5865df..9ca02006 100644 --- a/kernel/src/linker.ld +++ b/kernel/src/linker.ld @@ -8,6 +8,7 @@ SECTIONS { .multiboot_header : { /* Be sure that multiboot header is at the beginning */ + _multiboot_header_start = .; *(.multiboot_header) } diff --git a/kernel/src/memory/memoryIO.cpp b/kernel/src/memory/memoryIO.cpp index 97224c3a..e77c386c 100644 --- a/kernel/src/memory/memoryIO.cpp +++ b/kernel/src/memory/memoryIO.cpp @@ -26,8 +26,9 @@ MemIO8Bit::~MemIO8Bit() = default; * * @param data the data to write */ -void MemIO8Bit::write(uint8_t data){ - (*((volatile uint8_t*)(m_address)))=(data); +void MemIO8Bit::write(uint8_t data) { + + (*((volatile uint8_t*) (m_address))) = (data); } /** @@ -35,8 +36,9 @@ void MemIO8Bit::write(uint8_t data){ * * @return the data read */ -uint8_t MemIO8Bit::read(){ - return *((volatile uint8_t*)(m_address)); +uint8_t MemIO8Bit::read() { + + return *((volatile uint8_t*) (m_address)); } MemIO16Bit::MemIO16Bit(uintptr_t address) @@ -51,8 +53,9 @@ MemIO16Bit::~MemIO16Bit() = default; * * @param data the data to write */ -void MemIO16Bit::write(uint16_t data){ - (*((volatile uint16_t*)(m_address)))=(data); +void MemIO16Bit::write(uint16_t data) { + + (*((volatile uint16_t*) (m_address))) = (data); } /** @@ -60,12 +63,13 @@ void MemIO16Bit::write(uint16_t data){ * * @return the data read */ -uint16_t MemIO16Bit::read(){ - return *((volatile uint16_t*)(m_address)); +uint16_t MemIO16Bit::read() { + + return *((volatile uint16_t*) (m_address)); } MemIO32Bit::MemIO32Bit(uintptr_t address) - : MemIO(address) +: MemIO(address) { } @@ -76,8 +80,9 @@ MemIO32Bit::~MemIO32Bit() = default; * * @param data the data to write */ -void MemIO32Bit::write(uint32_t data){ - (*((volatile uint32_t*)(m_address)))=(data); +void MemIO32Bit::write(uint32_t data) { + + (*((volatile uint32_t*) (m_address))) = (data); } /** @@ -85,8 +90,9 @@ void MemIO32Bit::write(uint32_t data){ * * @return the data read */ -uint32_t MemIO32Bit::read(){ - return *((volatile uint32_t*)(m_address)); +uint32_t MemIO32Bit::read() { + + return *((volatile uint32_t*) (m_address)); } MemIO64Bit::MemIO64Bit(uintptr_t address) @@ -101,8 +107,9 @@ MemIO64Bit::~MemIO64Bit() = default; * * @param data the data to write */ -void MemIO64Bit::write(uint64_t data){ - (*((volatile uint64_t*)(m_address)))=(data); +void MemIO64Bit::write(uint64_t data) { + + (*((volatile uint64_t*) (m_address))) = (data); } /** @@ -110,8 +117,9 @@ void MemIO64Bit::write(uint64_t data){ * * @return the data read */ -uint64_t MemIO64Bit::read(){ - return *((volatile uint64_t*)(m_address)); +uint64_t MemIO64Bit::read() { + + return *((volatile uint64_t*) (m_address)); } @@ -127,29 +135,28 @@ uint64_t MemIO64Bit::read(){ */ void* memcpy(void* destination, const void* source, uint64_t num) { - // Make sure the source and destination are not the same - if (destination == source) - return destination; + // Make sure the source and destination are not the same + if (destination == source) + return destination; - // Make sure they exist - if (destination == nullptr || source == nullptr) - return destination; + // Make sure they exist + if (destination == nullptr || source == nullptr) + return destination; - // Get the source and destination - auto* dst = (unsigned char*) destination; - const auto* src = (const unsigned char*) source; + // Get the source and destination + auto* dst = (unsigned char*) destination; + const auto* src = (const unsigned char*) source; - // Copy the data - for (size_t i = 0; i < num; i++) - dst[i] = src[i]; + // Copy the data + for (size_t i = 0; i < num; i++) + dst[i] = src[i]; - // Usefully for easier code writing - return destination; + // Usefully for easier code writing + return destination; } /** * @brief Fills a block of memory with a specified value - * * @param ptr The pointer to the block of memory * @param value The value to fill the block of memory with @@ -158,14 +165,14 @@ void* memcpy(void* destination, const void* source, uint64_t num) { */ void* memset(void* ptr, uint32_t value, uint64_t num) { - // Make sure the pointer exists - if (ptr == nullptr) - return ptr; + // Make sure the pointer exists + if (ptr == nullptr) + return ptr; - auto* dst = (unsigned char*) ptr; - for (size_t i = 0; i < num; i++) - dst[i] = (unsigned char) value; - return ptr; + auto* dst = (unsigned char*) ptr; + for (size_t i = 0; i < num; i++) + dst[i] = (unsigned char) value; + return ptr; } /** @@ -178,24 +185,24 @@ void* memset(void* ptr, uint32_t value, uint64_t num) { */ void* memmove(void* destination, const void* source, uint64_t num) { - // Make sure the source and destination are not the same - if (destination == source) - return destination; + // Make sure the source and destination are not the same + if (destination == source) + return destination; - // Make sure they exist - if (destination == nullptr || source == nullptr) - return destination; + // Make sure they exist + if (destination == nullptr || source == nullptr) + return destination; - auto* dst = (unsigned char*) destination; - const auto* src = (const unsigned char*) source; - if (dst < src) { - for (size_t i = 0; i < num; i++) - dst[i] = src[i]; - } else { - for (size_t i = num; i != 0; i--) - dst[i-1] = src[i-1]; - } - return destination; + auto* dst = (unsigned char*) destination; + const auto* src = (const unsigned char*) source; + if (dst < src) { + for (size_t i = 0; i < num; i++) + dst[i] = src[i]; + } else { + for (size_t i = num; i != 0; i--) + dst[i - 1] = src[i - 1]; + } + return destination; } /** @@ -208,17 +215,17 @@ void* memmove(void* destination, const void* source, uint64_t num) { */ int memcmp(const void* ptr1, const void* ptr2, uint64_t num) { - // Make sure the pointers exist - if (ptr1 == nullptr || ptr2 == nullptr) - return 0; - - const auto *p1 = (const unsigned char *)ptr1; - const auto *p2 = (const unsigned char *)ptr2; - for (size_t i = 0; i < num; i++) { - if (p1[i] < p2[i]) - return -1; - if (p1[i] > p2[i]) - return 1; - } - return 0; + // Make sure the pointers exist + if (ptr1 == nullptr || ptr2 == nullptr) + return 0; + + const auto* p1 = (const unsigned char*) ptr1; + const auto* p2 = (const unsigned char*) ptr2; + for (size_t i = 0; i < num; i++) { + if (p1[i] < p2[i]) + return -1; + if (p1[i] > p2[i]) + return 1; + } + return 0; } \ No newline at end of file diff --git a/kernel/src/memory/memorymanagement.cpp b/kernel/src/memory/memorymanagement.cpp index e957a4b9..362c563b 100644 --- a/kernel/src/memory/memorymanagement.cpp +++ b/kernel/src/memory/memorymanagement.cpp @@ -14,78 +14,70 @@ MemoryManager::MemoryManager(VirtualMemoryManager* vmm) : m_virtual_memory_manager(vmm) { - // Create the VMM if not provided - if(m_virtual_memory_manager == nullptr) - m_virtual_memory_manager = new VirtualMemoryManager(); + // Create the VMM if not provided + if (m_virtual_memory_manager == nullptr) + m_virtual_memory_manager = new VirtualMemoryManager(); - // Enable the memory manager - switch_active_memory_manager(this); + // Enable the memory manager + switch_active_memory_manager(this); - // Get the first chunk of memory - this -> m_first_memory_chunk = (MemoryChunk*)m_virtual_memory_manager->allocate(PhysicalMemoryManager::s_page_size + sizeof(MemoryChunk), 0); + // Setup the first chunk of memory + this->m_first_memory_chunk = (MemoryChunk*) m_virtual_memory_manager->allocate(PhysicalMemoryManager::s_page_size + sizeof(MemoryChunk), 0); + m_first_memory_chunk->allocated = false; + m_first_memory_chunk->prev = nullptr; + m_first_memory_chunk->next = nullptr; + m_first_memory_chunk->size = PhysicalMemoryManager::s_page_size - sizeof(MemoryChunk); + m_last_memory_chunk = m_first_memory_chunk; - // Set the first chunk's properties - m_first_memory_chunk-> allocated = false; - m_first_memory_chunk-> prev = nullptr; - m_first_memory_chunk-> next = nullptr; - m_first_memory_chunk-> size = PhysicalMemoryManager::s_page_size - sizeof(MemoryChunk); - - // Set the last chunk to the first chunk - m_last_memory_chunk = m_first_memory_chunk; - - // First memory manager is the kernel memory manager - if(s_kernel_memory_manager == nullptr) - s_kernel_memory_manager = this; + // First memory manager is the kernel memory manager + if (s_kernel_memory_manager == nullptr) + s_kernel_memory_manager = this; } MemoryManager::~MemoryManager() { + // Free the VMM (if this is not the kernel memory manager) + if (m_virtual_memory_manager != nullptr && s_current_memory_manager != s_kernel_memory_manager) + delete m_virtual_memory_manager; - // Free the VMM (if this is not the kernel memory manager) - if(m_virtual_memory_manager != nullptr && s_current_memory_manager != s_kernel_memory_manager) - delete m_virtual_memory_manager; - - // Check if the current memory manager is this one - if(s_kernel_memory_manager == this) - s_kernel_memory_manager = nullptr; - - // Check if the current memory manager is this one - if(s_current_memory_manager == this) - s_current_memory_manager = nullptr; + // Remove the kernel reference to this + if (s_kernel_memory_manager == this) + s_kernel_memory_manager = nullptr; + // Remove the active reference to this + if (s_current_memory_manager == this) + s_current_memory_manager = nullptr; } /** - * @brief Allocates a block of memory using the current memory manager + * @brief Allocates a block of memory in the current USERSPACE heap * * @param size size of the block * @return a pointer to the block, 0 if no block is available or no memory manager is set */ void* MemoryManager::malloc(size_t size) { - // Make sure there is somthing to do the allocation - if(s_current_memory_manager == nullptr) - return nullptr; - - return s_current_memory_manager->handle_malloc(size); + // Make sure there is somthing to do the allocation + if (s_current_memory_manager == nullptr) + return nullptr; + return s_current_memory_manager->handle_malloc(size); } /** - * @brief Allocates a block of memory using the kernel memory manager + * @brief Allocates a block of memory in the KERNEL space * * @param size The size of the block * @return The pointer to the block, or nullptr if no block is available */ -void *MemoryManager::kmalloc(size_t size) { - - // Make sure there is a kernel memory manager - if(s_kernel_memory_manager == nullptr) - return nullptr; +void* MemoryManager::kmalloc(size_t size) { - return s_kernel_memory_manager->handle_malloc(size); + // Make sure there is a kernel memory manager + if (s_kernel_memory_manager == nullptr) + return nullptr; + return s_kernel_memory_manager->handle_malloc(size); } /** @@ -94,55 +86,57 @@ void *MemoryManager::kmalloc(size_t size) { * @param size The size of the block to allocate * @return A pointer to the block, or nullptr if no block is available */ -void *MemoryManager::handle_malloc(size_t size) { - MemoryChunk* result = nullptr; - - // Don't allocate a block of size 0 - if(size == 0) - return nullptr; - - // Size must include the size of the chunk and be aligned - size = align(size + sizeof(MemoryChunk)); - - // Find the next free chunk that is big enough - for (MemoryChunk* chunk = m_first_memory_chunk; chunk != nullptr && result == nullptr; chunk = chunk->next) { - if(chunk -> size > size && !chunk -> allocated) - result = chunk; - } - - // If there is no free chunk then expand the heap - if(result == nullptr) - result = expand_heap(size); - - // If there is not enough space to create a new chunk then just allocate the current chunk - if(result -> size < size + sizeof(MemoryChunk) + 1) { - result->allocated = true; - return (void *)(((size_t)result) + sizeof(MemoryChunk)); - } - - // Create a new chunk after the current one - auto* temp = (MemoryChunk*)((size_t)result + sizeof(MemoryChunk) + size); - - // Set the new chunk's properties and insert it into the linked list - temp -> allocated = false; - temp -> size = result->size - size - sizeof(MemoryChunk); - temp -> prev = result; - temp -> next = result -> next; - - // If there is a chunk after the current one then set it's previous to the new chunk - if(temp -> next != nullptr) - temp -> next -> prev = temp; - - // Current chunk is now allocated and is pointing to the new chunk - result->size = size; - result -> allocated = true; - result->next = temp; - - // Update the last memory chunk if necessary - if(result == m_last_memory_chunk) - m_last_memory_chunk = temp; - - return (void*)(((size_t)result) + sizeof(MemoryChunk)); +void* MemoryManager::handle_malloc(size_t size) { + + MemoryChunk* result = nullptr; + + // Nothing to allocate + if (size == 0) + return nullptr; + + // Add room to store the chunk information + size = align(size + sizeof(MemoryChunk)); + + // Find the next free chunk that is big enough + for (MemoryChunk* chunk = m_first_memory_chunk; chunk != nullptr && result == nullptr; chunk = chunk->next) { + if (chunk->size > size && !chunk->allocated) + result = chunk; + } + + // If there is no free chunk then make more room + if (result == nullptr) + result = expand_heap(size); + + // If there is not left over space to store extra chunks there is no need to split the chunk + if (result->size < size + sizeof(MemoryChunk) + 1) { + result->allocated = true; + void* p = (void*) (((size_t) result) + sizeof(MemoryChunk)); + return p; + } + + // Split the chunk into: what was requested + free overflow space for future allocates + // - This prevents waste in the event that a big free chunk was found but the requested size would only use a portion of that + auto* extra = (MemoryChunk*) ((size_t) result + sizeof(MemoryChunk) + size); + extra->allocated = false; + extra->size = result->size - size - sizeof(MemoryChunk); + extra->prev = result; + + // Add to the linked list + extra->next = result->next; + if (extra->next != nullptr) + extra->next->prev = extra; + + // Requested chunk is now allocated exactly to the size requested and points to the free (split) block of memory that + // it did not use + result->size = size; + result->allocated = true; + result->next = extra; + + // Update the last memory chunk if necessary + if (result == m_last_memory_chunk) + m_last_memory_chunk = extra; + + return (void*) (((size_t) result) + sizeof(MemoryChunk)); } /** @@ -150,14 +144,13 @@ void *MemoryManager::handle_malloc(size_t size) { * * @param pointer The pointer to the block */ -void MemoryManager::free(void *pointer) { - - // Make sure there is a memory manager - if(s_current_memory_manager == nullptr) - return; +void MemoryManager::free(void* pointer) { - s_current_memory_manager->handle_free(pointer); + // Make sure there is a memory manager + if (s_current_memory_manager == nullptr) + return; + s_current_memory_manager->handle_free(pointer); } /** @@ -165,14 +158,13 @@ void MemoryManager::free(void *pointer) { * * @param pointer The pointer to the block */ -void MemoryManager::kfree(void *pointer) { +void MemoryManager::kfree(void* pointer) { - // Make sure there is a kernel memory manager - if(s_kernel_memory_manager == nullptr) - return; - - s_kernel_memory_manager->handle_free(pointer); + // Make sure there is a kernel memory manager + if (s_kernel_memory_manager == nullptr) + return; + s_kernel_memory_manager->handle_free(pointer); } /** @@ -180,49 +172,49 @@ void MemoryManager::kfree(void *pointer) { * * @param pointer A pointer to the block */ -void MemoryManager::handle_free(void *pointer) { - +void MemoryManager::handle_free(void* pointer) { - // If nothing to free then return - if(pointer == nullptr) - return; + // Cant free unallocated memory + if (pointer == nullptr) + return; - // If block is not in the memory manager's range then return - if((uint64_t ) pointer < (uint64_t ) m_first_memory_chunk || (uint64_t ) pointer > (uint64_t ) m_last_memory_chunk) - return; + // Check bounds + if ((uint64_t) pointer < (uint64_t) m_first_memory_chunk || (uint64_t) pointer > (uint64_t) m_last_memory_chunk) + return; - // Create a new free chunk - auto* chunk = (MemoryChunk*)((size_t)pointer - sizeof(MemoryChunk)); - chunk -> allocated = false; + // Get the chunk information from the pointer + auto* chunk = (MemoryChunk*) ((size_t) pointer - sizeof(MemoryChunk)); + chunk->allocated = false; - // If there is a free chunk before this chunk then merge them - if(chunk -> prev != nullptr && !chunk -> prev -> allocated){ + // If there is a free chunk before this chunk then merge them + if (chunk->prev != nullptr && !chunk->prev->allocated) { - // Increase the previous chunk's size and remove the current chunk from the linked list - chunk->prev->size += chunk->size + sizeof(MemoryChunk); - chunk -> prev -> next = chunk -> next; + // Grow the chunk behind this one so that it now contains the freed one + chunk->prev->size += chunk->size + sizeof(MemoryChunk); + chunk->prev->next = chunk->next; - // If there is a next chunk then ensure this chunk is removed from its linked list - if(chunk -> next != nullptr) - chunk -> next -> prev = chunk->prev; + // The chunk in front of the freed one now needs to point to the merged chunk + if (chunk->next != nullptr) + chunk->next->prev = chunk->prev; - // Chunk is now the previous chunk - chunk = chunk -> prev; + // Freed chunk doesn't exist anymore so now working with the merged chunk + chunk = chunk->prev; - } + } - // If there is a free chunk after this chunk then merge them - if(chunk -> next != nullptr && !chunk -> next -> allocated){ + // If there is a free chunk after this chunk then merge them + if (chunk->next != nullptr && !chunk->next->allocated) { - // Increase the current chunk's size and remove the next chunk from the linked list - chunk -> size += chunk -> next -> size + sizeof(MemoryChunk); - chunk -> next = chunk -> next -> next; + // Grow this chunk so that it now contains the free chunk in front of the old (now freed) one + chunk->size += chunk->next->size + sizeof(MemoryChunk); - // Remove the just merged chunk from the linked list - if(chunk -> next != nullptr) - chunk -> next -> prev = chunk; + // Now that this chunk contains the next one, it has to point to the one in front of what has just been merged + // and that has to point to this + chunk->next = chunk->next->next; + if (chunk->next != nullptr) + chunk->next->prev = chunk; - } + } } /** @@ -231,37 +223,34 @@ void MemoryManager::handle_free(void *pointer) { * @param size The size to expand the heap by * @return The new chunk of memory */ -MemoryChunk *MemoryManager::expand_heap(size_t size) { - - // Create a new chunk of memory - auto* chunk = (MemoryChunk*)m_virtual_memory_manager->allocate(size, Present | Write | NoExecute); +MemoryChunk* MemoryManager::expand_heap(size_t size) { - // If the chunk is null then there is no more memory - ASSERT(chunk != nullptr, "Out of memory - kernel cannot allocate any more memory"); + // Create a new chunk of memory + auto* chunk = (MemoryChunk*) m_virtual_memory_manager->allocate(size, Present | Write | NoExecute); + ASSERT(chunk != nullptr, "Out of memory - kernel cannot allocate any more memory"); - // Handled by assert, but just in case - if(chunk == nullptr) - return nullptr; + // Handled by assert, but just in case + if (chunk == nullptr) + return nullptr; - // Set the chunk's properties - chunk -> allocated = false; - chunk -> size = size; - chunk -> next = nullptr; + // Set the chunk's properties + chunk->allocated = false; + chunk->size = size; + chunk->next = nullptr; - // Insert the chunk into the linked list - m_last_memory_chunk -> next = chunk; - chunk -> prev = m_last_memory_chunk; - m_last_memory_chunk = chunk; + // Insert the chunk into the linked list + m_last_memory_chunk->next = chunk; + chunk->prev = m_last_memory_chunk; + m_last_memory_chunk = chunk; - // If it is possible to move to merge the new chunk with the previous chunk then do so (note: this happens if the previous chunk is free but cant contain the size required) - if(!chunk -> prev -> allocated) - free((void*)((size_t)chunk + sizeof(MemoryChunk))); - - return chunk; + // If it is possible to merge the new chunk with the previous chunk then do so (note: this happens if the + // previous chunk is free but cant contain the size required) + if (!chunk->prev->allocated) + free((void*) ((size_t) chunk + sizeof(MemoryChunk))); + return chunk; } - /** * @brief Returns the amount of memory used * @@ -269,14 +258,14 @@ MemoryChunk *MemoryManager::expand_heap(size_t size) { */ int MemoryManager::memory_used() { - int result = 0; + int result = 0; - // Loop through all the chunks and add up the size of the allocated chunks - for (MemoryChunk* chunk = m_first_memory_chunk; chunk != nullptr; chunk = chunk->next) - if(chunk -> allocated) - result += chunk -> size; + // Loop through all the chunks and add up the size of the allocated chunks + for (MemoryChunk* chunk = m_first_memory_chunk; chunk != nullptr; chunk = chunk->next) + if (chunk->allocated) + result += chunk->size; - return result; + return result; } /** @@ -286,10 +275,9 @@ int MemoryManager::memory_used() { * @return The aligned size */ size_t MemoryManager::align(size_t size) { - return (size / s_chunk_alignment + 1) * s_chunk_alignment; -} - + return (size / s_chunk_alignment + 1) * s_chunk_alignment; +} /** @@ -297,18 +285,17 @@ size_t MemoryManager::align(size_t size) { * * @param manager The new memory manager */ -void MemoryManager::switch_active_memory_manager(MemoryManager *manager) { +void MemoryManager::switch_active_memory_manager(MemoryManager* manager) { - // Make sure there is a manager - if(manager == nullptr) - return; + // Make sure there is a manager + if (manager == nullptr) + return; - // Switch the address space - asm volatile("mov %0, %%cr3" :: "r"((uint64_t)manager->m_virtual_memory_manager->pml4_root_address_physical()) : "memory"); - - // Set the active memory manager - s_current_memory_manager = manager; + // Switch the address space + asm volatile("mov %0, %%cr3"::"r"((uint64_t) manager->m_virtual_memory_manager->pml4_root_address_physical()) : "memory"); + // Set the active memory manager + s_current_memory_manager = manager; } /** @@ -318,9 +305,7 @@ void MemoryManager::switch_active_memory_manager(MemoryManager *manager) { */ VirtualMemoryManager* MemoryManager::vmm() { - // Return the virtual memory manager - return m_virtual_memory_manager; - + return m_virtual_memory_manager; } //Redefine the default object functions with memory orientated ones (defaults disabled in makefile) @@ -332,11 +317,10 @@ VirtualMemoryManager* MemoryManager::vmm() { * @param size The size of the memory to allocate * @return The pointer to the allocated memory */ -void* operator new(size_t size) throw(){ - - // Handle the memory allocation - return MaxOS::memory::MemoryManager::kmalloc(size); +void* operator new(size_t size) throw() { + // Handle the memory allocation + return MaxOS::memory::MemoryManager::kmalloc(size); } /** @@ -345,11 +329,11 @@ void* operator new(size_t size) throw(){ * @param size The size of the memory to allocate * @return The pointer to the allocated memory */ -void* operator new[](size_t size) throw(){ - - // Handle the memory allocation - return MaxOS::memory::MemoryManager::kmalloc(size); +void* operator new[](size_t size) throw() { + // Handle the memory allocation + void* p = MaxOS::memory::MemoryManager::kmalloc(size); + return p; } /** @@ -358,10 +342,9 @@ void* operator new[](size_t size) throw(){ * @param pointer The pointer to the memory to allocate * @return The pointer to the memory */ -void* operator new(size_t, void* pointer){ - - return pointer; +void* operator new(size_t, void* pointer) { + return pointer; } /** @@ -370,11 +353,10 @@ void* operator new(size_t, void* pointer){ * @param pointer The pointer to the memory to allocate * @return The pointer to the memory */ -void* operator new[](size_t, void* pointer){ +void* operator new[](size_t, void* pointer) { - return pointer; - + return pointer; } /** @@ -382,11 +364,10 @@ void* operator new[](size_t, void* pointer){ * * @param pointer The pointer to the memory to free */ -void operator delete(void* pointer){ - - // Handle the memory freeing - return MaxOS::memory::MemoryManager::kfree(pointer); +void operator delete(void* pointer) { + // Handle the memory freeing + return MaxOS::memory::MemoryManager::kfree(pointer); } /** @@ -394,25 +375,22 @@ void operator delete(void* pointer){ * * @param pointer The pointer to the memory to free */ -void operator delete[](void* pointer){ - - // Handle the memory freeing - return MaxOS::memory::MemoryManager::kfree(pointer); +void operator delete[](void* pointer) { + // Handle the memory freeing + return MaxOS::memory::MemoryManager::kfree(pointer); } - /** * @brief Overloaded delete operator, frees memory using the KERNEL memory manager * * @param pointer The pointer to the memory to free * @param size The size of the memory to free - ignored in this implementation */ -void operator delete(void* pointer, size_t){ - - // Handle the memory freeing - return MaxOS::memory::MemoryManager::kfree(pointer); +void operator delete(void* pointer, size_t) { + // Handle the memory freeing + return MaxOS::memory::MemoryManager::kfree(pointer); } /** @@ -420,9 +398,8 @@ void operator delete(void* pointer, size_t){ * * @param pointer The pointer to the memory to free */ -void operator delete[](void* pointer, size_t){ - - // Handle the memory freeing - return MaxOS::memory::MemoryManager::kfree(pointer); +void operator delete[](void* pointer, size_t) { + // Handle the memory freeing + return MaxOS::memory::MemoryManager::kfree(pointer); } \ No newline at end of file diff --git a/kernel/src/memory/physical.cpp b/kernel/src/memory/physical.cpp index a9ec369d..8b5a2841 100644 --- a/kernel/src/memory/physical.cpp +++ b/kernel/src/memory/physical.cpp @@ -6,7 +6,6 @@ #include #include - using namespace MaxOS::memory; using namespace MaxOS::system; using namespace MaxOS::common; @@ -22,110 +21,103 @@ extern uint64_t _kernel_size; extern uint64_t _kernel_physical_end; PhysicalMemoryManager::PhysicalMemoryManager(Multiboot* multiboot) -: m_kernel_end((uint64_t)&_kernel_physical_end), +: m_kernel_end((uint64_t) &_kernel_physical_end), m_multiboot(multiboot), - m_pml4_root_address((uint64_t*)p4_table), - m_pml4_root((pte_t *)p4_table) + m_pml4_root_address((uint64_t*) p4_table), + m_pml4_root((pte_t*) p4_table) { - Logger::INFO() << "Setting up Physical Memory Manager\n"; - - // Unload the kernel from the lower half - unmap_lower_kernel(); + Logger::INFO() << "Setting up Physical Memory Manager\n"; + Logger::DEBUG() << "Kernel Memory: kernel_end = 0x" << (uint64_t) &_kernel_end << ", kernel_size = 0x" << (uint64_t) &_kernel_size << ", kernel_physical_end = 0x" << (uint64_t) &_kernel_physical_end << "\n"; - // Log the kernel memory - Logger::DEBUG() << "Kernel Memory: kernel_end = 0x" << (uint64_t)&_kernel_end << ", kernel_size = 0x" << (uint64_t)&_kernel_size << ", kernel_physical_end = 0x" << (uint64_t)&_kernel_physical_end << "\n"; + // Set up the current manager + unmap_lower_kernel(); + m_lock.unlock(); + s_current_manager = this; + m_nx_allowed = CPU::check_nx(); - // Set up the current manager - m_lock.unlock(); - s_current_manager = this; - m_nx_allowed = CPU::check_nx(); + // Store the information about the bitmap + m_memory_size = (m_multiboot->basic_meminfo()->mem_upper + 1024) * 1024; + m_bitmap_size = m_memory_size / s_page_size + 1; + m_total_entries = m_bitmap_size / s_row_bits + 1; + Logger::DEBUG() << "Memory Info: size = " << (int) (m_memory_size / 1024 / 1024) << "mb, bitmap size = 0x" << (uint64_t) m_bitmap_size << ", total entries = " << (int) m_total_entries << ", page size = 0x" << (uint64_t) s_page_size << "\n"; - // Store the information about the bitmap - m_memory_size = (m_multiboot->basic_meminfo()->mem_upper + 1024) * 1024; - m_bitmap_size = m_memory_size / s_page_size + 1; - m_total_entries = m_bitmap_size / s_row_bits + 1; - Logger::DEBUG() << "Memory Info: size = " << (int)(m_memory_size / 1024 / 1024) << "mb, bitmap size = 0x" << (uint64_t)m_bitmap_size << ", total entries = " << (int)m_total_entries << ", page size = 0x" << (uint64_t)s_page_size << "\n"; + // Find a region of memory available to be used + m_mmap_tag = m_multiboot->mmap(); + for (multiboot_mmap_entry *entry = m_mmap_tag->entries; (multiboot_uint8_t *)entry < (multiboot_uint8_t *)m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry *)((unsigned long)entry + m_mmap_tag->entry_size)) { - // Get the mmap that stores the memory to use - m_mmap_tag = m_multiboot->mmap(); - for (multiboot_mmap_entry *entry = m_mmap_tag->entries; (multiboot_uint8_t *)entry < (multiboot_uint8_t *)m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry *)((unsigned long)entry + m_mmap_tag->entry_size)) { + // Skip if the region is not free or there is not enough space + if (entry->type != MULTIBOOT_MEMORY_AVAILABLE) + continue; - // Skip if the region is not free or there is not enough space - if (entry->type != MULTIBOOT_MEMORY_AVAILABLE) - continue; + // Store the entry. (note: don't break here as it is desired to find the last usable entry as that is normally biggest) + m_mmap = entry; + } + Logger::DEBUG() << "Mmap in use: 0x" << (uint64_t) m_mmap->addr << " - 0x" << (uint64_t) (m_mmap->addr + m_mmap->len) << "\n"; - // We want the last entry - m_mmap = entry; - } - Logger::DEBUG() << "Mmap in use: 0x" << (uint64_t)m_mmap->addr << " - 0x" << (uint64_t)(m_mmap->addr + m_mmap->len) << "\n"; + // Memory after the kernel to be used for direct mapping (the bitmap is not ready while mapping the higher half so have to use this) + m_anonymous_memory_physical_address = align_up_to_page((size_t) &_kernel_physical_end + s_page_size, s_page_size); + m_anonymous_memory_virtual_address = align_up_to_page((size_t) &_kernel_end + s_page_size, s_page_size); + Logger::DEBUG() << "Anonymous Memory: physical = " << (uint64_t) m_anonymous_memory_physical_address << ", virtual = " << (uint64_t) m_anonymous_memory_virtual_address << "\n"; - // Memory after the kernel to be used for direct mapping (when there is no bitmap of the physical memory) - m_anonymous_memory_physical_address = (uint64_t)align_up_to_page((size_t)&_kernel_physical_end + s_page_size, s_page_size); - m_anonymous_memory_virtual_address = (uint64_t)align_up_to_page((size_t)&_kernel_end + s_page_size, s_page_size); - Logger::DEBUG() << "Anonymous Memory: physical = " << (uint64_t)m_anonymous_memory_physical_address << ", virtual = " << (uint64_t)m_anonymous_memory_virtual_address << "\n"; + // Map the physical memory into the virtual memory + for (uint64_t physical_address = 0; physical_address < (m_mmap->addr + m_mmap->len); physical_address += s_page_size) + map((physical_address_t*) physical_address, (virtual_address_t*) (s_hh_direct_map_offset + physical_address), Present | Write); - // Map the physical memory into the virtual memory - for (uint64_t physical_address = 0; physical_address < (m_mmap->addr + m_mmap->len); physical_address += s_page_size) - map((physical_address_t *)physical_address, (virtual_address_t *)(s_hh_direct_map_offset + physical_address), Present | Write); + m_anonymous_memory_physical_address += s_page_size; + Logger::DEBUG() << "Mapped physical memory to higher half direct map at offset 0x" << s_hh_direct_map_offset << "\n"; - m_anonymous_memory_physical_address += s_page_size; - Logger::DEBUG() << "Mapped physical memory to higher half direct map at offset 0x" << s_hh_direct_map_offset << "\n"; + // Set up the bitmap + initialise_bit_map(); + reserve_kernel_regions(multiboot); - // Set up the bitmap - initialise_bit_map(); - reserve_kernel_regions(multiboot); - - // Initialisation Done - m_initialized = true; + // Initialisation Done + m_initialized = true; } PhysicalMemoryManager::~PhysicalMemoryManager() = default; -void PhysicalMemoryManager::reserve_kernel_regions(Multiboot *multiboot) { - - // Reserve the area for the bitmap - Logger::DEBUG() << "Bitmap: location: 0x" << (uint64_t)m_bit_map << " - 0x" << (uint64_t)(m_bit_map + m_bitmap_size / 8) << " (range of 0x" << (uint64_t)m_bitmap_size / 8 << ")\n"; - reserve((uint64_t)from_dm_region((uint64_t)m_bit_map), m_bitmap_size / 8 ); +void PhysicalMemoryManager::reserve_kernel_regions(Multiboot* multiboot) { - // Calculate how much space the kernel takes up - uint32_t kernel_entries = (m_anonymous_memory_physical_address / s_page_size) + 1; - if ((((uint32_t)(m_anonymous_memory_physical_address)) % s_page_size) != 0) - kernel_entries += 1; + // Reserve the area for the bitmap + Logger::DEBUG() << "Bitmap: location: 0x" << (uint64_t) m_bit_map << " - 0x" << (uint64_t) (m_bit_map + m_bitmap_size / 8) << " (range of 0x" << (uint64_t) m_bitmap_size / 8 << ")\n"; + reserve((uint64_t) from_dm_region((uint64_t) m_bit_map), m_bitmap_size / 8); - Logger::DEBUG() << "Kernel: location: 0x" << (uint64_t)m_anonymous_memory_physical_address << " - 0x" << (uint64_t)(m_anonymous_memory_physical_address + kernel_entries * s_page_size) << " (range of 0x" << (uint64_t)kernel_entries * s_page_size << ")\n"; - reserve(0, kernel_entries * s_page_size); + // Calculate how much space the kernel takes up + uint32_t kernel_entries = (m_anonymous_memory_physical_address / s_page_size) + 1; + if ((((uint32_t) (m_anonymous_memory_physical_address)) % s_page_size) != 0) + kernel_entries += 1; - // Reserve the area for the mmap - uint64_t mem_end = m_mmap->addr + m_mmap->len; - for (multiboot_mmap_entry *entry = m_mmap_tag->entries; (multiboot_uint8_t *)entry < (multiboot_uint8_t *)m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry *)((unsigned long)entry + m_mmap_tag->entry_size)) { + // Reserve the kernel entries + Logger::DEBUG() << "Kernel: location: 0x" << (uint64_t) m_anonymous_memory_physical_address << " - 0x" << (uint64_t) (m_anonymous_memory_physical_address + kernel_entries * s_page_size) << " (range of 0x" << (uint64_t) kernel_entries * s_page_size << ")\n"; + reserve(0, kernel_entries * s_page_size); - // Check if the entry is to be mapped - if (entry->type <= MULTIBOOT_MEMORY_AVAILABLE) - continue; + // Reserve the area for the mmap + uint64_t mem_end = m_mmap->addr + m_mmap->len; + for (multiboot_mmap_entry* entry = m_mmap_tag->entries; (multiboot_uint8_t*) entry < (multiboot_uint8_t*) m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry*) ((unsigned long) entry + m_mmap_tag->entry_size)) { - // Where the free mem starts - if(entry->addr >= mem_end) - continue; + // Dont reserve free regions + if (entry->type <= MULTIBOOT_MEMORY_AVAILABLE) + continue; - // Reserve the area - reserve(entry->addr, entry->len); - } + // Don't reserve the memory being managed by pmm + if (entry->addr >= mem_end) + continue; - // Reserve the area for each multiboot module - for(multiboot_tag* tag = multiboot -> start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag *) ((multiboot_uint8_t *) tag + ((tag->size + 7) & ~7))) { + reserve(entry->addr, entry->len); + } - // Check if the tag is a module - if(tag -> type != MULTIBOOT_TAG_TYPE_MODULE) - continue; + // Reserve the area for each multiboot module + for (multiboot_tag* tag = multiboot->start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag*) ((multiboot_uint8_t*) tag + ((tag->size + 7) & ~7))) { - // Get the module tag - auto* module = (struct multiboot_tag_module*)tag; + if (tag->type != MULTIBOOT_TAG_TYPE_MODULE) + continue; - // Reserve the address - reserve(module->mod_start, module->mod_end - module->mod_start); - } + // Reserve the module's address + auto* module = (struct multiboot_tag_module*) tag; + reserve(module->mod_start, module->mod_end - module->mod_start); + } } /** @@ -135,7 +127,8 @@ void PhysicalMemoryManager::reserve_kernel_regions(Multiboot *multiboot) { * @return The number of frames */ size_t PhysicalMemoryManager::size_to_frames(size_t size) { - return align_to_page(size) / s_page_size; + + return align_to_page(size) / s_page_size; } /** @@ -145,7 +138,8 @@ size_t PhysicalMemoryManager::size_to_frames(size_t size) { * @return The aligned size */ size_t PhysicalMemoryManager::align_to_page(size_t size) { - return ((size + s_page_size - 1) /s_page_size) * s_page_size; + + return ((size + s_page_size - 1) / s_page_size) * s_page_size; } /** @@ -156,7 +150,8 @@ size_t PhysicalMemoryManager::align_to_page(size_t size) { * @return The aligned size */ size_t PhysicalMemoryManager::align_up_to_page(size_t size, size_t page_size) { - return (size + page_size - 1) & ~(page_size - 1); + + return (size + page_size - 1) & ~(page_size - 1); } /** @@ -165,8 +160,9 @@ size_t PhysicalMemoryManager::align_up_to_page(size_t size, size_t page_size) { * @param size The address to check * @return True if the address is aligned */ -bool PhysicalMemoryManager::check_aligned(size_t size){ - return (size % s_page_size) == 0; +bool PhysicalMemoryManager::check_aligned(size_t size) { + + return (size % s_page_size) == 0; } /** @@ -176,72 +172,62 @@ bool PhysicalMemoryManager::check_aligned(size_t size){ */ void* PhysicalMemoryManager::allocate_frame() { - // Wait for the lock - m_lock.lock(); - - // Check if the pmm is initialized - if(!m_initialized){ - - - // TODO: This seems to destroy the multiboot memory map, need to fix this - - // Find the first free frame - while ((!is_anonymous_available(m_anonymous_memory_physical_address)) && (m_anonymous_memory_physical_address < m_memory_size)) { - m_anonymous_memory_physical_address += s_page_size; - m_anonymous_memory_virtual_address += s_page_size; - } - - // Mark frame as used - m_anonymous_memory_physical_address += s_page_size; - m_anonymous_memory_virtual_address += s_page_size; - - // Clear the lock - m_lock.unlock(); - - // Return the address - return (void*)(m_anonymous_memory_physical_address - s_page_size); + // Wait for the lock + m_lock.lock(); - } + // If not initialised, cant use the bitmap or higher half mapped physical memory so use leftover kernel memory already + // mapped in loader.s + if (!m_initialized) { + // TODO: This seems to destroy the multiboot memory map, need to fix this - // Check if there are enough frames - ASSERT(m_used_frames < m_bitmap_size, "No more frames available\n"); + // Find the first free frame + while ((!is_anonymous_available(m_anonymous_memory_physical_address)) && (m_anonymous_memory_physical_address < m_memory_size)) { + m_anonymous_memory_physical_address += s_page_size; + m_anonymous_memory_virtual_address += s_page_size; + } - // Loop through the bitmap - for (uint32_t row = 0; row < m_total_entries; ++row) { + // Mark frame as used + m_anonymous_memory_physical_address += s_page_size; + m_anonymous_memory_virtual_address += s_page_size; - // If the row is full continue - if(m_bit_map[row] == 0xFFFFFFFFFFFFFFF) - continue; + // Return the address + m_lock.unlock(); + return (void*) (m_anonymous_memory_physical_address - s_page_size); - for (uint32_t column = 0; column < s_row_bits; ++column) { + } - // Check if the bitmap is free - if (m_bit_map[row] & (1ULL << column)) - continue; + // Check if there are enough frames + ASSERT(m_used_frames < m_bitmap_size, "No more frames available\n"); + for (uint32_t row = 0; row < m_total_entries; ++row) { - // Mark the frame as used - m_bit_map[row] |= (1ULL << column); - m_used_frames++; + // If the row is full continue + if (m_bit_map[row] == 0xFFFFFFFFFFFFFFF) + continue; - // Return the address - uint64_t frame_address = (row * s_row_bits) + column; - frame_address *= s_page_size; + for (uint32_t column = 0; column < s_row_bits; ++column) { + // Entry isn't free + if (m_bit_map[row] & (1ULL << column)) + continue; - // Clear the lock - m_lock.unlock(); + // Mark the frame as used + m_bit_map[row] |= (1ULL << column); + m_used_frames++; - // Return the address - return (void*)(frame_address); - } - } + // Thread safe + m_lock.unlock(); - // Error frame not found - ASSERT(false, "Frame not found\n"); - m_lock.unlock(); - return nullptr; + // Return the address + uint64_t frame_address = (row * s_row_bits) + column; + return (void*) (frame_address * s_page_size); + } + } + // Error frame not found + ASSERT(false, "Frame not found\n"); + m_lock.unlock(); + return nullptr; } /** @@ -249,20 +235,16 @@ void* PhysicalMemoryManager::allocate_frame() { * * @param address The address to free */ -void PhysicalMemoryManager::free_frame(void *address) { +void PhysicalMemoryManager::free_frame(void* address) { - // Wait for the lock - m_lock.lock(); + m_lock.lock(); - // Mark the frame as not used - m_used_frames--; + // Mark the frame as not used + m_used_frames--; + uint64_t frame_address = (uint64_t) address / s_page_size; + m_bit_map[frame_address / s_row_bits] &= ~(1 << (frame_address % s_row_bits)); - // Set the bit to 0 - uint64_t frame_address = (uint64_t)address / s_page_size; - m_bit_map[frame_address / s_row_bits] &= ~(1 << (frame_address % s_row_bits)); - - // Clear the lock - m_lock.unlock(); + m_lock.unlock(); } /** @@ -274,73 +256,65 @@ void PhysicalMemoryManager::free_frame(void *address) { */ void* PhysicalMemoryManager::allocate_area(uint64_t start_address, size_t size) { - // Wait to be able to allocate - m_lock.lock(); - - // Check how many frames are needed - size_t frame_count = size_to_frames(size); + m_lock.lock(); - // Store the information about the frames needed to be allocated for this size - uint32_t start_row = 0; - uint32_t start_column = 0; - size_t adjacent_frames = 0; + // Store the information about the frames needed to be allocated for this size + size_t frame_count = size_to_frames(size); + uint32_t start_row = 0; + uint32_t start_column = 0; + size_t adjacent_frames = 0; - // Loop through the bitmap - for (uint32_t row = 0; row < m_total_entries; ++row) { + for (uint32_t row = 0; row < m_total_entries; ++row) { - // If the row is full continue - if(m_bit_map[row] == 0xFFFFFFFFFFFFFFF) - continue; + // Skip full rows + if (m_bit_map[row] == 0xFFFFFFFFFFFFFFF) + continue; - for (uint32_t column = 0; column < s_row_bits; ++column) { + for (uint32_t column = 0; column < s_row_bits; ++column) { - // If this bit is not free, reset the adjacent frames - if (m_bit_map[row] & (1ULL << column)) { - adjacent_frames = 0; - continue; - } + // Not enough adjacent frames + if (m_bit_map[row] & (1ULL << column)) { + adjacent_frames = 0; + continue; + } - // Store the start of the area if it is not already stored - if(adjacent_frames == 0){ - start_row = row; - start_column = column; - } + // Store the address of the first frame in set of adjacent ones + if (adjacent_frames == 0) { + start_row = row; + start_column = column; + } - // Increment the adjacent frames - adjacent_frames++; + // Make sure there are enough frames in a row found + adjacent_frames++; + if (adjacent_frames != frame_count) + continue; - // If enough frames are found we can allocate the area - if(adjacent_frames == frame_count){ + // Mark the frames as used + m_used_frames += frame_count; + for (uint32_t i = 0; i < frame_count; ++i) { - // Mark the frames as used - m_used_frames += frame_count; - for (uint32_t i = 0; i < frame_count; ++i) { + // Get the location of the bit + uint32_t index = start_row + (start_column + i) / s_row_bits; + uint32_t bit = (start_column + i) % s_row_bits; - // Get the location of the bit - uint32_t index = start_row + (start_column + i) / s_row_bits; - uint32_t bit = (start_column + i) % s_row_bits; + // Check bounds + ASSERT(index >= m_total_entries || bit >= s_row_bits, "Index out of bounds\n"); - // Skip if index exceeds bounds - if (index >= m_total_entries || bit >= s_row_bits) { - ASSERT(false, "Index out of bounds\n"); - } + // Mark the bit as used + m_bit_map[index] |= (1ULL << bit); + } - m_bit_map[index] |= (1ULL << bit); // Mark the bit as used - } + // Return start of the block of adjacent frames + m_lock.unlock(); + return (void*) (start_address + (start_row * s_row_bits + start_column) * s_page_size); - // Clear the lock - m_lock.unlock(); + } + } - // Return the address - return (void*)(start_address + (start_row * s_row_bits + start_column) * s_page_size); - } - } - } - - // Error cant allocate that much - m_lock.unlock(); - ASSERT(false, "Cannot allocate that much memory\n"); - return nullptr; + // Not enough free frames adjacent to each other + m_lock.unlock(); + ASSERT(false, "Cannot allocate that much memory\n"); + return nullptr; } /** @@ -351,25 +325,23 @@ void* PhysicalMemoryManager::allocate_area(uint64_t start_address, size_t size) */ void PhysicalMemoryManager::free_area(uint64_t start_address, size_t size) { - // Check how many frames are needed - size_t frame_count = size_to_frames(size); - uint64_t frame_address = start_address / s_page_size; - - // Check if the address is valid - if(frame_address >= m_bitmap_size) - return; + // Convert address into frames + size_t frame_count = size_to_frames(size); + uint64_t frame_address = start_address / s_page_size; - // Wait to be able to free - m_lock.lock(); + // Check bounds + if (frame_address >= m_bitmap_size) + return; - // Mark the frames as not used - m_used_frames -= frame_count; - for (uint32_t i = 0; i < frame_count; ++i) - m_bit_map[(frame_address + i) / s_row_bits] &= ~(1 << ((frame_address + i) % s_row_bits)); + // Wait until other threads have finished other memory operations + m_lock.lock(); + // Mark the frames as not used + m_used_frames -= frame_count; + for (uint32_t i = 0; i < frame_count; ++i) + m_bit_map[(frame_address + i) / s_row_bits] &= ~(1 << ((frame_address + i) % s_row_bits)); - // Clear the lock - m_lock.unlock(); + m_lock.unlock(); } /** @@ -381,7 +353,8 @@ void PhysicalMemoryManager::free_area(uint64_t start_address, size_t size) { * @return */ pml_t* PhysicalMemoryManager::get_higher_half_table(uint64_t index, uint64_t index2, uint64_t index3) { - return (pml_t*)(0xFFFF000000000000 | ((510UL << 39) | (index3 << 30) | (index2 << 21) | (index << 12))); + + return (pml_t*) (0xFFFF000000000000 | ((510UL << 39) | (index3 << 30) | (index2 << 21) | (index << 12))); } @@ -395,23 +368,21 @@ pml_t* PhysicalMemoryManager::get_higher_half_table(uint64_t index, uint64_t ind */ pml_t* PhysicalMemoryManager::get_or_create_table(pml_t* table, size_t index, size_t flags) { + // Table is already created so just find the entry + if (table->entries[index].present) + return (pml_t*) to_dm_region(physical_address_of_entry(&table->entries[index])); - // If the table is already created return it - if(table -> entries[index].present) - return (pml_t *) to_dm_region((uintptr_t)physical_address_of_entry(&table -> entries[index])); - - // Need to create the table - auto* new_table = (uint64_t*)allocate_frame(); - table -> entries[index] = create_page_table_entry((uint64_t)new_table, flags); + // Create the table + auto* new_table = (uint64_t*) allocate_frame(); + table->entries[index] = create_page_table_entry((uint64_t) new_table, flags); - // Move the table to the higher half (can't rely on the direct map if the pmm is not initialized) - new_table = (uint64_t*)to_dm_region((uintptr_t) new_table); + // Move the table to the higher half + new_table = (uint64_t*) to_dm_region((uintptr_t) new_table); - // Clear the table - clean_page_table(new_table); + // Reset the memory contents at the address + clean_page_table(new_table); - // All done - return (pml_t*)new_table; + return (pml_t*) new_table; } /** @@ -419,73 +390,68 @@ pml_t* PhysicalMemoryManager::get_or_create_table(pml_t* table, size_t index, si * * @param parent_table The parent table to create the entry in * @param table_index The index of the table to create - * @param pml4_index The index of the PML4 table - * @param pdpr_index The index of the PDPR table (defaults to 510) - * @param pd_index The index of the PD table (defaults to 510) - * @param pt_index The index of the PT table (defaults to 510) + * @param table The table to create * * @return The created page table entry */ pml_t* PhysicalMemoryManager::get_and_create_table(pml_t* parent_table, uint64_t table_index, pml_t* table) { - // If the table is already created return it - if(parent_table -> entries[table_index].present) - return table; + // Table already created so dont need to do anything + /// Note: this is where it differs when not having pmm init done as cant find the entry (direct map not setup) thus + /// requiring get_higher_half_table() to be passed in as the *table arg by the caller + if (parent_table->entries[table_index].present) + return table; - // Create the table - auto* new_table = (uint64_t *)allocate_frame(); - parent_table -> entries[table_index] = create_page_table_entry((uint64_t)new_table, Present | Write); + // Create the table + auto* new_table = (uint64_t*) allocate_frame(); + parent_table->entries[table_index] = create_page_table_entry((uint64_t) new_table, Present | Write); - // Invalidate the table - asm volatile("invlpg (%0)" ::"r" (table) : "memory"); + // Move the table to higher half + // Except this doesn't need to be done because using anom memory - // Clear the table - clean_page_table((uint64_t*)table); + // Reset the memory contents at the address + clean_page_table((uint64_t*) table); - // Return the table - return (pml_t *)table; + return table; } - /** * @brief Get the page table entry for a virtual address * * @param virtual_address The virtual address to get the entry for * @return The page table entry for the virtual address or nullptr if not found */ -pte_t *PhysicalMemoryManager::get_entry(virtual_address_t* virtual_address, pml_t* pml4_table) { - - // Check if the address is in the higher region - uint8_t is_user = !(in_higher_region((uint64_t)virtual_address)); - if(is_user) - is_user = User; - - // Get the indexes - uint16_t pml4_index = PML4_GET_INDEX((uint64_t) virtual_address); - uint16_t pdpr_index = PML3_GET_INDEX((uint64_t) virtual_address); - uint16_t pd_index = PML2_GET_INDEX((uint64_t) virtual_address); - uint16_t pt_index = PML1_GET_INDEX((uint64_t) virtual_address); - - // Get the tables - pml_t* pdpr_table = nullptr; - pml_t* pd_table = nullptr; - pml_t* pt_table = nullptr; - - // If it is before initialization then cant rely on the direct map - if(!m_initialized) { - pdpr_table = get_and_create_table(pml4_table, pml4_index, get_higher_half_table(pml4_index)); - pd_table = get_and_create_table(pdpr_table, pdpr_index, get_higher_half_table(pdpr_index, pml4_index)); - pt_table = get_and_create_table(pd_table, pd_index, get_higher_half_table(pd_index, pdpr_index, pml4_index)); - - }else{ - pdpr_table = get_or_create_table(pml4_table, pml4_index, Present | Write | is_user); - pd_table = get_or_create_table(pdpr_table, pdpr_index, Present | Write | is_user); - pt_table = get_or_create_table(pd_table, pd_index, Present | Write | is_user); - } - - // Get the entry - return &pt_table -> entries[pt_index]; +pte_t* PhysicalMemoryManager::get_entry(virtual_address_t* virtual_address, pml_t* pml4_table) { + + // Kernel memory must be in the higher half + size_t flags = Present | Write; + if (!in_higher_region((uint64_t) virtual_address)) + flags |= User; + + uint16_t pml4_index = PML4_GET_INDEX((uint64_t) virtual_address); + uint16_t pdpr_index = PML3_GET_INDEX((uint64_t) virtual_address); + uint16_t pd_index = PML2_GET_INDEX((uint64_t) virtual_address); + uint16_t pt_index = PML1_GET_INDEX((uint64_t) virtual_address); + + pml_t* pdpr_table = nullptr; + pml_t* pd_table = nullptr; + pml_t* pt_table = nullptr; + + // If it is before initialization then cant rely on the direct map + if (!m_initialized) { + pdpr_table = get_and_create_table(pml4_table, pml4_index, get_higher_half_table(pml4_index)); + pd_table = get_and_create_table(pdpr_table, pdpr_index, get_higher_half_table(pdpr_index, pml4_index)); + pt_table = get_and_create_table(pd_table, pd_index, get_higher_half_table(pd_index, pdpr_index, pml4_index)); + + } else { + pdpr_table = get_or_create_table(pml4_table, pml4_index, flags); + pd_table = get_or_create_table(pdpr_table, pdpr_index, flags); + pt_table = get_or_create_table(pd_table, pd_index, flags); + } + + // Get the entry + return &pt_table->entries[pt_index]; } @@ -495,8 +461,9 @@ pte_t *PhysicalMemoryManager::get_entry(virtual_address_t* virtual_address, pml_ * @param entry The entry to get the physical address of * @return The physical address of the entry */ -uint64_t PhysicalMemoryManager::physical_address_of_entry(pte_t *entry) { - return entry -> physical_address << 12; +uint64_t PhysicalMemoryManager::physical_address_of_entry(pte_t* entry) { + + return entry->physical_address << 12; } @@ -504,14 +471,14 @@ uint64_t PhysicalMemoryManager::physical_address_of_entry(pte_t *entry) { * @brief Maps a physical address to a virtual address, using the kernel's pml4 table * * @param physical_address The physical address to map - * @param address The virtual address to map to + * @param virtual_address The virtual address to map to * @param flags The flags to set the mapping to * @return The virtual address */ -virtual_address_t* PhysicalMemoryManager::map(physical_address_t *physical_address, virtual_address_t* virtual_address, size_t flags) { +virtual_address_t* PhysicalMemoryManager::map(physical_address_t* physical_address, virtual_address_t* virtual_address, size_t flags) { - // Map using the kernel's pml4 table - return map(physical_address, virtual_address, flags, (uint64_t*)m_pml4_root_address); + // Map using the kernel's pml4 table + return map(physical_address, virtual_address, flags, m_pml4_root_address); } /** @@ -525,27 +492,22 @@ virtual_address_t* PhysicalMemoryManager::map(physical_address_t *physical_addre */ virtual_address_t* PhysicalMemoryManager::map(physical_address_t* physical_address, virtual_address_t* virtual_address, size_t flags, uint64_t* pml4_table) { - // If it is in a lower region then assume it is the user space - uint8_t is_user = !(in_higher_region((uint64_t)virtual_address)); - if(is_user) { - // Change the flags to user - flags |= User; - } + // If it is in a lower region then assume it is the user space + if (!in_higher_region((uint64_t) virtual_address)) + flags |= User; - // Get the entry - pte_t* pte = get_entry(virtual_address, (pml_t *)pml4_table); + // If the entry already exists then the mapping is already done + pte_t* pte = get_entry(virtual_address, (pml_t*) pml4_table); + if (pte->present) + return virtual_address; - // If it already exists return the address - if(pte -> present) - return virtual_address; + // Map the physical address to the virtual address + *pte = create_page_table_entry((uint64_t) physical_address, flags); - // Map the physical address to the virtual address - *pte = create_page_table_entry((uint64_t)physical_address, flags); + // Flush the TLB (cache) + asm volatile("invlpg (%0)"::"r" (virtual_address) : "memory"); - // Flush the TLB - asm volatile("invlpg (%0)" ::"r" (virtual_address) : "memory"); - - return virtual_address; + return virtual_address; } /** @@ -555,13 +517,10 @@ virtual_address_t* PhysicalMemoryManager::map(physical_address_t* physical_addre * @param flags The flags to set the mapping to * @return The virtual address */ -virtual_address_t* PhysicalMemoryManager::map(virtual_address_t *virtual_address, size_t flags) { - - // Create a new physical address for the frame - auto* physical_address = (physical_address_t *)allocate_frame(); +virtual_address_t* PhysicalMemoryManager::map(virtual_address_t* virtual_address, size_t flags) { - // Map the physical address to the virtual address - return map(physical_address, virtual_address, flags); + // Map a new physical address to the requested virtual address + return map(allocate_frame(), virtual_address, flags); } @@ -574,12 +533,9 @@ virtual_address_t* PhysicalMemoryManager::map(virtual_address_t *virtual_address */ void PhysicalMemoryManager::map_area(virtual_address_t* virtual_address_start, size_t length, size_t flags) { - // Get the size of the area - size_t size = size_to_frames(length); - - // Map the required frames - for (size_t i = 0; i < size; ++i) - map(virtual_address_start + (i * s_page_size), flags); + // Map the required frames + for (size_t i = 0; i < size_to_frames(length); ++i) + map(virtual_address_start + (i * s_page_size), flags); } @@ -593,13 +549,9 @@ void PhysicalMemoryManager::map_area(virtual_address_t* virtual_address_start, s */ void PhysicalMemoryManager::map_area(physical_address_t* physical_address_start, virtual_address_t* virtual_address_start, size_t length, size_t flags) { - // Get the size of the area - size_t size = size_to_frames(length); - - // Map the required frames - for (size_t i = 0; i < size; ++i) - map(physical_address_start + (i * s_page_size), virtual_address_start + (i * s_page_size), flags); - + // Map the required frames + for (size_t i = 0; i < size_to_frames(length); ++i) + map(physical_address_start + (i * s_page_size), virtual_address_start + (i * s_page_size), flags); } /** @@ -608,11 +560,10 @@ void PhysicalMemoryManager::map_area(physical_address_t* physical_address_start, * @param physical_address The physical address to map * @param flags The flags to set the mapping to */ -void PhysicalMemoryManager::identity_map(physical_address_t *physical_address, size_t flags) { - - // Map the physical address to its virtual address counter-part - map(physical_address, physical_address, flags); +void PhysicalMemoryManager::identity_map(physical_address_t* physical_address, size_t flags) { + // Map the physical address to its virtual address counter-part + map(physical_address, physical_address, flags); } /** @@ -622,8 +573,8 @@ void PhysicalMemoryManager::identity_map(physical_address_t *physical_address, s */ void PhysicalMemoryManager::unmap(virtual_address_t* virtual_address) { - // Pass the kernel's pml4 table - unmap(virtual_address, (uint64_t*)m_pml4_root_address); + // Pass the kernel's pml4 table + unmap(virtual_address, m_pml4_root_address); } /** @@ -632,21 +583,20 @@ void PhysicalMemoryManager::unmap(virtual_address_t* virtual_address) { * @param virtual_address The virtual address to unmap * @param pml4_root The pml4 table to use */ -void PhysicalMemoryManager::unmap(virtual_address_t *virtual_address, uint64_t* pml4_root) { +void PhysicalMemoryManager::unmap(virtual_address_t* virtual_address, uint64_t* pml4_root) { - // Get the entries - pte_t* pte = get_entry(virtual_address, (pml_t *)pml4_root); + // Get the entry + pte_t* pte = get_entry(virtual_address, (pml_t*) pml4_root); - // Check if the entry is present - if(!pte -> present) - return; + // Make sure the address is actually mapped + if (!pte->present) + return; - // Unmap the entry - pte -> present = false; - - // Flush the TLB - asm volatile("invlpg (%0)" ::"r" (virtual_address) : "memory"); + // Unmap the entry + pte->present = false; + // Flush the TLB (cache) + asm volatile("invlpg (%0)"::"r" (virtual_address) : "memory"); } /** @@ -655,14 +605,11 @@ void PhysicalMemoryManager::unmap(virtual_address_t *virtual_address, uint64_t* * @param virtual_address_start The start of the area * @param length The length of the area */ -void PhysicalMemoryManager::unmap_area(virtual_address_t *virtual_address_start, size_t length) { - - // Get the size of the area - size_t size = size_to_frames(length); +void PhysicalMemoryManager::unmap_area(virtual_address_t* virtual_address_start, size_t length) { - // Unmap the required frames - for (size_t i = 0; i < size; ++i) - unmap(virtual_address_start + (i * s_page_size)); + // Unmap the required frames + for (size_t i = 0; i < size_to_frames(length); ++i) + unmap(virtual_address_start + (i * s_page_size)); } /** @@ -670,10 +617,11 @@ void PhysicalMemoryManager::unmap_area(virtual_address_t *virtual_address_start, * * @param table The table to clean */ -void PhysicalMemoryManager::clean_page_table(uint64_t *table) { - for(int i = 0; i < 512; i++){ - table[i] = 0x00l; - } +void PhysicalMemoryManager::clean_page_table(uint64_t* table) { + + // Null the table (prevents false mappings when re-using frames) + for (int i = 0; i < 512; i++) + table[i] = 0x00l; } /** @@ -683,30 +631,30 @@ void PhysicalMemoryManager::clean_page_table(uint64_t *table) { * @param flags The flags to set the entry to * @return The created page table entry */ -pte_t PhysicalMemoryManager::create_page_table_entry(uintptr_t address, size_t flags) { - - pte_t page = (pte_t){ - .present = (flags & Present) != 0, - .write = (flags & Write) != 0, - .user = (flags & User) != 0, - .write_through = (flags & WriteThrough) != 0, - .cache_disabled = (flags & CacheDisabled) != 0, - .accessed = (flags & Accessed) != 0, - .dirty = (flags & Dirty) != 0, - .huge_page = (flags & HugePage) != 0, - .global = (flags & Global) != 0, - .available = 0, - .physical_address = (uint64_t)address >> 12, - }; - - // Set the NX bit if it is allowed - if(m_nx_allowed && (flags & NoExecute)){ - auto page_raw = (uint64_t)&page; - page_raw |= NoExecute; - page = *(pte_t*)page_raw; - } - - return page; +pte_t PhysicalMemoryManager::create_page_table_entry(uintptr_t address, size_t flags) const { + + pte_t page = (pte_t) { + .present = (flags & Present) != 0, + .write = (flags & Write) != 0, + .user = (flags & User) != 0, + .write_through = (flags & WriteThrough) != 0, + .cache_disabled = (flags & CacheDisabled) != 0, + .accessed = (flags & Accessed) != 0, + .dirty = (flags & Dirty) != 0, + .huge_page = (flags & HugePage) != 0, + .global = (flags & Global) != 0, + .available = 0, + .physical_address = address >> 12, + }; + + // Set the NX bit if it is allowed + if (m_nx_allowed && (flags & NoExecute)) { + auto page_raw = (uint64_t) &page; + page_raw |= NoExecute; + page = *(pte_t*) page_raw; + } + + return page; } /** @@ -717,34 +665,34 @@ pte_t PhysicalMemoryManager::create_page_table_entry(uintptr_t address, size_t f */ bool PhysicalMemoryManager::is_anonymous_available(size_t address) { - // Return false if the address range is entirely within or overlaps with the multiboot reserved region - if ((address > m_multiboot-> start_address && address + s_page_size < m_multiboot -> end_address) - || (address + s_page_size > m_multiboot-> start_address && address < m_multiboot -> end_address)) { - return false; - } + // Make sure the address isn't (entirely) within or overlapping with the multiboot memory chunk + if ((address > m_multiboot->start_address && address + s_page_size < m_multiboot->end_address) + || (address + s_page_size > m_multiboot->start_address && address < m_multiboot->end_address)) { + return false; + } - // Loop through the mmmap entries - for (multiboot_mmap_entry *entry = m_mmap_tag->entries; (multiboot_uint8_t *)entry < (multiboot_uint8_t *)m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry *)((unsigned long)entry + m_mmap_tag->entry_size)) { + // Make sure the address isn't used by a multiboot module + if (m_multiboot->is_reserved(address)) + return false; - // If it doesn't overlap with the mmap entry - if ((entry -> addr + entry -> len) < (address + s_page_size)) - continue; + // Make sure the address doesn't overlap with a reserved chunk of physical memory + for (multiboot_mmap_entry* entry = m_mmap_tag->entries; (multiboot_uint8_t*) entry < (multiboot_uint8_t*) m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry*) ((unsigned long) entry + m_mmap_tag->entry_size)) { - // If it is not available - if(entry -> type != MULTIBOOT_MEMORY_AVAILABLE) - continue; + // This entry doesnt contain the address + if ((entry->addr + entry->len) < (address + s_page_size)) + continue; - // Check if the address is overwriting with some reserved memory - if(m_multiboot -> is_reserved(address)) - return false; + // This entry is not free + if (entry->type != MULTIBOOT_MEMORY_AVAILABLE) + continue; - // Memory is available - return true; + // This entry must contain the address and must be free so it can be used + return true; - } + } - // Memory is not available - return false; + // Memory is not available (it was not found in a free region) + return false; } @@ -756,40 +704,39 @@ bool PhysicalMemoryManager::is_anonymous_available(size_t address) { void PhysicalMemoryManager::initialise_bit_map() { - // Earliest address to place the bitmap (after the kernel and hh direct map) - uint64_t limit = m_anonymous_memory_physical_address; + // Earliest address to place the bitmap (after the kernel and hh direct map) + uint64_t limit = m_anonymous_memory_physical_address; - // Loop through the mmap entries - for (multiboot_mmap_entry *entry = m_mmap_tag->entries; (multiboot_uint8_t *)entry < (multiboot_uint8_t *)m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry *)((unsigned long)entry + m_mmap_tag->entry_size)) { + // Find a region for the bitmap to handle + for (multiboot_mmap_entry* entry = m_mmap_tag->entries; (multiboot_uint8_t*) entry < (multiboot_uint8_t*) m_mmap_tag + m_mmap_tag->size; entry = (multiboot_mmap_entry*) ((unsigned long) entry + m_mmap_tag->entry_size)) { - // If the entry is not available or the address is before the limit - if (entry -> type != MULTIBOOT_MEMORY_AVAILABLE || entry -> len > limit) - continue; + // Cant use a non-free entry or an entry past limit (ie dont user higher addresses that will be overridden later) + if (entry->type != MULTIBOOT_MEMORY_AVAILABLE || entry->len > limit) + continue; - size_t space = entry -> len; - size_t offset = 0; + size_t space = entry->len; + size_t offset = 0; - // If the entry starts before the limit then adjust the space to start where the limit is - if(entry -> addr < limit){ - offset = limit - entry -> addr; - space -= offset; - } + // Determine how much of the region is below the limit and adjust the starting point to there + if (entry->addr < limit) { + offset = limit - entry->addr; + space -= offset; + } + // Make sure there is enough space + ASSERT(space >= (m_bitmap_size / 8 + 1), "Not enough space for the bitmap (too big)\n"); - // Make sure there is enough space - ASSERT(space >= (m_bitmap_size / 8 + 1), "Not enough space for the bitmap\n"); + // Return the address (ensuring that it is in the safe region) + m_bit_map = (uint64_t*) to_dm_region(entry->addr + offset); + break; + } - // Return the address - m_bit_map = (uint64_t*)to_dm_region(entry -> addr + offset); - break; - } + // Error no suitable space for the bitmap found + ASSERT(m_bit_map != nullptr, "No space for the bitmap (no region)\n"); - // Error no space for the bitmap - ASSERT(m_bit_map != nullptr, "No space for the bitmap\n"); - - // Clear the bitmap - for (uint32_t i = 0; i < m_total_entries; ++i) - m_bit_map[i] = 0; + // Clear the bitmap (mark all as free) + for (uint32_t i = 0; i < m_total_entries; ++i) + m_bit_map[i] = 0; } /** @@ -797,8 +744,9 @@ void PhysicalMemoryManager::initialise_bit_map() { * * @return The pml4 root address */ -uint64_t *PhysicalMemoryManager::pml4_root_address() { - return m_pml4_root_address; +uint64_t* PhysicalMemoryManager::pml4_root_address() { + + return m_pml4_root_address; } /** @@ -807,7 +755,8 @@ uint64_t *PhysicalMemoryManager::pml4_root_address() { * @return The memory size in bytes */ uint64_t PhysicalMemoryManager::memory_size() const { - return m_memory_size; + + return m_memory_size; } /** @@ -816,7 +765,8 @@ uint64_t PhysicalMemoryManager::memory_size() const { * @return The memory size in bytes */ uint64_t PhysicalMemoryManager::memory_used() const { - return m_used_frames * s_page_size; + + return m_used_frames * s_page_size; } /** @@ -826,7 +776,8 @@ uint64_t PhysicalMemoryManager::memory_used() const { * @return The aligned address */ size_t PhysicalMemoryManager::align_direct_to_page(size_t size) { - return (size & (~(s_page_size - 1))); + + return (size & (~(s_page_size - 1))); } /** @@ -836,20 +787,15 @@ size_t PhysicalMemoryManager::align_direct_to_page(size_t size) { */ void PhysicalMemoryManager::reserve(uint64_t address) { + // Cant reserve virtual addresses (ensure the address is physical) + if (address >= m_memory_size) + return; - // If the address is not part of physical memory then return - if(address >= m_memory_size) - return; - - // Get the address to a page - address = align_direct_to_page(address); - - // Set the bit to 1 in the bitmap - m_bit_map[address / s_row_bits] |= (1 << (address % s_row_bits)); - - - Logger::DEBUG() << "Reserved Address: 0x" << address << "\n"; + // Mark as used in the bitmap + address = align_direct_to_page(address); + m_bit_map[address / s_row_bits] |= (1 << (address % s_row_bits)); + Logger::DEBUG() << "Reserved Address: 0x" << address << "\n"; } /** @@ -860,31 +806,32 @@ void PhysicalMemoryManager::reserve(uint64_t address) { */ void PhysicalMemoryManager::reserve(uint64_t address, size_t size) { - if(address >= m_memory_size) - return; + // Cant reserve virtual addresses (ensure the address is physical) + if (address >= m_memory_size) + return; + + // Wait to be able to reserve + m_lock.lock(); - // Wait to be able to reserve - m_lock.lock(); + // Ensure the area is a range of pages + address = align_direct_to_page(address); + size = align_up_to_page(size, s_page_size); - // Align address and size to page boundaries - address = align_direct_to_page(address); - size = align_up_to_page(size, s_page_size); + // Convert in to amount of pages + size_t page_count = size / s_page_size; + uint64_t frame_index = address / s_page_size; - // Calculate how many pages need to be reserved - size_t page_count = size / s_page_size; - // Convert the starting address to a frame index - uint64_t frame_index = address / s_page_size; + // Mark all as free + for (size_t i = 0; i < page_count; ++i) + m_bit_map[(frame_index + i) / s_row_bits] |= (1ULL << ((frame_index + i) % s_row_bits)); - for (size_t i = 0; i < page_count; ++i) { - m_bit_map[(frame_index + i) / s_row_bits] |= (1ULL << ((frame_index + i) % s_row_bits)); - } - // Update the used frames - m_used_frames += page_count; + // Update the used frames + m_used_frames += page_count; - // Clear the lock - m_lock.unlock(); - Logger::DEBUG() << "Reserved Address: 0x" << address << " - 0x" << address + size << " (length of 0x" << size << ")\n"; + // Clear the lock + m_lock.unlock(); + Logger::DEBUG() << "Reserved Address: 0x" << address << " - 0x" << address + size << " (length of 0x" << size << ")\n"; } /** @@ -893,17 +840,15 @@ void PhysicalMemoryManager::reserve(uint64_t address, size_t size) { * @param virtual_address The virtual address to get the physical address from * @return The physical address or nullptr if it does not exist */ -physical_address_t *PhysicalMemoryManager::get_physical_address(virtual_address_t *virtual_address, uint64_t *pml4_root) { +physical_address_t* PhysicalMemoryManager::get_physical_address(virtual_address_t* virtual_address, uint64_t* pml4_root) { - // Get the entry - pte_t* entry = get_entry(virtual_address, (pml_t *)pml4_root); + pte_t* entry = get_entry(virtual_address, (pml_t*) pml4_root); - // Check if the entry is present - if(!entry -> present) - return nullptr; + // Cant get a physical address if its inst free + if (!entry->present) + return nullptr; - // Get the physical address - return (physical_address_t*)physical_address_of_entry(entry); + return (physical_address_t*) physical_address_of_entry(entry); } /** @@ -912,20 +857,18 @@ physical_address_t *PhysicalMemoryManager::get_physical_address(virtual_address_ * @param virtual_address The virtual address of the page * @param flags The flags to set the page to */ -void PhysicalMemoryManager::change_page_flags(virtual_address_t *virtual_address, size_t flags, uint64_t *pml4_root) { +void PhysicalMemoryManager::change_page_flags(virtual_address_t* virtual_address, size_t flags, uint64_t* pml4_root) { - // Get the entry - pte_t* entry = get_entry(virtual_address, (pml_t *)pml4_root); + pte_t* entry = get_entry(virtual_address, (pml_t*) pml4_root); - // Check if the entry is present - if(!entry -> present) - return; + // Cant edit a non-present entry (will page fault) + if (!entry->present) + return; - // Change the flags - *entry = create_page_table_entry(physical_address_of_entry(entry), flags); + *entry = create_page_table_entry(physical_address_of_entry(entry), flags); - // Flush the TLB - asm volatile("invlpg (%0)" ::"r" (virtual_address) : "memory"); + // Flush the TLB (cache) + asm volatile("invlpg (%0)"::"r" (virtual_address) : "memory"); } @@ -937,9 +880,9 @@ void PhysicalMemoryManager::change_page_flags(virtual_address_t *virtual_address * @param pml4_root The pml4 table to use * @return True if the physical address is mapped to the virtual address */ -bool PhysicalMemoryManager::is_mapped(uintptr_t physical_address, uintptr_t virtual_address, uint64_t *pml4_root) { +bool PhysicalMemoryManager::is_mapped(uintptr_t physical_address, uintptr_t virtual_address, uint64_t* pml4_root) { - return get_physical_address((virtual_address_t*)virtual_address, pml4_root) == (physical_address_t*)physical_address; + return get_physical_address((virtual_address_t*) virtual_address, pml4_root) == (physical_address_t*) physical_address; } @@ -951,13 +894,12 @@ bool PhysicalMemoryManager::is_mapped(uintptr_t physical_address, uintptr_t virt */ void* PhysicalMemoryManager::to_higher_region(uintptr_t physical_address) { - // If it's in the lower half then add the offset - if(physical_address < s_higher_half_kernel_offset) - return (void*)(physical_address + s_higher_half_kernel_offset); - - // Must be in the higher half - return (void*)physical_address; + // If it's in the lower half then add the offset + if (physical_address < s_higher_half_kernel_offset) + return (void*) (physical_address + s_higher_half_kernel_offset); + // Must be in the higher half + return (void*) physical_address; } /** @@ -967,12 +909,13 @@ void* PhysicalMemoryManager::to_higher_region(uintptr_t physical_address) { * @return The lower region address */ void* PhysicalMemoryManager::to_lower_region(uintptr_t virtual_address) { - // If it's in the lower half then add the offset - if(virtual_address > s_higher_half_kernel_offset) - return (void*)(virtual_address - s_higher_half_kernel_offset); - // Must be in the lower half - return (void*)virtual_address; + // If it's in the lower half then add the offset + if (virtual_address > s_higher_half_kernel_offset) + return (void*) (virtual_address - s_higher_half_kernel_offset); + + // Must be in the lower half + return (void*) virtual_address; } /** @@ -983,12 +926,11 @@ void* PhysicalMemoryManager::to_lower_region(uintptr_t virtual_address) { */ void* PhysicalMemoryManager::to_io_region(uintptr_t physical_address) { - if(physical_address < s_higher_half_mem_offset) - return (void*)(physical_address + s_higher_half_mem_offset); - - // Must be in the higher half - return (void*)physical_address; + if (physical_address < s_higher_half_mem_offset) + return (void*) (physical_address + s_higher_half_mem_offset); + // Must be in the higher half + return (void*) physical_address; } /** @@ -999,12 +941,11 @@ void* PhysicalMemoryManager::to_io_region(uintptr_t physical_address) { */ void* PhysicalMemoryManager::to_dm_region(uintptr_t physical_address) { - if(physical_address < s_higher_half_offset) - return (void*)(physical_address + s_hh_direct_map_offset); - - // Must be in the higher half - return (void*)physical_address; + if (physical_address < s_higher_half_offset) + return (void*) (physical_address + s_hh_direct_map_offset); + // Must be in the higher half + return (void*) physical_address; } /** @@ -1015,12 +956,11 @@ void* PhysicalMemoryManager::to_dm_region(uintptr_t physical_address) { */ void* PhysicalMemoryManager::from_dm_region(uintptr_t physical_address) { - if(physical_address > s_hh_direct_map_offset) - return (void*)(physical_address - s_hh_direct_map_offset); - - // Must be in the lower half - return (void*)physical_address; + if (physical_address > s_hh_direct_map_offset) + return (void*) (physical_address - s_hh_direct_map_offset); + // Must be in the lower half + return (void*) physical_address; } @@ -1031,7 +971,8 @@ void* PhysicalMemoryManager::from_dm_region(uintptr_t physical_address) { * @return True if the address is in the higher region, false otherwise */ bool PhysicalMemoryManager::in_higher_region(uintptr_t virtual_address) { - return virtual_address & (1l << 62); + + return virtual_address & (1l << 62); } @@ -1039,5 +980,6 @@ bool PhysicalMemoryManager::in_higher_region(uintptr_t virtual_address) { * @brief Unmaps the kernel physical memory from the lower half that was set up during the kernel boot */ void PhysicalMemoryManager::unmap_lower_kernel() { - p4_table[0] = 0; + + p4_table[0] = 0; } \ No newline at end of file diff --git a/kernel/src/memory/virtual.cpp b/kernel/src/memory/virtual.cpp index 5db0118c..b1b27cd5 100644 --- a/kernel/src/memory/virtual.cpp +++ b/kernel/src/memory/virtual.cpp @@ -10,100 +10,94 @@ using namespace MaxOS::memory; using namespace MaxOS::common; using namespace MaxOS::processes; -VirtualMemoryManager::VirtualMemoryManager() -{ +VirtualMemoryManager::VirtualMemoryManager() { - // Set the kernel flag - bool is_kernel = MemoryManager::s_kernel_memory_manager == nullptr; + // Set the kernel flag + bool is_kernel = MemoryManager::s_kernel_memory_manager == nullptr; + if (!is_kernel) { + // Get a new pml4 table + m_pml4_root_physical_address = (uint64_t*) PhysicalMemoryManager::s_current_manager->allocate_frame(); + m_pml4_root_address = (uint64_t*) PhysicalMemoryManager::to_dm_region((uint64_t) m_pml4_root_physical_address); - if(!is_kernel){ + // Clear the table + PhysicalMemoryManager::clean_page_table(m_pml4_root_address); - // Get a new pml4 table - m_pml4_root_physical_address = (uint64_t*)PhysicalMemoryManager::s_current_manager->allocate_frame(); - m_pml4_root_address = (uint64_t*)PhysicalMemoryManager::to_dm_region((uint64_t)m_pml4_root_physical_address); + // Map the higher half of the kernel (p4 256 - 511) + for (size_t i = 256; i < 512; i++) { - // Clear the table - PhysicalMemoryManager::clean_page_table(m_pml4_root_address); + // Recursive Map the pml4 table (so that we can access the new pml4 table later on) + if (i == 510) { + m_pml4_root_address[i] = (uint64_t) m_pml4_root_physical_address | Present | Write; + continue; + } - // Map the higher half of the kernel (p4 256 - 511) - for (size_t i = 256; i < 512; i++){ + // Set the new pml4 table to the old (kernel) pml4 table + m_pml4_root_address[i] = PhysicalMemoryManager::s_current_manager->pml4_root_address()[i]; - // Recursive Map the pml4 table (so that we can access the new pml4 table later on) - if(i == 510) { - m_pml4_root_address[i] = (uint64_t)m_pml4_root_physical_address | Present | Write; - continue; - } + } + Logger::DEBUG() << "Mapped higher half of kernel\n"; - // Set the new pml4 table to the old (kernel) pml4 table - m_pml4_root_address[i] = PhysicalMemoryManager::s_current_manager->pml4_root_address()[i]; - } - Logger::DEBUG() << "Mapped higher half of kernel\n"; + } else { + m_pml4_root_address = PhysicalMemoryManager::s_current_manager->pml4_root_address(); + m_pml4_root_physical_address = (uint64_t*) PhysicalMemoryManager::to_lower_region((uint64_t) m_pml4_root_address); + } + // Allocate space for the vmm + uint64_t vmm_space = PhysicalMemoryManager::align_to_page(PhysicalMemoryManager::s_hh_direct_map_offset + PhysicalMemoryManager::s_current_manager->memory_size() + PhysicalMemoryManager::s_page_size); //TODO: Check that am not slowly overwriting the kernel (filling first space with 0s bugs out the kernel) + void* vmm_space_physical = PhysicalMemoryManager::s_current_manager->allocate_frame(); + PhysicalMemoryManager::s_current_manager->map(vmm_space_physical, (virtual_address_t*) vmm_space, Present | Write, m_pml4_root_address); + Logger::DEBUG() << "VMM space: physical - 0x" << (uint64_t) vmm_space_physical << ", virtual - 0x" << (uint64_t) vmm_space << "\n"; + // Make sure everything is mapped correctly + if (!is_kernel) + ASSERT(vmm_space_physical != PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*) vmm_space, m_pml4_root_address), "Physical address does not match mapped address: 0x%x != 0x%x\n", vmm_space_physical, + PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*) vmm_space, m_pml4_root_address)); - }else{ - m_pml4_root_address = PhysicalMemoryManager::s_current_manager->pml4_root_address(); - m_pml4_root_physical_address = (uint64_t*)PhysicalMemoryManager::to_lower_region((uint64_t)m_pml4_root_address); - } + // Set the first region + m_first_region = (virtual_memory_region_t*) vmm_space; + m_current_region = m_first_region; + m_first_region->next = nullptr; - // Log the VMM's PML4 address - Logger::DEBUG() << "VMM PML4: physical - 0x" << (uint64_t)m_pml4_root_physical_address << ", virtual - 0x" << (uint64_t)m_pml4_root_address << "\n"; - - // Allocate space for the vmm - uint64_t vmm_space = PhysicalMemoryManager::align_to_page(PhysicalMemoryManager::s_hh_direct_map_offset + PhysicalMemoryManager::s_current_manager->memory_size() + PhysicalMemoryManager::s_page_size); //TODO: Check that am not slowly overwriting the kernel (filling first space with 0s bugs out the kernel) - void* vmm_space_physical = PhysicalMemoryManager::s_current_manager->allocate_frame(); - PhysicalMemoryManager::s_current_manager->map(vmm_space_physical, (virtual_address_t*)vmm_space, Present | Write, m_pml4_root_address); - Logger::DEBUG() << "VMM space: physical - 0x" << (uint64_t)vmm_space_physical << ", virtual - 0x" << (uint64_t)vmm_space << "\n"; - if(!is_kernel) - ASSERT(vmm_space_physical != PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*)vmm_space, m_pml4_root_address), "Physical address does not match mapped address: 0x%x != 0x%x\n", vmm_space_physical, PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*)vmm_space, m_pml4_root_address)); - - // Set the first region - m_first_region = (virtual_memory_region_t*)vmm_space; - m_current_region = m_first_region; - m_first_region->next = nullptr; - - // Calculate the next available address (kernel needs to reserve space for the higher half) - m_next_available_address = is_kernel ? vmm_space + s_reserved_space : PhysicalMemoryManager::s_page_size; - Logger::DEBUG() << "Next available address: 0x" << m_next_available_address << "\n"; + // Calculate the next available address (kernel needs to reserve space for the higher half) + m_next_available_address = is_kernel ? vmm_space + s_reserved_space : PhysicalMemoryManager::s_page_size; + Logger::DEBUG() << "Next available address: 0x" << m_next_available_address << "\n"; } VirtualMemoryManager::~VirtualMemoryManager() { - // Free all the frames used by the VMM - virtual_memory_region_t* region = m_first_region; + // Free all the frames used by the VMM + virtual_memory_region_t* region = m_first_region; - // Loop through the regions - while(region != nullptr){ + // Loop through the regions + while (region != nullptr) { - // Loop through the chunks - for (size_t i = 0; i < s_chunks_per_page; i++){ + // Loop through the chunks + for (size_t i = 0; i < s_chunks_per_page; i++) { - // Have reached the end? - if(i == m_current_chunk && region == m_current_region) - break; + // Have reached the end? + if (i == m_current_chunk && region == m_current_region) + break; - // Loop through the pages - size_t pages = PhysicalMemoryManager::size_to_frames(region->chunks[i].size); - for (size_t j = 0; j < pages; j++){ + // Loop through the pages + size_t pages = PhysicalMemoryManager::size_to_frames(region->chunks[i].size); + for (size_t j = 0; j < pages; j++) { - // Get the frame - physical_address_t* frame = PhysicalMemoryManager::s_current_manager -> get_physical_address((virtual_address_t*)region->chunks[i].start_address + (j * PhysicalMemoryManager::s_page_size), m_pml4_root_address); + // Get the frame + physical_address_t* frame = PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*) region->chunks[i].start_address + (j * PhysicalMemoryManager::s_page_size), m_pml4_root_address); - // Free the frame - PhysicalMemoryManager::s_current_manager->free_frame(frame); + // Free the frame + PhysicalMemoryManager::s_current_manager->free_frame(frame); - } + } + } - } - - // Move to the next region - region = region->next; - - } + // Move to the next region + region = region->next; + } } @@ -116,7 +110,7 @@ VirtualMemoryManager::~VirtualMemoryManager() { */ void* VirtualMemoryManager::allocate(size_t size, size_t flags) { - return allocate(0, size, flags); + return allocate(0, size, flags); } @@ -128,90 +122,90 @@ void* VirtualMemoryManager::allocate(size_t size, size_t flags) { * @param flags The flags to set on the memory * @return The address of the allocated memory or nullptr if failed */ -void *VirtualMemoryManager::allocate(uint64_t address, size_t size, size_t flags) { +void* VirtualMemoryManager::allocate(uint64_t address, size_t size, size_t flags) { - // Make sure allocating something - if(size == 0) - return nullptr; + // Make sure allocating something + if (size == 0) + return nullptr; - // If specific address is given - if(address != 0){ + // If specific address is given + if (address != 0) { - // Make sure isn't already allocated - if(address < m_next_available_address) - return nullptr; + // Make sure isn't already allocated + if (address < m_next_available_address) + return nullptr; - // Make sure its aligned - if(!PhysicalMemoryManager::check_aligned(address)) - return nullptr; + // Make sure its aligned + if (!PhysicalMemoryManager::check_aligned(address)) + return nullptr; - } + } - // Make sure the size is aligned - size = PhysicalMemoryManager::align_up_to_page(size, PhysicalMemoryManager::s_page_size); + // Make sure the size is aligned + size = PhysicalMemoryManager::align_up_to_page(size, PhysicalMemoryManager::s_page_size); - // Check the free list for a chunk (if not asking for a specific address) - free_chunk_t* reusable_chunk = address == 0 ? find_and_remove_free_chunk(size) : nullptr; - if(reusable_chunk != nullptr){ + // Check the free list for a chunk (if not asking for a specific address) + free_chunk_t* reusable_chunk = address == 0 ? find_and_remove_free_chunk(size) : nullptr; + if (reusable_chunk != nullptr) { - // If the chunk is not being reserved then the old memory needs to be unmapped - if(flags & Reserve){ + // If the chunk is not being reserved then the old memory needs to be unmapped + if (flags & Reserve) { - // Unmap the memory - size_t pages = PhysicalMemoryManager::size_to_frames(size); - for (size_t i = 0; i < pages; i++){ + // Unmap the memory + size_t pages = PhysicalMemoryManager::size_to_frames(size); + for (size_t i = 0; i < pages; i++) { - // Get the frame - physical_address_t* frame = PhysicalMemoryManager::s_current_manager -> get_physical_address((virtual_address_t*)reusable_chunk->start_address + (i * PhysicalMemoryManager::s_page_size), m_pml4_root_address); + // Get the frame + physical_address_t* frame = PhysicalMemoryManager::s_current_manager->get_physical_address((virtual_address_t*) reusable_chunk->start_address + (i * PhysicalMemoryManager::s_page_size), m_pml4_root_address); - // Free the frame - PhysicalMemoryManager::s_current_manager->free_frame(frame); + // Free the frame + PhysicalMemoryManager::s_current_manager->free_frame(frame); - } - } + } + } - // Return the address - return (void*)reusable_chunk->start_address; - } + // Return the address + return (void*) reusable_chunk->start_address; + } - // Is there space in the current region - if(m_current_chunk >= s_chunks_per_page) - new_region(); + // Is there space in the current region + if (m_current_chunk >= s_chunks_per_page) + new_region(); - // If needed to allocate at a specific address, fill with free memory up to that address to prevent fragmentation - if(address != 0) - fill_up_to_address(address, flags, false); + // If needed to allocate at a specific address, fill with free memory up to that address to prevent fragmentation + if (address != 0) + fill_up_to_address(address, flags, false); - // Allocate the memory - virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk]; - chunk->size = size; - chunk->flags = flags; - chunk->start_address = m_next_available_address; + // Allocate the memory + virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk]; + chunk->size = size; + chunk->flags = flags; + chunk->start_address = m_next_available_address; - // Update the next available address - m_next_available_address += size; - m_current_chunk++; + // Update the next available address + m_next_available_address += size; + m_current_chunk++; - // If just reserving the space don't map it - if(flags & Reserve) - return (void*)chunk->start_address; + // If just reserving the space don't map it + if (flags & Reserve) + return (void*) chunk->start_address; - // Map the memory - size_t pages = PhysicalMemoryManager::size_to_frames(size); - for (size_t i = 0; i < pages; i++){ + // Map the memory + size_t pages = PhysicalMemoryManager::size_to_frames(size); + for (size_t i = 0; i < pages; i++) { - // Allocate a new frame - physical_address_t* frame = PhysicalMemoryManager::s_current_manager->allocate_frame(); - ASSERT(frame != nullptr, "Failed to allocate frame (from current region)\n"); + // Allocate a new frame + physical_address_t* frame = PhysicalMemoryManager::s_current_manager->allocate_frame(); + ASSERT(frame != nullptr, "Failed to allocate frame (from current region)\n"); - // Map the frame - PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*)chunk->start_address + (i * PhysicalMemoryManager::s_page_size), Present | Write, m_pml4_root_address); + // Map the frame + PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*) chunk->start_address + (i * PhysicalMemoryManager::s_page_size), Present | Write, m_pml4_root_address); - } + } - // Return the address - return (void*)chunk->start_address; + // Return the address + return (void*) chunk->start_address; } /** @@ -219,29 +213,28 @@ void *VirtualMemoryManager::allocate(uint64_t address, size_t size, size_t flags */ void VirtualMemoryManager::new_region() { - // Space for the new region - physical_address_t* new_region_physical = PhysicalMemoryManager::s_current_manager->allocate_frame(); - ASSERT(new_region_physical != nullptr, "Failed to allocate new VMM region\n"); - - // Align the new region - auto* new_region = (virtual_memory_region_t*)PhysicalMemoryManager::align_to_page((uint64_t)m_current_region + PhysicalMemoryManager::s_page_size); + // Space for the new region + physical_address_t* new_region_physical = PhysicalMemoryManager::s_current_manager->allocate_frame(); + ASSERT(new_region_physical != nullptr, "Failed to allocate new VMM region\n"); - // Map the new region - PhysicalMemoryManager::s_current_manager->map(new_region_physical, (virtual_address_t*)new_region, Present | Write, m_pml4_root_address); - new_region->next = nullptr; + // Align the new region + auto* new_region = (virtual_memory_region_t*) PhysicalMemoryManager::align_to_page((uint64_t) m_current_region + PhysicalMemoryManager::s_page_size); - // Clear the new region - for (size_t i = 0; i < s_chunks_per_page; i++){ - new_region->chunks[i].size = 0; - new_region->chunks[i].flags = 0; - new_region->chunks[i].start_address = 0; - } + // Map the new region + PhysicalMemoryManager::s_current_manager->map(new_region_physical, (virtual_address_t*) new_region, Present | Write, m_pml4_root_address); + new_region->next = nullptr; - // Set the current region - m_current_region -> next = new_region; - m_current_chunk = 0; - m_current_region = new_region; + // Clear the new region + for (size_t i = 0; i < s_chunks_per_page; i++) { + new_region->chunks[i].size = 0; + new_region->chunks[i].flags = 0; + new_region->chunks[i].start_address = 0; + } + // Set the current region + m_current_region->next = new_region; + m_current_chunk = 0; + m_current_region = new_region; } /** @@ -249,54 +242,66 @@ void VirtualMemoryManager::new_region() { * * @param address The address of the memory to free */ -void VirtualMemoryManager::free(void *address) { - - // Make sure freeing something - if(address == nullptr) - return; - - // Find the chunk - virtual_memory_region_t* region = m_first_region; - virtual_memory_chunk_t* chunk = nullptr; - while(region != nullptr){ - - // Loop through the chunks - for (size_t i = 0; i < s_chunks_per_page; i++){ - - // Check if the address is in the chunk - if(region->chunks[i].start_address == (uintptr_t)address){ - chunk = ®ion->chunks[i]; - break; - } - } - - // If the chunk was found - if(chunk != nullptr) - break; - - // Move to the next region - region = region->next; - } - - // Make sure the chunk was found - if(chunk == nullptr) - return; - - // If the chunk is shared, don't unmap it incase other processes are using it - if(chunk->flags & Shared){ - - // Let the IPC handle the shared memory - Scheduler::scheduler_ipc()->free_shared_memory((uintptr_t)address); - - } - - // Add the chunk to the free list - add_free_chunk(chunk->start_address, chunk->size); - - // Clear the chunk - chunk->size = 0; - chunk->flags = 0; - chunk->start_address = 0; +void VirtualMemoryManager::free(void* address) { + + // Make sure freeing something + if (address == nullptr) + return; + + // Find the chunk + virtual_memory_region_t* region = m_first_region; + virtual_memory_chunk_t* chunk = nullptr; + while (region != nullptr) { + + // Loop through the chunks + for (size_t i = 0; i < s_chunks_per_page; i++) { + + // Check if the address is in the chunk + if (region->chunks[i].start_address == (uintptr_t) address) { + chunk = ®ion->chunks[i]; + break; + } + } + + // If the chunk was found + if (chunk != nullptr) + break; + + // Move to the next region + region = region->next; + } + + // Make sure the chunk was found + if (chunk == nullptr) + return; + + // If the chunk is shared, don't unmap it incase other processes are using it + if (chunk->flags & Shared) { + + // Find the resource + for(const auto& resource : Scheduler::current_process()->resource_manager.resources()){ + + // Skip non-shared memory resources + if(resource.second->type() != resource_type_t::SHARED_MEMORY) + continue; + + // Skip shared memory that points elsewhere + auto shared = (SharedMemory*)resource.second; + if((void*)shared->physical_address() != address) + continue; + + // Close the resource + Scheduler::current_process()->resource_manager.close_resource(resource.first, 0); + } + } + + // Add the chunk to the free list + add_free_chunk(chunk->start_address, chunk->size); + + // Clear the chunk + chunk->size = 0; + chunk->flags = 0; + chunk->start_address = 0; } /** @@ -306,26 +311,26 @@ void VirtualMemoryManager::free(void *address) { */ size_t VirtualMemoryManager::memory_used() { - // Loop through all the regions and add up the size of the allocated chunks - size_t result = 0; + // Loop through all the regions and add up the size of the allocated chunks + size_t result = 0; - // Iterate through the regions - virtual_memory_region_t *region = m_first_region; - while (region != nullptr) { + // Iterate through the regions + virtual_memory_region_t* region = m_first_region; + while (region != nullptr) { - // Loop through the chunks - for (size_t i = 0; i < s_chunks_per_page; i++) { + // Loop through the chunks + for (size_t i = 0; i < s_chunks_per_page; i++) { - // Check if the address is in the chunk - if (region->chunks[i].size != 0) - result += region->chunks[i].size; - } + // Check if the address is in the chunk + if (region->chunks[i].size != 0) + result += region->chunks[i].size; + } - // Move to the next region - region = region->next; - } + // Move to the next region + region = region->next; + } - return result; + return result; } /** @@ -336,15 +341,14 @@ size_t VirtualMemoryManager::memory_used() { */ void VirtualMemoryManager::add_free_chunk(uintptr_t start_address, size_t size) { + // Create the new chunk + auto* new_chunk = (free_chunk_t*) start_address; + new_chunk->start_address = start_address; + new_chunk->size = size; + new_chunk->next = m_free_chunks; - // Create the new chunk - auto* new_chunk = (free_chunk_t*)start_address; - new_chunk->start_address = start_address; - new_chunk->size = size; - new_chunk->next = m_free_chunks; - - // Set the new chunk - m_free_chunks = new_chunk; + // Set the new chunk + m_free_chunks = new_chunk; } /** @@ -355,35 +359,35 @@ void VirtualMemoryManager::add_free_chunk(uintptr_t start_address, size_t size) */ free_chunk_t* VirtualMemoryManager::find_and_remove_free_chunk(size_t size) { - // Find the chunk - free_chunk_t* current = m_free_chunks; - free_chunk_t* previous = nullptr; - while(current != nullptr){ + // Find the chunk + free_chunk_t* current = m_free_chunks; + free_chunk_t* previous = nullptr; + while (current != nullptr) { - // Check if the chunk is big enough - if(current->size >= size){ + // Check if the chunk is big enough + if (current->size >= size) { - // TODO: Some other time this will need to be split if the chunk is too big + // TODO: Some other time this will need to be split if the chunk is too big - // Remove the chunk - if(previous != nullptr){ - previous->next = current->next; - }else{ - m_free_chunks = current->next; - } + // Remove the chunk + if (previous != nullptr) { + previous->next = current->next; + } else { + m_free_chunks = current->next; + } - // Return the chunk - return current; + // Return the chunk + return current; - } + } - // Move to the next chunk - previous = current; - current = current->next; - } + // Move to the next chunk + previous = current; + current = current->next; + } - // No chunk found - return nullptr; + // No chunk found + return nullptr; } @@ -392,8 +396,9 @@ free_chunk_t* VirtualMemoryManager::find_and_remove_free_chunk(size_t size) { * * @return The physical address of the PML4 root */ -uint64_t *VirtualMemoryManager::pml4_root_address_physical() { - return m_pml4_root_physical_address; +uint64_t* VirtualMemoryManager::pml4_root_address_physical() { + + return m_pml4_root_physical_address; } /** @@ -402,16 +407,16 @@ uint64_t *VirtualMemoryManager::pml4_root_address_physical() { * @param name The name of the shared memory * @return The address of the shared memory in the VMM's address space */ -void *VirtualMemoryManager::load_shared_memory(const string& name) { +void* VirtualMemoryManager::load_shared_memory(const string& name) { - // Get the shared memory block - SharedMemory* block = Scheduler::scheduler_ipc()->get_shared_memory(name); + // Get the shared memory block + auto block = (SharedMemory*)Scheduler::current_process()->resource_manager.get_resource(name); - // Load the shared memory - if(block != nullptr) - return load_shared_memory(block -> physical_address(), block -> size()); + // Load the shared memory + if (block != nullptr) + return load_shared_memory(block->physical_address(), block->size()); - return nullptr; + return nullptr; } /** @@ -421,14 +426,14 @@ void *VirtualMemoryManager::load_shared_memory(const string& name) { * @param size The size of the shared memory * @return The address of the shared memory in the VMM's address space */ -void *VirtualMemoryManager::load_shared_memory(uintptr_t physical_address, size_t size) { +void* VirtualMemoryManager::load_shared_memory(uintptr_t physical_address, size_t size) { - // Make sure there is somthing to map - if(size == 0 || physical_address == 0) - return nullptr; + // Make sure there is somthing to map + if (size == 0 || physical_address == 0) + return nullptr; - // Load it into physical memory - return load_physical_into_address_space(physical_address, size, Shared); + // Load it into physical memory + return load_physical_into_address_space(physical_address, size, Shared); } /** @@ -439,22 +444,19 @@ void *VirtualMemoryManager::load_shared_memory(uintptr_t physical_address, size_ * @param flags The flags to set on the memory * @return The address of the memory in the VMM's address space */ -void *VirtualMemoryManager::load_physical_into_address_space(uintptr_t physical_address, size_t size, size_t flags){ - - // Reserve some space - void* address = allocate(size, flags | Reserve); +void* VirtualMemoryManager::load_physical_into_address_space(uintptr_t physical_address, size_t size, size_t flags) { - // Map the shared memory - size_t pages = PhysicalMemoryManager::size_to_frames(size); - for (size_t i = 0; i < pages; i++){ + // Reserve some space + void* address = allocate(size, flags | Reserve); - // Map the frame - PhysicalMemoryManager::s_current_manager->map((physical_address_t*)(physical_address + (i * PhysicalMemoryManager::s_page_size)), (virtual_address_t*)((uintptr_t)address + (i * PhysicalMemoryManager::s_page_size)), Present | Write, m_pml4_root_address); + // Map the shared memory + size_t pages = PhysicalMemoryManager::size_to_frames(size); + for (size_t i = 0; i < pages; i++) + PhysicalMemoryManager::s_current_manager->map((physical_address_t*) (physical_address + (i * PhysicalMemoryManager::s_page_size)), (virtual_address_t*) ((uintptr_t) address + (i * PhysicalMemoryManager::s_page_size)), Present | Write, m_pml4_root_address); - } - // All done - return address; + // All done + return address; } /** @@ -466,41 +468,41 @@ void *VirtualMemoryManager::load_physical_into_address_space(uintptr_t physical_ */ void VirtualMemoryManager::fill_up_to_address(uintptr_t address, size_t flags, bool mark_used) { - // Make sure the address is aligned - address = PhysicalMemoryManager::align_to_page(address); + // Make sure the address is aligned + address = PhysicalMemoryManager::align_to_page(address); - // Make sure the address is not before the next available address - ASSERT(address >= m_next_available_address, "FAILED TO FILL: Address is before the next available address - 0x%x < 0x%x\n", address, m_next_available_address); + // Make sure the address is not before the next available address + ASSERT(address >= m_next_available_address, "FAILED TO FILL: Address is before the next available address - 0x%x < 0x%x\n", address, m_next_available_address); - // Calculate the size - size_t size = address - m_next_available_address; + // Calculate the size + size_t size = address - m_next_available_address; - // Allocate the memory - virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk]; - chunk->size = size; - chunk->flags = flags; - chunk->start_address = m_next_available_address; + // Allocate the memory + virtual_memory_chunk_t* chunk = &m_current_region->chunks[m_current_chunk]; + chunk->size = size; + chunk->flags = flags; + chunk->start_address = m_next_available_address; - // Update the next available address - m_next_available_address += size; - m_current_chunk++; + // Update the next available address + m_next_available_address += size; + m_current_chunk++; - // Map the memory - size_t pages = PhysicalMemoryManager::size_to_frames(size); - for (size_t i = 0; i < pages; i++){ + // Map the memory + size_t pages = PhysicalMemoryManager::size_to_frames(size); + for (size_t i = 0; i < pages; i++) { - // Allocate a new frame - physical_address_t* frame = PhysicalMemoryManager::s_current_manager->allocate_frame(); - ASSERT(frame != nullptr, "Failed to allocate frame (from fill up)\n"); + // Allocate a new frame + physical_address_t* frame = PhysicalMemoryManager::s_current_manager->allocate_frame(); + ASSERT(frame != nullptr, "Failed to allocate frame (from fill up)\n"); - // Map the frame - PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*)chunk->start_address + (i * PhysicalMemoryManager::s_page_size), Present | Write, m_pml4_root_address); + // Map the frame + PhysicalMemoryManager::s_current_manager->map(frame, (virtual_address_t*) chunk->start_address + (i * PhysicalMemoryManager::s_page_size), Present | Write, m_pml4_root_address); - } + } - // Mark as free if needed - if(!mark_used) - free((void*)chunk->start_address); + // Mark as free if needed + if (!mark_used) + free((void*) chunk->start_address); } /** @@ -508,12 +510,12 @@ void VirtualMemoryManager::fill_up_to_address(uintptr_t address, size_t flags, b * * @return The virtual address or nullptr if not found */ -uint64_t *VirtualMemoryManager::pml4_root_address() { +uint64_t* VirtualMemoryManager::pml4_root_address() { - // Make sure the address is valid - if(m_pml4_root_address == nullptr) - return nullptr; + // Make sure the address is valid + if (m_pml4_root_address == nullptr) + return nullptr; - // Return the address - return m_pml4_root_address; + // Return the address + return m_pml4_root_address; } \ No newline at end of file diff --git a/kernel/src/processes/elf.cpp b/kernel/src/processes/elf.cpp index c3e2b5ca..fe7c5b6d 100644 --- a/kernel/src/processes/elf.cpp +++ b/kernel/src/processes/elf.cpp @@ -24,19 +24,18 @@ Elf64::Elf64(uintptr_t elf_header_address) /** * @brief Destructor for the Elf64 class */ -Elf64::~Elf64() -= default; +Elf64::~Elf64() = default; /** * @brief Loads the elf program into memory if a valid elf file */ void Elf64::load() { - // Check if valid - if(!is_valid()) return; //TODO: error handling when the syscall for this is implemented + //TODO: error handling when the syscall for this is implemented + if (!is_valid()) + return; - // Load the program headers - load_program_headers(); + load_program_headers(); } @@ -45,10 +44,9 @@ void Elf64::load() { * * @return The header of the elf file */ -elf_64_header_t *Elf64::header() const { +elf_64_header_t* Elf64::header() const { - // Return the header - return (elf_64_header_t*)m_elf_header_address; + return (elf_64_header_t*) m_elf_header_address; } /** @@ -57,16 +55,15 @@ elf_64_header_t *Elf64::header() const { * @param index The index of the program header * @return The program header at that index or nullptr if out of bounds */ -elf_64_program_header_t* Elf64::get_program_header(size_t index) { +elf_64_program_header_t* Elf64::get_program_header(size_t index) const { - // Check if within bounds - if(index >= header() -> program_header_count) return nullptr; + // Check bounds + if (index >= header()->program_header_count) + return nullptr; - // Get the address of the program headers - auto* program_headers = (elf_64_program_header_t*)(m_elf_header_address + header() -> program_header_offset); - - // Return the requested program header - return &program_headers[index]; + // Find the program headers and return the item + auto* program_headers = (elf_64_program_header_t*) (m_elf_header_address + header()->program_header_offset); + return &program_headers[index]; } @@ -76,91 +73,86 @@ elf_64_program_header_t* Elf64::get_program_header(size_t index) { * @param index The index of the section header * @return The section header at that index or nullptr if out of bounds */ -elf_64_section_header_t *Elf64::get_section_header(size_t index) { - - // Check if within bounds - if(index >= header() -> section_header_count) return nullptr; +elf_64_section_header_t* Elf64::get_section_header(size_t index) const { - // Get the address of the section headers - auto* section_headers = (elf_64_section_header_t*)(m_elf_header_address + header() -> section_header_offset); + // Check bound + if (index >= header()->section_header_count) + return nullptr; - // Return the requested section header - return §ion_headers[index]; + // Find the section headers and return the item + auto* section_headers = (elf_64_section_header_t*) (m_elf_header_address + header()->section_header_offset); + return §ion_headers[index]; } /** * @brief Checks if the elf file is valid for MaxOS runtime */ -bool Elf64::is_valid() { +bool Elf64::is_valid() const { - // Validate the magic number - for (size_t i = 0; i < 4; i++) - if (header() -> identification[i] != elf_magic[i]) - return false; + // Validate the magic number + for (size_t i = 0; i < 4; i++) + if (header()->identification[i] != elf_magic[i]) + return false; - // Check if the elf is 64 bit - if(header() -> identification[(int)ElfIdentification::Class] != (int)ElfClass::Bits64) - return false; + // Check if the elf is 64 bit + if (header()->identification[(int) ElfIdentification::Class] != (int) ElfClass::Bits64) + return false; - // Check if the elf is little endian - if(header() -> identification[(int)ElfIdentification::Data] != (int)ElfData::LittleEndian) - return false; + // Check if the elf is little endian + if (header()->identification[(int) ElfIdentification::Data] != (int) ElfData::LittleEndian) + return false; - // Check if the elf is version 1 - if(header() -> identification[(int)ElfIdentification::Version] != (int)ElfVersion::Current) - return false; + // Check if the elf is version 1 + if (header()->identification[(int) ElfIdentification::Version] != (int) ElfVersion::Current) + return false; - // Check if the elf is for the MaxOS platform - // if(header() -> identification[OSABI] != MaxOSABI) - // return false; TODO: Would be nice to have an OSABI + // Check if the elf is for the MaxOS platform + // if(header() -> identification[OSABI] != MaxOSABI) + // return false; TODO: Would be nice to have an OS ABI - // Check if the elf is executable - if(header() -> type != (int)ElfType::Executable) - return false; + // Check if the elf is executable + if (header()->type != (int) ElfType::Executable) + return false; - // Check if the elf is for the x86_64 platform - if(header() -> machine != (int)ElfMachine::x86_64) - return false; + // Check if the elf is for the x86_64 platform + if (header()->machine != (int) ElfMachine::x86_64) + return false; - // LGTM - return true; + // LGTM + return true; } /** * @brief Loop through the program headers and load the program into memory at the give address with the given size */ -void Elf64::load_program_headers() { - - // Loop through the program headers - for (size_t i = 0; i < header() -> program_header_count; i++) { - - // Get the program header - elf_64_program_header_t* program_header = get_program_header(i); +void Elf64::load_program_headers() const { - // Check if the program header is loadable - if(program_header -> type != (int)ElfProgramType::Load) - continue; + for (size_t i = 0; i < header()->program_header_count; i++) { - // Type of the program header - uint64_t flags = to_vmm_flags(program_header->flags); + // Get the header information + elf_64_program_header_t* program_header = get_program_header(i); - // Allocate space at the requested address - void* address = MemoryManager::s_current_memory_manager -> vmm() -> allocate(program_header -> virtual_address, program_header -> memory_size, Present | PageFlags::Write); - ASSERT(address != nullptr, "Failed to allocate memory for program header\n"); + // Only load headers that actually need loading + if (program_header->type != (int) ElfProgramType::Load) + continue; - // Copy the program to the address - memcpy((void*)address, (void*)(m_elf_header_address + program_header -> offset), program_header -> file_size); + // Allocate space at the requested address + void* address = MemoryManager::s_current_memory_manager->vmm()->allocate(program_header->virtual_address, program_header->memory_size, Present | Write); + ASSERT(address != nullptr, "Failed to allocate memory for program header\n"); - // Zero the rest of the memory if needed - size_t zero_size = program_header -> memory_size - program_header -> file_size; - memset((void*)((uintptr_t)address + program_header -> file_size), 0, zero_size); + // Copy the program into memory at that address + memcpy(address, (void*) (m_elf_header_address + program_header->offset), program_header->file_size); - // Now that we are done with modifying the memory, we should set the flags to the correct ones - PhysicalMemoryManager::s_current_manager -> change_page_flags((virtual_address_t*)address, flags, MemoryManager::s_current_memory_manager -> vmm() -> pml4_root_address()); - } + // Zero the rest of the memory if needed + size_t zero_size = program_header->memory_size - program_header->file_size; + memset((void*) ((uintptr_t) address + program_header->file_size), 0, zero_size); + // Once the memory has been copied can now mark the pages as read only etc + uint64_t flags = to_vmm_flags(program_header->flags); + PhysicalMemoryManager::s_current_manager->change_page_flags(address, flags, MemoryManager::s_current_memory_manager->vmm()->pml4_root_address()); + } } /** @@ -171,24 +163,21 @@ void Elf64::load_program_headers() { */ uint64_t Elf64::to_vmm_flags(uint32_t type) { - // Conversion - // ELF | VMM - // 0x0 | Executable (not used) - // 0x1 | Write - // 0x2 | Read + // Conversion + // ELF | VMM + // 0x0 | Executable + // 0x1 | Write + // 0x2 | Read - uint64_t flags = Present | User | NoExecute; + uint64_t flags = Present | User | NoExecute; - // Enable write - if(type & ElfWrite) - flags |= Write; + // Enable write + if (type & ElfWrite) + flags |= Write; - // Disable no execute - if(type & ElfProgramFlags::ElfExecute) - flags &= ~NoExecute; - - - return flags; - -} + // Disable no execute + if (type & ElfExecute) + flags &= ~NoExecute; + return flags; +} \ No newline at end of file diff --git a/kernel/src/processes/ipc.cpp b/kernel/src/processes/ipc.cpp index 83eb50cf..b65e9d32 100644 --- a/kernel/src/processes/ipc.cpp +++ b/kernel/src/processes/ipc.cpp @@ -2,6 +2,7 @@ // Created by 98max on 24/03/2025. // #include + using namespace MaxOS; using namespace MaxOS::processes; using namespace MaxOS::common; @@ -10,389 +11,140 @@ using namespace MaxOS::memory; #include #include //TODO: Circular dependency, need to fix -/** - * @brief Creates a new IPC handler - */ -InterProcessCommunicationManager::InterProcessCommunicationManager() { - - // Clear the spinlock - m_lock = Spinlock(); - -} - - -InterProcessCommunicationManager::~InterProcessCommunicationManager() { - - // Free all the shared memory - for(auto block : m_shared_memory_blocks){ - delete block; - } - - -} - /** * @brief Creates a new shared memory block * - * @param size The size of the block - * @param name The name of the block - * @return The shared memory block - */ -SharedMemory* InterProcessCommunicationManager::alloc_shared_memory(size_t size, string name) { - - // Wait for the lock - m_lock.lock(); - - // Make sure the name is unique - for(auto endpoint : m_message_endpoints){ - if(endpoint -> name -> equals(name)){ - m_lock.unlock(); - return nullptr; - } - } - - // Create the shared memory block - auto* block = new SharedMemory(name, size); - - // Add the block to the list - m_shared_memory_blocks.push_back(block); - - // Clear the lock - m_lock.unlock(); - - Logger::DEBUG() << "Created shared memory block " << name << " at 0x" << block -> physical_address() << "\n"; - - // Return the block - return block; -} - -/** - * @brief Gets a shared memory block by name - * * @param name The name of the block - * @return The shared memory block or nullptr if not found */ -SharedMemory* InterProcessCommunicationManager::get_shared_memory(const string& name) { - - // Wait for the lock - m_lock.lock(); - - // Find the block - for(auto block : m_shared_memory_blocks){ - if(block -> name -> equals(name)){ - block -> use_count++; - m_lock.unlock(); - return block; - } - } - - // Clear the lock - m_lock.unlock(); - - // Not found - return nullptr; +SharedMemory::SharedMemory(const string& name, size_t size, resource_type_t type) +: Resource(name, size, type), + m_size(size), + name(name) +{ + m_physical_address = (uintptr_t) PhysicalMemoryManager::s_current_manager->allocate_area(0, size); } -/** - * @brief Deletes a shared memory block by physical address - * - * @param physical_address The physical address of the block - */ -void InterProcessCommunicationManager::free_shared_memory(uintptr_t physical_address) { - - // Find the block - for(auto block : m_shared_memory_blocks){ - - if(block -> physical_address() == physical_address){ - free_shared_memory(block); - return; - } - - } -} +SharedMemory::~SharedMemory() = default; /** - * @brief Deletes a shared memory block by name + * @brief Get the address that the current process has this shared memory mapped to * - * @param name The name of the block + * @param buffer Not used + * @param size Not used + * @param flags Not used + * @return The virtual address in the current process's address space or 0 if not found */ -void InterProcessCommunicationManager::free_shared_memory(const string& name) { - - - // Find the block - for (auto block : m_shared_memory_blocks) { - - // Check if the block is the one we are looking for - if (!block->name->equals(name)) - continue; - - free_shared_memory(block); - - } +int SharedMemory::read(void* buffer, size_t size, size_t flags) { + // Process hasn't opened the resource + auto it = m_mappings.find(Scheduler::current_process()->pid()); + if(it == m_mappings.end()) + return 0; + return it->second; } /** - * @brief Deletes a shared memory block - * - * @param block The block to delete (will only free memory if no one is using it) - */ -void InterProcessCommunicationManager::free_shared_memory(SharedMemory* block) { - - // Wait for the lock - m_lock.lock(); - - // Decrement the use count - block->use_count--; - - // If the block is still in use - if (block->use_count > 0) { - m_lock.unlock(); - return; - } - - Logger::DEBUG() << "Deleting shared memory block " << block->name->c_str() << " at 0x" << block -> physical_address() << "\n"; - - // Free the block - delete block; - - // Clear the lock - m_lock.unlock(); - -} - -/** - * @brief Creates a endpoint for message passing + * @brief Gets the physical address of the shared memory block * - * @param name The name of the endpoint - * @return The endpoint + * @return The physical address */ -SharedMessageEndpoint* InterProcessCommunicationManager::create_message_endpoint(const string& name) { - - // Wait for the lock - m_lock.lock(); - - // Make sure the name is unique - SharedMessageEndpoint* existing = get_message_endpoint(name); - if(existing != nullptr){ - m_lock.unlock(); - return nullptr; - } - - // Create the endpoint - auto* endpoint = new SharedMessageEndpoint(name); - - // Add the endpoint to the list - m_message_endpoints.push_back(endpoint); - - // Free the lock - m_lock.unlock(); +uintptr_t SharedMemory::physical_address() const { - // Return the endpoint - return endpoint; + return m_physical_address; } /** - * @brief Gets a message endpoint by name + * @brief Gets the size of the shared memory block * - * @param name The name of the endpoint - * @return The endpoint or nullptr if not found + * @return The size */ -SharedMessageEndpoint* InterProcessCommunicationManager::get_message_endpoint(const string& name) { - - // Try to find the endpoint - for(auto endpoint : m_message_endpoints){ - if(endpoint -> name -> equals(name)) - return endpoint; - } - - // Not found - return nullptr; +size_t SharedMemory::size() const { + return m_size; } /** - * @brief Deletes a message endpoint by name - all messages will be lost + * @brief Map the shared memory into the address space of the owning process * - * @param name The name of the endpoint + * @param flags Not used for this resource */ -void InterProcessCommunicationManager::free_message_endpoint(const string& name) { +void SharedMemory::open(size_t flags) { - // Find the endpoint - SharedMessageEndpoint* endpoint = get_message_endpoint(name); + // Process has already opened this memory + auto it = m_mappings.find(Scheduler::current_process()->pid()); + if(it != m_mappings.end()) + return; - // Free the endpoint - free_message_endpoint(endpoint); + auto virtual_address = (uintptr_t)Scheduler::current_process()->memory_manager->vmm()->load_shared_memory(m_physical_address, m_size); + m_mappings.insert(Scheduler::current_process()->pid(), virtual_address); } /** - * @brief Deletes a message endpoint - all messages will be lost + * @brief Frees the page containing the shared memory * - * @param endpoint + * @param flags Not used for this resource */ -void InterProcessCommunicationManager::free_message_endpoint(SharedMessageEndpoint* endpoint) { - - // Make sure the endpoint exists - if(endpoint == nullptr) - return; - - // Make sure the endpoint is owned by the current process - if(endpoint -> owned_by_current_process()) - return; +void SharedMemory::close(size_t flags) { - // Delete the endpoint - delete endpoint; + PhysicalMemoryManager::s_current_manager->free_area(m_physical_address, m_size); } -/** - * @brief Creates a new shared memory block - * - * @param name The name of the block - */ -SharedMemory::SharedMemory(string name, size_t size) -: m_size(size), - name(new string(name)) +SharedMessageEndpoint::SharedMessageEndpoint(const string& name, size_t size, resource_type_t type) +: Resource(name, size, type) { - m_physical_address = (uintptr_t)PhysicalMemoryManager::s_current_manager -> allocate_area(0, size); - m_owner_pid = Scheduler::current_process()->pid(); - } -SharedMemory::~SharedMemory() { +SharedMessageEndpoint::~SharedMessageEndpoint() { - // Free the memory - PhysicalMemoryManager::s_current_manager->free_area(m_physical_address, m_size); + // Free the messages + for(auto& message : m_queue) + delete message; } -/** - * @brief Gets the physical address of the shared memory block - * - * @return The physical address - */ -uintptr_t SharedMemory::physical_address() const { - - return m_physical_address; - -} +// TODO: Add a min() max() to common somewhere /** - * @brief Gets the size of the shared memory block + * @brief Reads the first message from the endpoint or will yield until a message has been written * - * @return The size + * @param buffer Where to write the message to + * @param size Max size of the message to be read + * @return The amount of bytes read */ -size_t SharedMemory::size() const { - - return m_size; - -} - -SharedMessageEndpoint::SharedMessageEndpoint(string name) -: name(new string(name)) -{ - - // Make the queue in the user's memory space - m_queue = (ipc_message_queue_t*)MemoryManager::malloc(sizeof(ipc_message_queue_t)); - - // Get the owner - m_owner_pid = Scheduler::current_process() -> pid(); - - -} - -SharedMessageEndpoint::~SharedMessageEndpoint() { - - // Wait for the lock - m_message_lock.lock(); - - // Delete the messages - ipc_message_t* message = m_queue -> messages; - while(message != nullptr){ - auto* next = (ipc_message_t*)message -> next_message; - MemoryManager::free(message -> message_buffer); - message = next; - } +int SharedMessageEndpoint::read(void* buffer, size_t size, size_t flags) { - // Free the queue - MemoryManager::free(m_queue); + // Wait for a message + if(m_queue.empty()) + return -1 * (int)resource_error_base_t::SHOULD_BLOCK; - // Free the name - delete name; + // Read the message into the buffer + buffer_t* message = m_queue.pop_front(); + size_t readable = size > message->capacity() ? message->capacity() : size; + memcpy(buffer, message -> raw(), readable); - m_message_lock.unlock(); + return readable; } /** - * @brief Gets the message queue for the endpoint + * @brief Handles writing to the message endpoint resource by writing the buffer as a message * - * @return The message queue - */ -ipc_message_queue_t *SharedMessageEndpoint::queue() const { - - // Return the queue - return m_queue; - -} - -/** - * @brief Checks if the current process can delete the endpoint - * - * @return True if the current process can delete the endpoint + * @param buffer The message to write to the endpoint + * @param size The size of the message + * @return The amount of bytes written */ -bool SharedMessageEndpoint::owned_by_current_process() const { +int SharedMessageEndpoint::write(void const* buffer, size_t size, size_t flags) { - // Check if the owner is the current process - return m_owner_pid == Scheduler::current_process() -> pid(); + m_message_lock.lock(); -} + // Create the message + auto* new_message = new buffer_t(size); + new_message->copy_from(buffer, size); + m_queue.push_back(new_message); -/** - * @brief Queues a message to be processed by the endpoint (buffer can be freed as it is copied in to the receiving process's memory) - * - * @param message The message to queue - * @param size The size of the message - */ -void SharedMessageEndpoint::queue_message(void *message, size_t size) { - - // Wait for the lock - m_message_lock.lock(); - - // Copy the buffer into the kernel so that the endpoint can access it - auto* kernel_copy = (uintptr_t*)new char[size]; - memcpy(kernel_copy, message, size); - - //Switch to endpoint's memory space - MemoryManager::switch_active_memory_manager(Scheduler::get_process(m_owner_pid) -> memory_manager); - - // Create the message & copy it into the endpoint's memory space - auto* new_message = (ipc_message_t*)MemoryManager::malloc(sizeof(ipc_message_t)); - void* new_buffer = MemoryManager::malloc(size); - new_message -> message_buffer = memcpy(new_buffer, kernel_copy, size); - new_message -> message_size = size; - new_message -> next_message = 0; - - // Add the message to the end of the queue - ipc_message_t* current = m_queue -> messages; - while(current != nullptr){ - if(current -> next_message == 0){ - current -> next_message = (uintptr_t)new_message; - break; - } - current = (ipc_message_t*)current -> next_message; - } - - // If it was the first message - if (current == nullptr) - m_queue->messages = new_message; - - //Switch back from endpoint's memory space - MemoryManager::switch_active_memory_manager(Scheduler::current_process() -> memory_manager); - - // Free the lock & kernel copy - delete[] kernel_copy; - m_message_lock.unlock(); -} + m_message_lock.unlock(); + return size; +} \ No newline at end of file diff --git a/kernel/src/processes/process.cpp b/kernel/src/processes/process.cpp index 723aaa31..2a5fb274 100644 --- a/kernel/src/processes/process.cpp +++ b/kernel/src/processes/process.cpp @@ -15,54 +15,52 @@ using namespace MaxOS::common; /** * @brief Constructor for the Thread class */ -Thread::Thread(void (*_entry_point)(void *), void *args, int arg_amount, Process* parent) { - - // Basic setup - thread_state = ThreadState::NEW; - wakeup_time = 0; - ticks = 0; - - // Create the stack - m_stack_pointer = (uintptr_t)MemoryManager::malloc(s_stack_size); - - // Create the TSS stack - if(parent -> is_kernel) { - - // Use the kernel stack - m_tss_stack_pointer = CPU::tss.rsp0; - - } else{ - m_tss_stack_pointer = (uintptr_t)MemoryManager::kmalloc(s_stack_size) + s_stack_size; - } - - // Mak sure there is a stack - ASSERT(m_stack_pointer != 0 && m_tss_stack_pointer != 0, "Failed to allocate stack for thread"); - - // Set up the execution state - execution_state = new cpu_status_t(); - execution_state->rip = (uint64_t)_entry_point; - execution_state->ss = parent -> is_kernel ? 0x10 : 0x23; - execution_state->cs = parent -> is_kernel ? 0x8 : 0x1B; - execution_state->rflags = 0x202; - execution_state->interrupt_number = 0; - execution_state->error_code = 0; - execution_state->rsp = (uint64_t)m_stack_pointer; - execution_state->rbp = 0; - - // Copy the args into userspace - uint64_t argc = arg_amount; - void* argv = MemoryManager::malloc(arg_amount * sizeof(void*)); - memcpy(argv, args, arg_amount * sizeof(void*)); - - - execution_state->rdi = argc; - execution_state->rsi = (uint64_t)argv; - //execution_state->rdx = (uint64_t)env_args; - - // Begin scheduling this thread - parent_pid = parent->pid(); - tid = Scheduler::system_scheduler() -> add_thread(this); - +Thread::Thread(void (* _entry_point)(void*), void* args, int arg_amount, Process* parent) { + + // Basic setup + thread_state = ThreadState::NEW; + wakeup_time = 0; + ticks = 0; + + // Create the stack + m_stack_pointer = (uintptr_t) MemoryManager::malloc(s_stack_size); + + // Create the TSS stack + if (parent->is_kernel) { + + // Use the kernel stack + m_tss_stack_pointer = CPU::tss.rsp0; + + } else { + m_tss_stack_pointer = (uintptr_t) MemoryManager::kmalloc(s_stack_size) + s_stack_size; + } + + // Mak sure there is a stack + ASSERT(m_stack_pointer != 0 && m_tss_stack_pointer != 0, "Failed to allocate stack for thread"); + + // Set up the execution state + execution_state = new cpu_status_t; + execution_state->rip = (uint64_t) _entry_point; + execution_state->ss = parent->is_kernel ? 0x10 : 0x23; + execution_state->cs = parent->is_kernel ? 0x8 : 0x1B; + execution_state->rflags = 0x202; + execution_state->interrupt_number = 0; + execution_state->error_code = 0; + execution_state->rsp = m_stack_pointer; + execution_state->rbp = 0; + + // Copy the args into userspace + uint64_t argc = arg_amount; + void* argv = MemoryManager::malloc(arg_amount * sizeof(void*)); + memcpy(argv, args, arg_amount * sizeof(void*)); + + execution_state->rdi = argc; + execution_state->rsi = (uint64_t) argv; + //execution_state->rdx = (uint64_t)env_args; + + // Begin scheduling this thread + parent_pid = parent->pid(); + tid = Scheduler::system_scheduler()->add_thread(this); } /** @@ -78,13 +76,12 @@ Thread::~Thread() = default; */ cpu_status_t* Thread::sleep(size_t milliseconds) { - // Update the vars - thread_state = ThreadState::SLEEPING; - wakeup_time = Scheduler::system_scheduler() -> ticks() + milliseconds; - - // Yield - return Scheduler::system_scheduler() -> yield(); + // Update the state + thread_state = ThreadState::SLEEPING; + wakeup_time = Scheduler::system_scheduler()->ticks() + milliseconds; + // Let other processes do stuff while this thread is sleeping + return Scheduler::system_scheduler()->yield(); } /** @@ -92,13 +89,12 @@ cpu_status_t* Thread::sleep(size_t milliseconds) { */ void Thread::save_sse_state() { - // Ensure the state saving is enabled - if(!CPU::s_xsave) - return; - - // Save the state - asm volatile("fxsave %0" : "=m" (m_sse_save_region)); + // Ensure the state saving is enabled + if (!CPU::s_xsave) + return; + // Save the state + asm volatile("fxsave %0" : "=m" (m_sse_save_region)); } /** @@ -106,13 +102,12 @@ void Thread::save_sse_state() { */ void Thread::restore_sse_state() { - // Ensure the state saving is enabled - if(!CPU::s_xsave) - return; - - // Restore the state - asm volatile("fxrstor %0" : : "m" (m_sse_save_region)); + // Ensure the state saving is enabled + if (!CPU::s_xsave) + return; + // Restore the state + asm volatile("fxrstor %0" : : "m" (m_sse_save_region)); } /** @@ -125,14 +120,17 @@ Process::Process(const string& p_name, bool is_kernel) : is_kernel(is_kernel), name(p_name) { - // Pause interrupts while creating the process - asm("cli"); - // Basic setup - m_pid = Scheduler::system_scheduler() ->add_process(this); + // Pause interrupts while creating the process + asm("cli"); + + // Basic setup + m_pid = Scheduler::system_scheduler()->add_process(this); + + // If it is a kernel process then don't need a new memory manager + memory_manager = is_kernel ? MemoryManager::s_kernel_memory_manager : new MemoryManager(); - // If it is a kernel process then don't need a new memory manager - memory_manager = is_kernel ? MemoryManager::s_kernel_memory_manager : new MemoryManager(); + // Resuming interrupts are done when adding a thread } /** @@ -144,16 +142,15 @@ Process::Process(const string& p_name, bool is_kernel) * @param arg_amount The amount of arguments * @param is_kernel If the process is a kernel process */ -Process::Process(const string& p_name, void (*_entry_point)(void *), void *args, int arg_amount, bool is_kernel) +Process::Process(const string& p_name, void (* _entry_point)(void*), void* args, int arg_amount, bool is_kernel) : Process(p_name, is_kernel) { - // Create the main thread - auto* main_thread = new Thread(_entry_point, args, arg_amount, this); - - // Add the thread - add_thread(main_thread); + // Create the main thread + auto* main_thread = new Thread(_entry_point, args, arg_amount, this); + // Add the thread + add_thread(main_thread); } /** @@ -165,21 +162,17 @@ Process::Process(const string& p_name, void (*_entry_point)(void *), void *args, * @param elf The elf file to load the process from * @param is_kernel If the process is a kernel process */ -Process::Process(const string& p_name, void *args, int arg_amount, Elf64* elf, bool is_kernel) +Process::Process(const string& p_name, void* args, int arg_amount, Elf64* elf, bool is_kernel) : Process(p_name, is_kernel) { + // Get the entry point + elf->load(); + auto* entry_point = (void (*)(void*)) elf->header()->entry; - // Get the entry point - elf -> load(); - auto* entry_point = (void (*)(void *))elf -> header() -> entry; - - // Create the main thread - auto* main_thread = new Thread(entry_point, args, arg_amount, this); - add_thread(main_thread); - - // Free the elf - delete elf; + // Create the main thread + auto* main_thread = new Thread(entry_point, args, arg_amount, this); + add_thread(main_thread); } @@ -188,18 +181,18 @@ Process::Process(const string& p_name, void *args, int arg_amount, Elf64* elf, b */ Process::~Process() { - uint64_t pages = PhysicalMemoryManager::s_current_manager -> memory_used(); + uint64_t pages = PhysicalMemoryManager::s_current_manager->memory_used(); - // Free the threads - for (auto thread : m_threads) - delete thread; + // Free the threads + for (auto thread: m_threads) + delete thread; - // Free the memory manager (only if it was created) - if(!is_kernel) - delete memory_manager; + // Free the memory manager (only if it was created) + if (!is_kernel) + delete memory_manager; - // Log the cleanup - Logger::DEBUG() << "Process " << name.c_str() << " cleaned up, memory before: " << pages << " bytes, after cleanup: " << PhysicalMemoryManager::s_current_manager -> memory_used() << " bytes\n"; + // Log the cleanup + Logger::DEBUG() << "Process " << name.c_str() << " cleaned up, memory before: " << pages << " bytes, after cleanup: " << PhysicalMemoryManager::s_current_manager->memory_used() << " bytes\n"; } /** @@ -207,20 +200,17 @@ Process::~Process() { * * @param thread The thread to add */ -void Process::add_thread(Thread *thread) { +void Process::add_thread(Thread* thread) { - // Pause interrupts while adding the thread - asm("cli"); + // Pause interrupts while adding the thread + asm("cli"); - // Store the thread - m_threads.push_back(thread); - - // Set the pid - thread->parent_pid = m_pid; - - // Can now resume interrupts - asm("sti"); + // Store the thread + m_threads.push_back(thread); + thread->parent_pid = m_pid; + // Can now resume interrupts + asm("sti"); } /** @@ -230,27 +220,26 @@ void Process::add_thread(Thread *thread) { */ void Process::remove_thread(uint64_t tid) { - // Find the thread - for (uint32_t i = 0; i < m_threads.size(); i++) { - if (m_threads[i]->tid == tid) { - - // Get the thread - Thread* thread = m_threads[i]; + // Find the thread + for (uint32_t i = 0; i < m_threads.size(); i++) { - // Delete the thread - delete thread; + // Thread is not what is being removed + if (m_threads[i]->tid != tid) + continue; - // Remove the thread from the list - m_threads.erase(m_threads.begin() + i); + // Delete the thread + Thread* thread = m_threads[i]; + delete thread; + // Remove the thread from the list + m_threads.erase(m_threads.begin() + i); - // If there are no more threads then delete the process (done on the scheduler side) - if (m_threads.empty()) - Scheduler::system_scheduler() -> remove_process(this); + // If there are no more threads then delete the process from the scheduler + if (m_threads.empty()) + Scheduler::system_scheduler()->remove_process(this); - return; - } - } + return; + } } /** @@ -260,17 +249,16 @@ void Process::remove_thread(uint64_t tid) { */ void Process::set_pid(uint64_t pid) { - // Check if the pid is already set - if (m_pid != 0) - return; + // Check if the pid is already set + if (m_pid != 0) + return; - // Set the pid - m_pid = pid; - - // Assign the pid to the threads - for (auto thread : m_threads) - thread->parent_pid = pid; + // Set the pid + m_pid = pid; + // Assign the pid to the threads + for (auto thread: m_threads) + thread->parent_pid = pid; } @@ -279,9 +267,7 @@ void Process::set_pid(uint64_t pid) { */ Vector Process::threads() { - // Return the threads - return m_threads; - + return m_threads; } /** @@ -290,7 +276,8 @@ Vector Process::threads() { * @return The pid of the process */ uint64_t Process::pid() const { - return m_pid; + + return m_pid; } /** @@ -300,9 +287,9 @@ uint64_t Process::pid() const { */ uint64_t Process::total_ticks() { - uint64_t total_ticks = 0; - for (auto thread : m_threads) - total_ticks += thread->ticks; + uint64_t total_ticks = 0; + for (auto thread: m_threads) + total_ticks += thread->ticks; - return total_ticks; + return total_ticks; } diff --git a/kernel/src/processes/resource.cpp b/kernel/src/processes/resource.cpp new file mode 100644 index 00000000..4befd963 --- /dev/null +++ b/kernel/src/processes/resource.cpp @@ -0,0 +1,328 @@ +// +// Created by 98max on 8/26/2025. +// +#include + +using namespace MaxOS; +using namespace MaxOS::processes; + +Resource::Resource(const string& name, size_t flags, resource_type_t type) +: m_name(name), + m_type(type) +{ + +} + +Resource::~Resource() = default; + +/** + * @brief Opens the resource + * + * @param flags Optional flags to pass (unused by default but resource type specific) + */ +void Resource::open(size_t flags) { + +} + + +/** + * @brief Closes the resource + * + * @param flags Optional flags to pass (unused by default but resource type specific) + */ +void Resource::close(size_t flags) { + +} + +/** + * @brief Read a certain amount of bytes from a resource + * + * @param buffer The buffer to read into + * @param size How many bytes to read + * @param flags Optional flags to pass (unused by default but resource type specific) + * @return How many bytes were successfully read (negative can be used as errors) + */ +int Resource::read(void* buffer, size_t size, size_t flags) { + return 0; +} + +/** + * @brief Write a certain amount of bytes to a resource + * + * @param buffer The buffer to read from + * @param size How many bytes to write + * @param flags Optional flags to pass (unused by default but resource type specific) + * @return How many bytes were successfully written + */ +int Resource::write(void const* buffer, size_t size, size_t flags) { + + return 0; +} + +/** + * @brief Gets the name of this resource + * + * @return The name + */ +string Resource::name() { + return m_name; +} + +/** + * @brief Gets the type of this resource + * + * @return The type + */ +resource_type_t Resource::type() { + + return m_type; +} + +BaseResourceRegistry::BaseResourceRegistry(resource_type_t type) +: m_type(type) +{ + GlobalResourceRegistry::add_registry(type, this); +} + +BaseResourceRegistry::~BaseResourceRegistry(){ + + GlobalResourceRegistry::remove_registry(this); +} + +/** + * @brief Gets the type of resources this registry handles + * + * @return The type + */ +resource_type_t BaseResourceRegistry::type() { + + return m_type; +} + +/** + * @brief Gets a resource from the registry by name + * + * @param name The name of the resource to find + * @return The resource or nullptr if the resource was not found + */ +Resource* BaseResourceRegistry::get_resource(string const& name) { + + // Resource isn't stored in this registry + if(m_resources.find(name) == m_resources.end()) + return nullptr; + + // Increment the use count of the resource and return it + m_resource_uses[name]++; + return m_resources[name]; + +} + +/** + * @brief Registers a resource in the registry + * + * @param resource The resource to store + * @param name The name of the resource (must not already be in use) + * @return True if the register was successful, false if not + */ +bool BaseResourceRegistry::register_resource(Resource* resource) { + + // Resource name is already stored in this registry + if(m_resources.find(resource->name()) != m_resources.end()) + return false; + + m_resources.insert(resource->name(), resource); + m_resource_uses.insert(resource->name(), 0); + return true; +} + + +/** + * @brief Close the resource provided, if it exists in this registry + * + * @param resource The resource to close + * @param flags Optional flags to pass (unused by default but registry type specific) + */ +void BaseResourceRegistry::close_resource(Resource* resource, size_t flags) { + + // Resource isn't stored in this registry + if(m_resources.find(resource->name()) == m_resources.end()) + return; + + m_resource_uses[resource->name()]--; + + // Don't close a resource that has more processes using it + if(m_resource_uses[resource->name()]) + return; + + // Can safely close the resource + resource->close(flags); + m_resources.erase(resource->name()); + m_resource_uses.erase(resource->name()); + delete resource; +} + +/** + * @brief Try to create a new resource with the specified name, will fail if the name is in use + * + * @param name The name of the resource to create + * @param flags Optional flags to pass (unused by default but registry type specific) + * @return The resource created or nullptr if failed to create the resource + */ +Resource* BaseResourceRegistry::create_resource(string const& name, size_t flags) { + return nullptr; +} + +GlobalResourceRegistry::GlobalResourceRegistry() { + s_current = this; + +} + +GlobalResourceRegistry::~GlobalResourceRegistry() { + if(s_current == this) + s_current = nullptr; + +} + +/** + * @brief Gets a registry of a type + * + * @param type The type of registry to get + * @return The registry or nullptr if not found + */ +BaseResourceRegistry* GlobalResourceRegistry::get_registry(resource_type_t type) { + + auto registry = s_current->m_registries.find(type); + if(registry == s_current->m_registries.end()) + return nullptr; + + return registry->second; +} + +/** + * @brief Adds a registry to the global list if there is not already one for that type + * + * @param type The type of registry being added + * @param registry The registry to add + */ +void GlobalResourceRegistry::add_registry(resource_type_t type, BaseResourceRegistry* registry) { + + // Does it already exist? + if(s_current->m_registries.find(type) != s_current->m_registries.end()) + return; + + s_current->m_registries.insert(type, registry); +} + +/** + * @brief Adds a registry to the global list if there is not already one for that type + * + * @param type The type of registry being added + * @param registry The registry to add + */ +void GlobalResourceRegistry::remove_registry(BaseResourceRegistry* registry) { + + // Does it already exist? + if(s_current->m_registries.find(registry->type()) == s_current->m_registries.end()) + return; + + s_current->m_registries.erase(registry->type()); + +} + +ResourceManager::ResourceManager() = default; + +ResourceManager::~ResourceManager(){ + + // Collect all resources (as closing will break iteration) + common::Vector handles; + for (const auto& kv : m_resources) + handles.push_back(kv.first); + + // Close the resources + for (auto h : handles) + close_resource(h, 0); + +}; + +/** + * @brief Get the resources currently open + * + * @return The resources + */ +common::Map ResourceManager::resources() { + + return m_resources; +} + +/** + * @brief Registers a resource with the resource manager and then opens it + * + * @param resource The resource to register + * @param name The name of the resource + * @return The handle id of the resource or 0 if failed + */ +uint64_t ResourceManager::open_resource(resource_type_t type, string const& name, size_t flags) { + + // Get the resource + auto resource = GlobalResourceRegistry::get_registry(type) -> get_resource(name); + if(!resource) + return 0; + + // Store it + m_resources.insert(m_next_handle, resource); + + // Open it + resource->open(flags); + return m_next_handle++; +} + +/** + * @brief Un registers the resource and then closes it + * + * @param handle The handle number of the resource + * @param flags Flags to pass to the closing + */ +void ResourceManager::close_resource(uint64_t handle, size_t flags) { + + auto it = m_resources.find(handle); + if(it == m_resources.end()) + return; + + // Remove it + Resource* resource = it->second; + m_resources.erase(handle); + + // Close it + GlobalResourceRegistry::get_registry(resource->type()) -> close_resource(resource, flags); +} + +/** + * @brief Gets a resource based on the handle id + * + * @param handle The handle number of the resource + * @return The resource or nullptr if not found + */ +Resource* ResourceManager::get_resource(uint64_t handle) { + + if(m_resources.find(handle) == m_resources.end()) + return nullptr; + + return m_resources[handle]; +} + +/** + * @brief Finds a resource by name + * + * @param name The name of the resource + * @return The resource or nullptr if not found + */ +Resource* ResourceManager::get_resource(string const& name) { + + for(const auto& resource : m_resources) + if(resource.second->name() == name) + return resource.second; + + return nullptr; +} + + diff --git a/kernel/src/processes/scheduler.cpp b/kernel/src/processes/scheduler.cpp index 91f792e2..8a0586fb 100644 --- a/kernel/src/processes/scheduler.cpp +++ b/kernel/src/processes/scheduler.cpp @@ -11,39 +11,36 @@ using namespace MaxOS::memory; using namespace MaxOS::hardwarecommunication; using namespace MaxOS::system; - Scheduler::Scheduler(Multiboot& multiboot) : InterruptHandler(0x20), m_current_thread_index(0), m_active(false), m_ticks(0), m_next_pid(-1), - m_next_tid(-1) - + m_next_tid(-1), + m_shared_memory_registry(resource_type_t::SHARED_MEMORY), + m_shared_messages_registry(resource_type_t::MESSAGE_ENDPOINT) { - /// Setup the basic scheduler - Logger::INFO() << "Setting up Scheduler \n"; - s_instance = this; - m_ipc = new InterProcessCommunicationManager(); + // Set up the basic scheduler + Logger::INFO() << "Setting up Scheduler \n"; + s_instance = this; - // Create the idle process - auto* idle = new Process("kernelMain Idle", nullptr, nullptr,0, true); - idle->memory_manager = MemoryManager::s_kernel_memory_manager; - add_process(idle); - idle->set_pid(0); + // Create the idle process + auto* idle = new Process("kernelMain Idle", nullptr, nullptr, 0, true); + idle->memory_manager = MemoryManager::s_kernel_memory_manager; + add_process(idle); + idle->set_pid(0); - // Load the elfs - load_multiboot_elfs(&multiboot); + // Load the elfs + load_multiboot_elfs(&multiboot); } Scheduler::~Scheduler() { - s_instance = nullptr; - m_active = false; - - // Delete the IPC handler - delete m_ipc; + // Deactivate this scheduler + s_instance = nullptr; + m_active = false; } /** @@ -52,13 +49,9 @@ Scheduler::~Scheduler() { * @param status The current CPU status * @return The new CPU status */ -cpu_status_t* Scheduler::handle_interrupt(cpu_status_t *status) { +cpu_status_t* Scheduler::handle_interrupt(cpu_status_t* status) { - // Schedule the next thread - return schedule(status); - - /// Note: Could have set scheduler to just be the handle interrupt function, - //// but in the future there may be a need to schedule at other times + return schedule(status); } @@ -68,26 +61,24 @@ cpu_status_t* Scheduler::handle_interrupt(cpu_status_t *status) { * @param cpu_state The current CPU state * @return The next CPU state */ -cpu_status_t *Scheduler::schedule(cpu_status_t* cpu_state) { - - // If there are no threads to schedule or not active, return the current state - if (m_threads.empty() || !m_active) - return cpu_state; - +cpu_status_t* Scheduler::schedule(cpu_status_t* cpu_state) { - // Thread that we are dealing with - Thread* current_thread = m_threads[m_current_thread_index]; + // Scheduler cant schedule anything + if (m_threads.empty() || !m_active) + return cpu_state; - // Ticked - m_ticks++; - current_thread->ticks++; + // Thread that we are dealing with + Thread* current_thread = m_threads[m_current_thread_index]; - // Wait for a bit so that the scheduler doesn't run too fast - if (m_ticks % s_ticks_per_event != 0) return cpu_state; + // Ticked + m_ticks++; + current_thread->ticks++; - // Schedule the next thread - return schedule_next(cpu_state); + // Wait for a bit so that the scheduler doesn't run too fast TODO: fix + if (m_ticks % s_ticks_per_event != 0) return cpu_state; + // Schedule the next thread + return schedule_next(cpu_state); } /** @@ -96,73 +87,68 @@ cpu_status_t *Scheduler::schedule(cpu_status_t* cpu_state) { * @param status The current CPU status of the thread * @return The next CPU status */ -system::cpu_status_t *Scheduler::schedule_next(system::cpu_status_t* cpu_state) { +cpu_status_t* Scheduler::schedule_next(cpu_status_t* cpu_state) { - // Get the current thread - Thread* current_thread = m_threads[m_current_thread_index]; + // Get the thread that is executing right now + Thread* current_thread = m_threads[m_current_thread_index]; - // Save the current state - current_thread->execution_state = cpu_state; - current_thread -> save_sse_state(); - if(current_thread->thread_state == ThreadState::RUNNING) - current_thread->thread_state = ThreadState::WAITING; + // Save its state + current_thread->execution_state = cpu_state; + current_thread->save_sse_state(); + if (current_thread->thread_state == ThreadState::RUNNING) + current_thread->thread_state = ThreadState::READY; - // Switch to the next thread - m_current_thread_index++; - m_current_thread_index %= m_threads.size(); + // Switch to the thread that will now run + m_current_thread_index++; + m_current_thread_index %= m_threads.size(); + current_thread = m_threads[m_current_thread_index]; - current_thread = m_threads[m_current_thread_index]; + Process* owner_process = current_process(); - // If the current thread is in the process then we can get the process - Process* owner_process = current_process(); + // Handle state changes + switch (current_thread->thread_state) { - // Handle state changes - switch (current_thread->thread_state) { + case ThreadState::NEW: + current_thread->thread_state = ThreadState::RUNNING; + break; - case ThreadState::NEW: - current_thread->thread_state = ThreadState::RUNNING; - break; + case ThreadState::SLEEPING: - case ThreadState::SLEEPING: + // If the wake-up time hasn't occurred yet, run the next thread + if (current_thread->wakeup_time > m_ticks) + return schedule_next(current_thread->execution_state); - // If the wake-up time hasn't occurred yet, run the next thread - if (current_thread->wakeup_time > m_ticks) - return schedule_next(current_thread->execution_state); + break; - break; + case ThreadState::STOPPED: - case ThreadState::STOPPED: + // Find the process that has the thread and remove it + for (auto thread: owner_process->threads()) { + if (thread == current_thread) { + owner_process->remove_thread(m_current_thread_index); + break; + } + } - // Find the process that has the thread and remove it - for (auto thread : owner_process->threads()) { - if (thread == current_thread) { - owner_process->remove_thread(m_current_thread_index); - break; - } - } + // Remove the thread + m_threads.erase(m_threads.begin() + m_current_thread_index); - // Remove the thread - m_threads.erase(m_threads.begin() + m_current_thread_index); + // Run the next thread + return schedule_next(cpu_state); - // Run the next thread - return schedule_next(cpu_state); + default: + break; + } - default: - break; - } + // Prepare the next thread to run + current_thread->thread_state = ThreadState::RUNNING; + current_thread->restore_sse_state(); - // Prepare the next thread to run - current_thread -> thread_state = ThreadState::RUNNING; - current_thread -> restore_sse_state(); + // Load the thread's memory manager and task state + MemoryManager::switch_active_memory_manager(owner_process->memory_manager); + CPU::tss.rsp0 = current_thread->tss_pointer(); - // Load the threads memory manager - MemoryManager::switch_active_memory_manager(owner_process->memory_manager); - - // Load the TSS for the thread - system::CPU::tss.rsp0 = current_thread->tss_pointer(); - - // Return the next thread's state - return current_thread->execution_state; + return current_thread->execution_state; } @@ -172,18 +158,17 @@ system::cpu_status_t *Scheduler::schedule_next(system::cpu_status_t* cpu_state) * @param process The process to add * @return The process ID */ -uint64_t Scheduler::add_process(Process *process) { - - // Get the next process ID - m_next_pid++; +uint64_t Scheduler::add_process(Process* process) { - // Add the process to the list - m_processes.push_back(process); - Logger::DEBUG() << "Adding process " << m_next_pid << ": " << process->name << "\n"; + // Get the next process ID + m_next_pid++; - // Return the process ID - return m_next_pid; + // Add the process to the list + m_processes.push_back(process); + Logger::DEBUG() << "Adding process " << m_next_pid << ": " << process->name << "\n"; + // Return the process ID + return m_next_pid; } /** @@ -192,18 +177,17 @@ uint64_t Scheduler::add_process(Process *process) { * @param thread The thread to add * @return The thread ID */ -uint64_t Scheduler::add_thread(Thread *thread) { +uint64_t Scheduler::add_thread(Thread* thread) { - // Get the next thread ID - m_next_tid++; + // Get the next thread ID + m_next_tid++; - // Add the thread to the list - m_threads.push_back(thread); - Logger::DEBUG() << "Adding thread " << m_next_tid << " to process " << thread->parent_pid << "\n"; - - // Return the thread ID - return m_next_tid; + // Add the thread to the list + m_threads.push_back(thread); + Logger::DEBUG() << "Adding thread " << m_next_tid << " to process " << thread->parent_pid << "\n"; + // Return the thread ID + return m_next_tid; } /** @@ -211,12 +195,9 @@ uint64_t Scheduler::add_thread(Thread *thread) { * * @return The system scheduler or nullptr if not found */ -Scheduler *Scheduler::system_scheduler() { - - if(s_instance) - return s_instance; +Scheduler* Scheduler::system_scheduler() { - return nullptr; + return s_instance; } /** @@ -225,33 +206,34 @@ Scheduler *Scheduler::system_scheduler() { * @return The number of ticks */ uint64_t Scheduler::ticks() const { - return m_ticks; + + return m_ticks; } /** - * @brief Yield the current thread + * @brief Pass execution to the next thread */ cpu_status_t* Scheduler::yield() { - // If this is the only thread, can't yield - if (m_threads.size() <= 1) - return current_thread()->execution_state; + // If this is the only thread, can't yield + if (m_threads.size() <= 1) + return current_thread()->execution_state; - // Set the current thread to waiting if running - if (m_threads[m_current_thread_index]->thread_state == ThreadState::RUNNING) - m_threads[m_current_thread_index]->thread_state = ThreadState::WAITING; + // Set the current thread to waiting if running + if (m_threads[m_current_thread_index]->thread_state == ThreadState::RUNNING) + m_threads[m_current_thread_index]->thread_state = ThreadState::READY; - // Schedule the next thread - return schedule_next(current_thread()->execution_state); - + // Schedule the next thread + return schedule_next(current_thread()->execution_state); } /** * @brief Activates the scheduler */ void Scheduler::activate() { - m_active = true; + + m_active = true; } /** @@ -261,34 +243,33 @@ void Scheduler::activate() { * @param force If true, the process will be removed and so will all threads * @return -1 if the process has threads, 0 otherwise */ -uint64_t Scheduler::remove_process(Process *process) { - - // Check if the process has no threads - if (!process->threads().empty()) { +uint64_t Scheduler::remove_process(Process* process) { - // Set the threads to stopped or remove them if forced - for (auto thread : process->threads()) - thread->thread_state = ThreadState::STOPPED; + // Check if the process has no threads + if (!process->threads().empty()) { - // Need to wait until the threads are stopped before removing the process (this will be called again when all threads are stopped) - return -1; + // Set the threads to stopped or remove them if forced + for (auto thread: process->threads()) + thread->thread_state = ThreadState::STOPPED; - } + // Need to wait until the threads are stopped before removing the process (this will be called again when all threads are stopped) + return -1; - // Remove the process - for (uint32_t i = 0; i < m_processes.size(); i++) { - if (m_processes[i] == process) { - m_processes.erase(m_processes.begin() + i); + } - // Delete the process mem - delete process; - return 0; - } - } + // Remove the process + for (uint32_t i = 0; i < m_processes.size(); i++) { + if (m_processes[i] == process) { + m_processes.erase(m_processes.begin() + i); - // Process not found - return -1; + // Delete the process mem + delete process; + return 0; + } + } + // Process not found + return -1; } /** @@ -297,27 +278,27 @@ uint64_t Scheduler::remove_process(Process *process) { * @param process The process to remove * @return The status of the CPU for the next process to run or nullptr if the process was not found */ -cpu_status_t* Scheduler::force_remove_process(Process *process) { +cpu_status_t* Scheduler::force_remove_process(Process* process) { - // If there is no process, fail - if (!process) - return nullptr; + // If there is no process, fail + if (!process) + return nullptr; - // Remove all the threads - for (auto thread : process->threads()){ + // Remove all the threads + for (auto thread: process->threads()) { - // Remove the thread from the scheduler - int index = m_threads.find(thread) - m_threads.begin(); - m_threads.erase(m_threads.begin() + index); + // Remove the thread from the scheduler + int index = m_threads.find(thread) - m_threads.begin(); + m_threads.erase(m_threads.begin() + index); - // Delete the thread - process->remove_thread(thread->tid); + // Delete the thread + process->remove_thread(thread->tid); - } + } - - // Process will be dead now so run the next process (don't care about the execution state being outdated as we are removing it anyway) - return schedule_next(current_thread()->execution_state); + // Process will be dead now so run the next process (don't care about the execution state being outdated as it is being + // removed regardless) + return schedule_next(current_thread()->execution_state); } /** @@ -325,22 +306,22 @@ cpu_status_t* Scheduler::force_remove_process(Process *process) { * * @return The current process, or nullptr if not found */ -Process *Scheduler::current_process() { +Process* Scheduler::current_process() { - Process* current_process = nullptr; + Process* current_process = nullptr; - // Make sure there is a active scheduler - if(!s_instance) - return nullptr; + // Make sure there is something with process attached + if (!s_instance) + return nullptr; - // Find the process that has the thread being executed - for (auto process : s_instance -> m_processes) - if (process->pid() == current_thread() -> parent_pid) { - current_process = process; - break; - } + // Find the process that has the thread being executed + for (auto process: s_instance->m_processes) + if (process->pid() == current_thread()->parent_pid) { + current_process = process; + break; + } - return current_process; + return current_process; } /** @@ -349,15 +330,15 @@ Process *Scheduler::current_process() { * @param pid The process ID * @return The process or nullptr if not found */ -Process *Scheduler::get_process(uint64_t pid) { +Process* Scheduler::get_process(uint64_t pid) { - // Try to find the process - for (auto process : s_instance->m_processes) - if (process->pid() == pid) - return process; + // Try to find the process + for (auto process: s_instance->m_processes) + if (process->pid() == pid) + return process; - // Not found - return nullptr; + // Not found + return nullptr; } @@ -366,18 +347,17 @@ Process *Scheduler::get_process(uint64_t pid) { * * @return The currently executing thread */ -Thread *Scheduler::current_thread() { - - return s_instance -> m_threads[s_instance -> m_current_thread_index]; +Thread* Scheduler::current_thread() { + return s_instance->m_threads[s_instance->m_current_thread_index]; } /** * @brief Deactivates the scheduler */ void Scheduler::deactivate() { - m_active = false; + m_active = false; } /** @@ -385,42 +365,31 @@ void Scheduler::deactivate() { * * @param multiboot The multiboot structure */ -void Scheduler::load_multiboot_elfs(system::Multiboot *multiboot) { - - for(multiboot_tag* tag = multiboot -> start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag *) ((multiboot_uint8_t *) tag + ((tag->size + 7) & ~7))) { - if(tag -> type != MULTIBOOT_TAG_TYPE_MODULE) - continue; +void Scheduler::load_multiboot_elfs(Multiboot* multiboot) { - // Get the module tag - auto* module = (struct multiboot_tag_module*)tag; + for (multiboot_tag* tag = multiboot->start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag*) ((multiboot_uint8_t*) tag + ((tag->size + 7) & ~7))) { - // Create the elf - auto* elf = new Elf64((uintptr_t)PhysicalMemoryManager::to_dm_region((uintptr_t )module->mod_start)); - if(!elf->is_valid()) - continue; + // Tag is not an ELF + if (tag->type != MULTIBOOT_TAG_TYPE_MODULE) + continue; - Logger::DEBUG() << "Creating process from multiboot module for " << module->cmdline << " (at 0x" << (uint64_t)module->mod_start << ")\n"; + // Try create the elf from the module + auto* module = (struct multiboot_tag_module*) tag; + auto* elf = new Elf64((uintptr_t) PhysicalMemoryManager::to_dm_region(module->mod_start)); + if (!elf->is_valid()) + continue; - // Create an array of args for the process - char* args[1] = {module->cmdline}; + Logger::DEBUG() << "Creating process from multiboot module for " << module->cmdline << " (at 0x" << (uint64_t) module->mod_start << ")\n"; - // Create the process - auto* process = new Process(module->cmdline, args, 1, elf); - - Logger::DEBUG() << "Elf loaded to pid " << process->pid() << "\n"; - } - -} - -/** - * @brief Gets the IPC handler - * - * @return The IPC handler or nullptr if not found - */ -InterProcessCommunicationManager *Scheduler::scheduler_ipc() { + // Create an array of args for the process TODO: handle multiple args ("" & spaces) + char* args[1] = {module->cmdline}; - return s_instance -> m_ipc; + // Create the process + auto* process = new Process(module->cmdline, args, 1, elf); + Logger::DEBUG() << "Elf loaded to pid " << process->pid() << "\n"; + delete elf; + } } /** @@ -429,12 +398,12 @@ InterProcessCommunicationManager *Scheduler::scheduler_ipc() { * @param tid The thread ID * @return The thread or nullptr if not found */ -Thread *Scheduler::get_thread(uint64_t tid) { +Thread* Scheduler::get_thread(uint64_t tid) { - // Try to find the thread - for (auto thread : s_instance -> m_threads) - if (thread -> tid == tid) - return thread; + // Try to find the thread + for (auto thread: s_instance->m_threads) + if (thread->tid == tid) + return thread; - return nullptr; + return nullptr; } diff --git a/kernel/src/runtime/cplusplus.cpp b/kernel/src/runtime/cplusplus.cpp index e4340d22..aad7919e 100644 --- a/kernel/src/runtime/cplusplus.cpp +++ b/kernel/src/runtime/cplusplus.cpp @@ -12,15 +12,15 @@ extern "C" void* __dso_handle = nullptr; // Pure virtual function call extern "C" void __cxa_pure_virtual() { - ASSERT(false, "Pure virtual function call failed"); + + ASSERT(false, "Pure virtual function call failed"); } extern "C" constructor start_ctors; extern "C" constructor end_ctors; -extern "C" void call_constructors() -{ - // Loop through and initialise all the global constructors - for(constructor* i = &start_ctors; i != &end_ctors; i++) - (*i)(); +extern "C" void call_constructors() { + // Loop through and initialise all the global constructors + for (constructor* i = &start_ctors; i != &end_ctors; i++) + (*i)(); } \ No newline at end of file diff --git a/kernel/src/runtime/ubsan.cpp b/kernel/src/runtime/ubsan.cpp index a510aa5c..0f627789 100644 --- a/kernel/src/runtime/ubsan.cpp +++ b/kernel/src/runtime/ubsan.cpp @@ -19,8 +19,8 @@ UBSanHandler::~UBSanHandler() = default; */ void UBSanHandler::handle(source_location_t location) { - // Print the location - ASSERT(false, "UBSAN ERROR AT %s:%d:%d\n", location.file, location.line, location.column); + // Print the location + ASSERT(false, "UBSAN ERROR AT %s:%d:%d\n", location.file, location.line, location.column); } @@ -31,17 +31,17 @@ void UBSanHandler::handle(source_location_t location) { * @param info The type mismatch info * @param ptr The pointer to the object */ -void UBSanHandler::print_type_mismatch(type_mismatch_info_t *info, uintptr_t ptr) { +void UBSanHandler::print_type_mismatch(type_mismatch_info_t* info, uintptr_t ptr) { - // Print the error - Logger::DEBUG() << "UBSan: "; - if(info -> alignment != 0 && ubsan_aligned(ptr, info -> alignment)) - Logger::Out() << "misaligned memory access\n"; - else - Logger::Out() << Type_Check_Kinds[info -> type_check_kind] << " address 0x" << ptr << " with insufficient space for an object of type " << info -> type -> name << "\n"; + // Print the error + Logger::DEBUG() << "UBSan: "; + if (info->alignment != 0 && ubsan_aligned(ptr, info->alignment)) + Logger::Out() << "misaligned memory access\n"; + else + Logger::Out() << Type_Check_Kinds[info->type_check_kind] << " address 0x" << ptr << " with insufficient space for an object of type " << info->type->name << "\n"; - // Print the location - handle(info -> location); + // Print the location + handle(info->location); } /** @@ -50,90 +50,109 @@ void UBSanHandler::print_type_mismatch(type_mismatch_info_t *info, uintptr_t ptr * @param info The type mismatch info * @param ptr The pointer to the object */ -void UBSanHandler::print_type_mismatch_v1(type_mismatch_info_v1_t *info, uintptr_t ptr) { +void UBSanHandler::print_type_mismatch_v1(type_mismatch_info_v1_t* info, uintptr_t ptr) { - // Print the error - Logger::DEBUG() << "UBSan: "; - if(info -> log_alignment != 0 && ubsan_aligned(ptr, (1 << (info -> log_alignment)))) - Logger::Out() << "misaligned memory access\n"; - else - Logger::Out() << Type_Check_Kinds[info -> type_check_kind] << " address 0x" << ptr << " with insufficient space for an object of type " << info -> type -> name << "\n"; + // Print the error + Logger::DEBUG() << "UBSan: "; + if (info->log_alignment != 0 && ubsan_aligned(ptr, (1 << (info->log_alignment)))) + Logger::Out() << "misaligned memory access\n"; + else + Logger::Out() << Type_Check_Kinds[info->type_check_kind] << " address 0x" << ptr << " with insufficient space for an object of type " << info->type->name << "\n"; - // Print the location - handle(info -> location); + // Print the location + handle(info->location); } -extern "C" void __ubsan_handle_type_mismatch(type_mismatch_info_t *info, uintptr_t ptr) { - UBSanHandler::print_type_mismatch(info, ptr); - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_type_mismatch(type_mismatch_info_t* info, uintptr_t ptr) { + + UBSanHandler::print_type_mismatch(info, ptr); + UBSanHandler::handle(info->location); } -extern "C" void __ubsan_handle_type_mismatch_v1(type_mismatch_info_v1_t *info, uintptr_t ptr) { - UBSanHandler::print_type_mismatch_v1(info, ptr); - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_type_mismatch_v1(type_mismatch_info_v1_t* info, uintptr_t ptr) { + + UBSanHandler::print_type_mismatch_v1(info, ptr); + UBSanHandler::handle(info->location); } -extern "C" void __ubsan_handle_pointer_overflow(overflow_info_t *info) { - Logger::DEBUG() << "UBSan: Pointer overflow\n"; - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_pointer_overflow(overflow_info_t* info) { + + Logger::DEBUG() << "UBSan: Pointer overflow\n"; + UBSanHandler::handle(info->location); } -extern "C" void __ubsan_handle_sub_overflow(overflow_info_t *info) { - Logger::DEBUG() << "UBSan: Subtraction overflow\n"; - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_sub_overflow(overflow_info_t* info) { + + Logger::DEBUG() << "UBSan: Subtraction overflow\n"; + UBSanHandler::handle(info->location); } -extern "C" void __ubsan_handle_shift_out_of_bounds(shift_out_of_bounds_info_t *info) { - Logger::DEBUG() << "UBSan: Shift out of bounds\n"; - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_shift_out_of_bounds(shift_out_of_bounds_info_t* info) { + + Logger::DEBUG() << "UBSan: Shift out of bounds\n"; + UBSanHandler::handle(info->location); } -extern "C" void __ubsan_handle_out_of_bounds(out_of_bounds_info_t *info) { - Logger::DEBUG() << "UBSan: Array out of bounds\n"; - UBSanHandler::handle(info -> location); +extern "C" void __ubsan_handle_out_of_bounds(out_of_bounds_info_t* info) { + + Logger::DEBUG() << "UBSan: Array out of bounds\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_add_overflow(overflow_info_t* info) { - Logger::DEBUG() << "UBSan: Addition overflow\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Addition overflow\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_divrem_overflow(overflow_info_t* info) { - Logger::DEBUG() << "UBSan: Division overflow\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Division overflow\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_negate_overflow(overflow_info_t* info) { - Logger::DEBUG() << "UBSan: Negation overflow\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Negation overflow\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_builtin_unreachable(location_only_info_t* info) { - Logger::DEBUG() << "UBSan: Unreachable code\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Unreachable code\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_mul_overflow(overflow_info_t* info) { - Logger::DEBUG() << "UBSan: Multiplication overflow\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Multiplication overflow\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_load_invalid_value(invalid_value_info_t* info) { - Logger::DEBUG() << "UBSan: Load of invalid value\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Load of invalid value\n"; + UBSanHandler::handle(info->location); } extern "C" void __ubsan_handle_missing_return(location_only_info_t* info) { - Logger::DEBUG() << "UBSan: Missing return\n"; - UBSanHandler::handle(info -> location); + + Logger::DEBUG() << "UBSan: Missing return\n"; + UBSanHandler::handle(info->location); +} + +extern "C" void __ubsan_handle_vla_bound_not_positive(vla_bound_not_positive_info_t* info) { + + Logger::DEBUG() << "UBSan: VLA bound not positive\n"; + UBSanHandler::handle(info->location); } \ No newline at end of file diff --git a/kernel/src/system/cpu.cpp b/kernel/src/system/cpu.cpp index 77ee213b..d45751ef 100644 --- a/kernel/src/system/cpu.cpp +++ b/kernel/src/system/cpu.cpp @@ -5,6 +5,7 @@ #include #include #include +#include using namespace MaxOS; using namespace MaxOS::system; @@ -17,194 +18,186 @@ extern uint64_t stack[]; /** * @brief Constructor for the CPU class */ -CPU::CPU(GlobalDescriptorTable* gdt, Multiboot* multiboot){ +CPU::CPU(GlobalDescriptorTable* gdt, Multiboot* multiboot) { - Logger::INFO() << "Setting up CPU \n"; - acpi = new AdvancedConfigurationAndPowerInterface(multiboot); - apic = new AdvancedProgrammableInterruptController(acpi); + Logger::INFO() << "Setting up CPU \n"; + acpi = new AdvancedConfigurationAndPowerInterface(multiboot); + apic = new AdvancedProgrammableInterruptController(acpi); - // TODO: Multicore + // TODO: Multicore - // Setup cpu features - init_tss(gdt); - init_sse(); + // Setup cpu features + init_tss(gdt); + init_sse(); } -/** - * @brief Destructor for the CPU class - */ CPU::~CPU() = default; [[noreturn]] void CPU::halt() { - while (true) - asm volatile("hlt"); -} - -void CPU::get_status(cpu_status_t *status) { - - // Get the registers - asm volatile("mov %%r15, %0" : "=r" (status->r15)); - asm volatile("mov %%r14, %0" : "=r" (status->r14)); - asm volatile("mov %%r13, %0" : "=r" (status->r13)); - asm volatile("mov %%r12, %0" : "=r" (status->r12)); - asm volatile("mov %%r11, %0" : "=r" (status->r11)); - asm volatile("mov %%r10, %0" : "=r" (status->r10)); - asm volatile("mov %%r9, %0" : "=r" (status->r9)); - asm volatile("mov %%r8, %0" : "=r" (status->r8)); - asm volatile("mov %%rdi, %0" : "=r" (status->rdi)); - asm volatile("mov %%rsi, %0" : "=r" (status->rsi)); - asm volatile("mov %%rbp, %0" : "=r" (status->rbp)); - asm volatile("mov %%rdx, %0" : "=r" (status->rdx)); - asm volatile("mov %%rcx, %0" : "=r" (status->rcx)); - asm volatile("mov %%rbx, %0" : "=r" (status->rbx)); - asm volatile("mov %%rax, %0" : "=r" (status->rax)); + while (true) + asm volatile("hlt"); } -void CPU::set_status(cpu_status_t *status) { - - // Set the registers - asm volatile("mov %0, %%r15" : : "r" (status->r15)); - asm volatile("mov %0, %%r14" : : "r" (status->r14)); - asm volatile("mov %0, %%r13" : : "r" (status->r13)); - asm volatile("mov %0, %%r12" : : "r" (status->r12)); - asm volatile("mov %0, %%r11" : : "r" (status->r11)); - asm volatile("mov %0, %%r10" : : "r" (status->r10)); - asm volatile("mov %0, %%r9" : : "r" (status->r9)); - asm volatile("mov %0, %%r8" : : "r" (status->r8)); - asm volatile("mov %0, %%rdi" : : "r" (status->rdi)); - asm volatile("mov %0, %%rsi" : : "r" (status->rsi)); - asm volatile("mov %0, %%rbp" : : "r" (status->rbp)); - asm volatile("mov %0, %%rdx" : : "r" (status->rdx)); - asm volatile("mov %0, %%rcx" : : "r" (status->rcx)); - asm volatile("mov %0, %%rbx" : : "r" (status->rbx)); - asm volatile("mov %0, %%rax" : : "r" (status->rax)); +void CPU::get_status(cpu_status_t* status) { + + // Get the registers + asm volatile("mov %%r15, %0" : "=r" (status->r15)); + asm volatile("mov %%r14, %0" : "=r" (status->r14)); + asm volatile("mov %%r13, %0" : "=r" (status->r13)); + asm volatile("mov %%r12, %0" : "=r" (status->r12)); + asm volatile("mov %%r11, %0" : "=r" (status->r11)); + asm volatile("mov %%r10, %0" : "=r" (status->r10)); + asm volatile("mov %%r9, %0" : "=r" (status->r9)); + asm volatile("mov %%r8, %0" : "=r" (status->r8)); + asm volatile("mov %%rdi, %0" : "=r" (status->rdi)); + asm volatile("mov %%rsi, %0" : "=r" (status->rsi)); + asm volatile("mov %%rbp, %0" : "=r" (status->rbp)); + asm volatile("mov %%rdx, %0" : "=r" (status->rdx)); + asm volatile("mov %%rcx, %0" : "=r" (status->rcx)); + asm volatile("mov %%rbx, %0" : "=r" (status->rbx)); + asm volatile("mov %%rax, %0" : "=r" (status->rax)); } -void CPU::print_registers(cpu_status_t *status) { - - // Print the registers - Logger::ERROR() << "R15: \t0x" << status->r15 << "\n"; - Logger::ERROR() << "R14: \t0x" << status->r14 << "\n"; - Logger::ERROR() << "R13: \t0x" << status->r13 << "\n"; - Logger::ERROR() << "R12: \t0x" << status->r12 << "\n"; - Logger::ERROR() << "R11: \t0x" << status->r11 << "\n"; - Logger::ERROR() << "R10: \t0x" << status->r10 << "\n"; - Logger::ERROR() << "R9: \t0x" << status->r9 << "\n"; - Logger::ERROR() << "R8: \t0x" << status->r8 << "\n"; - Logger::ERROR() << "RDI: \t0x" << status->rdi << "\n"; - Logger::ERROR() << "RSI: \t0x" << status->rsi << "\n"; - Logger::ERROR() << "RBP: \t0x" << status->rbp << "\n"; - Logger::ERROR() << "RDX: \t0x" << status->rdx << "\n"; - Logger::ERROR() << "RCX: \t0x" << status->rcx << "\n"; - Logger::ERROR() << "RBX: \t0x" << status->rbx << "\n"; - Logger::ERROR() << "RAX: \t0x" << status->rax << "\n"; - Logger::ERROR() << "INT: \t0x" << status->interrupt_number << "\n"; - Logger::ERROR() << "ERRCD: \t0x" << status->error_code << "\n"; - Logger::ERROR() << "RIP: \t0x" << status->rip << "\n"; - Logger::ERROR() << "CS: \t0x" << status->cs << "\n"; - Logger::ERROR() << "RFlGS: \t0x" << status->rflags << "\n"; - Logger::ERROR() << "RSP: \t0x" << status->rsp << "\n"; - Logger::ERROR() << "SS: \t0x" << status->ss << "\n"; +void CPU::set_status(cpu_status_t* status) { + + // Set the registers + asm volatile("mov %0, %%r15" : : "r" (status->r15)); + asm volatile("mov %0, %%r14" : : "r" (status->r14)); + asm volatile("mov %0, %%r13" : : "r" (status->r13)); + asm volatile("mov %0, %%r12" : : "r" (status->r12)); + asm volatile("mov %0, %%r11" : : "r" (status->r11)); + asm volatile("mov %0, %%r10" : : "r" (status->r10)); + asm volatile("mov %0, %%r9" : : "r" (status->r9)); + asm volatile("mov %0, %%r8" : : "r" (status->r8)); + asm volatile("mov %0, %%rdi" : : "r" (status->rdi)); + asm volatile("mov %0, %%rsi" : : "r" (status->rsi)); + asm volatile("mov %0, %%rbp" : : "r" (status->rbp)); + asm volatile("mov %0, %%rdx" : : "r" (status->rdx)); + asm volatile("mov %0, %%rcx" : : "r" (status->rcx)); + asm volatile("mov %0, %%rbx" : : "r" (status->rbx)); + asm volatile("mov %0, %%rax" : : "r" (status->rax)); } +void CPU::print_registers(cpu_status_t* status) { + + // Print the registers + Logger::ERROR() << "R15: \t0x" << status->r15 << "\n"; + Logger::ERROR() << "R14: \t0x" << status->r14 << "\n"; + Logger::ERROR() << "R13: \t0x" << status->r13 << "\n"; + Logger::ERROR() << "R12: \t0x" << status->r12 << "\n"; + Logger::ERROR() << "R11: \t0x" << status->r11 << "\n"; + Logger::ERROR() << "R10: \t0x" << status->r10 << "\n"; + Logger::ERROR() << "R9: \t0x" << status->r9 << "\n"; + Logger::ERROR() << "R8: \t0x" << status->r8 << "\n"; + Logger::ERROR() << "RDI: \t0x" << status->rdi << "\n"; + Logger::ERROR() << "RSI: \t0x" << status->rsi << "\n"; + Logger::ERROR() << "RBP: \t0x" << status->rbp << "\n"; + Logger::ERROR() << "RDX: \t0x" << status->rdx << "\n"; + Logger::ERROR() << "RCX: \t0x" << status->rcx << "\n"; + Logger::ERROR() << "RBX: \t0x" << status->rbx << "\n"; + Logger::ERROR() << "RAX: \t0x" << status->rax << "\n"; + Logger::ERROR() << "INT: \t0x" << status->interrupt_number << "\n"; + Logger::ERROR() << "ERRCD: \t0x" << status->error_code << "\n"; + Logger::ERROR() << "RIP: \t0x" << status->rip << "\n"; + Logger::ERROR() << "CS: \t0x" << status->cs << "\n"; + Logger::ERROR() << "RFlGS: \t0x" << status->rflags << "\n"; + Logger::ERROR() << "RSP: \t0x" << status->rsp << "\n"; + Logger::ERROR() << "SS: \t0x" << status->ss << "\n"; -uint64_t CPU::read_msr(uint32_t msr) { +} - // Low and high parts of the MSR - uint32_t low, high; - // Read the MSR - asm volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr)); +uint64_t CPU::read_msr(uint32_t msr) { - // Return the value - return (uint64_t) low | ((uint64_t) high << 32); + // Read the MSR + uint32_t low, high; + asm volatile("rdmsr" : "=a" (low), "=d" (high) : "c" (msr)); + // Return the value + return (uint64_t) low | ((uint64_t) high << 32); } void CPU::write_msr(uint32_t msr, uint64_t value) { - // Write the MSR - asm volatile("wrmsr" : : "a" ((uint32_t) value), "d" ((uint32_t) (value >> 32)), "c" (msr)); - + // Write the MSR + asm volatile("wrmsr" : : "a" ((uint32_t) value), "d" ((uint32_t) (value >> 32)), "c" (msr)); } + void CPU::cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) { - // Call the cpuid instruction - __get_cpuid(leaf, eax, ebx, ecx, edx); + // Call the cpuid instruction + __get_cpuid(leaf, eax, ebx, ecx, edx); } void CPU::stack_trace(size_t level) { - // Get the first stack frame - auto* frame = (stack_frame_t*)__builtin_frame_address(0); - size_t current_level = 0; + // Get the first stack frame + auto* frame = (stack_frame_t*) __builtin_frame_address(0); - // Loop through the frames logging - while (current_level < level && frame != nullptr){ + // Loop through the frames logging + for (size_t current_level = 0; current_level < level; current_level++) { - // Print the frame - Logger::ERROR() << "(" << current_level << "):\t at 0x" << frame->rip << "\n"; + // Print the frame + Logger::ERROR() << "(" << current_level << "):\t at 0x" << frame->rip << "\n"; - // Next frame - frame = frame -> next; - current_level++; + // Next frame + frame = frame->next; + if (frame == nullptr) + break; - } + } } -#include -#include - -void CPU::PANIC(char const *message, cpu_status_t* status) { - - // Get the current process - Process* process = Scheduler::current_process(); - - // Ensure ready to panic - At this point it is not an issue if it is possible can avoid the panic as it is most likely called by a place that cant switch to the avoidable state - if(!is_panicking) - prepare_for_panic(); - - // Print using the backend - Logger::ERROR() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"; - Logger::ERROR() << "Kernel Panic: " << message << "\n"; - - // Info about the running process - Logger::ERROR() << "Process: " << (process ? process->name.c_str() : "Kernel") << "\n"; - if(process) - Logger::ERROR() << "After running for " << process->total_ticks() << " ticks (system uptime: " << Scheduler::system_scheduler()->ticks() << " ticks)\n"; - - // Stack trace - Logger::ERROR() << "----------------------------\n"; - Logger::ERROR() << "Stack Trace:\n"; - stack_trace(10); - - // Register dump - Logger::ERROR() << "----------------------------\n"; - Logger::ERROR() << "Register Dump:\n"; - - if(!status){ - auto* new_status = new cpu_status_t(); // Who cares about freeing we're fucked anyway at this point - get_status(new_status); - status = new_status; - } - print_registers(status); - - // Print some text to the user - Logger::ERROR() << "----------------------------\n"; - Logger::ERROR() << "There has been a fatal error in MaxOS and the system has been halted.\n"; - Logger::ERROR() << "Please restart the system.\n"; - - - // Print the logo - Logger::ERROR() << "----------------------------\n"; - console::VESABootConsole::print_logo_kernel_panic(); - - // Halt - halt(); - +void CPU::PANIC(char const* message, cpu_status_t* status) { + + // Get the current process + Process* process = Scheduler::current_process(); + + // Ensure ready to panic - At this point it is not an issue if it is possible can avoid the panic as it is most + // likely called by a place that cant switch to the avoidable state + if (!is_panicking) + prepare_for_panic(); + + // Print using the backend + Logger::ERROR() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n"; + Logger::ERROR() << "Kernel Panic: " << message << "\n"; + + // Info about the running process + Logger::ERROR() << "Process: " << (process ? process->name.c_str() : "Kernel") << "\n"; + if (process) + Logger::ERROR() << "After running for " << process->total_ticks() << " ticks (system uptime: " << Scheduler::system_scheduler()->ticks() << " ticks)\n"; + + // Stack trace + Logger::ERROR() << "----------------------------\n"; + Logger::ERROR() << "Stack Trace:\n"; + stack_trace(10); + + // Register dump + Logger::ERROR() << "----------------------------\n"; + Logger::ERROR() << "Register Dump:\n"; + + // Log the regs + cpu_status_t new_status{}; + if (!status) { + get_status(&new_status); + status = &new_status; + } + print_registers(status); + + // Print some text to the user + Logger::ERROR() << "----------------------------\n"; + Logger::ERROR() << "There has been a fatal error in MaxOS and the system has been halted.\n"; + Logger::ERROR() << "Please restart the system.\n"; + + // Print the logo + Logger::ERROR() << "----------------------------\n"; + console::VESABootConsole::print_logo_kernel_panic(); + + // Halt + halt(); } /** @@ -212,60 +205,57 @@ void CPU::PANIC(char const *message, cpu_status_t* status) { */ void CPU::init_tss(GlobalDescriptorTable* gdt) { - // The reserved have to be 0 - tss.reserved0 = 0; - tss.reserved1 = 0; - tss.reserved2 = 0; - tss.reserved3 = 0; - tss.reserved4 = 0; - - // The stacks - tss.rsp0 = (uint64_t)stack + 16384; // Kernel stack (scheduler will set the threads stack) - tss.rsp1 = 0; - tss.rsp2 = 0; - - // Interrupt stacks can all be 0 - tss.ist1 = 0; - tss.ist2 = 0; - tss.ist3 = 0; - tss.ist4 = 0; - tss.ist5 = 0; - tss.ist6 = 0; - tss.ist7 = 0; - - // Ports TODO when setting up userspace drivers come back to this - tss.io_bitmap_offset = 0; - - // Split the base into 4 parts (16 bits, 8 bits, 8 bits, 32 bits) - auto base = (uint64_t)&tss; - uint16_t base_1 = base & 0xFFFF; - uint8_t base_2 = (base >> 16) & 0xFF; - uint8_t base_3 = (base >> 24) & 0xFF; - uint32_t base_4 = (base >> 32) & 0xFFFFFFFF; - - uint16_t limit_low = sizeof(tss); - - // Flags: 1 - Type = 0x9, Descriptor Privilege Level = 0, Present = 1 - // 2 - Available = 0, Granularity = 0 - uint8_t flags_1 = 0x89; - uint8_t flags_2 = 0; - - - // Create the TSS descriptors - uint64_t tss_descriptor_low = (uint64_t) base_3 << 56 | (uint64_t) flags_2 << 48 | (uint64_t) flags_1 << 40 | (uint64_t) base_2 << 32 | (uint64_t) base_1 << 16 | (uint64_t) limit_low; - uint64_t tss_descriptor_high = base_4; - - // Store in the GDT - gdt -> table[5] = tss_descriptor_low; - gdt -> table[6] = tss_descriptor_high; - - // Load the TSS - Logger::DEBUG() << "Loading TSS: 0x0" << tss_descriptor_low << " 0x0" << tss_descriptor_high << " at 0x" << (uint64_t )&tss << "\n"; - asm volatile("ltr %%ax" : : "a" (0x28)); - - //TODO: For smp - load the TSS for each core or find a better way to do this - - + // The reserved have to be 0 + tss.reserved0 = 0; + tss.reserved1 = 0; + tss.reserved2 = 0; + tss.reserved3 = 0; + tss.reserved4 = 0; + + // The stacks + tss.rsp0 = (uint64_t) stack + 16384; // Kernel stack (scheduler will set the threads stack) + tss.rsp1 = 0; + tss.rsp2 = 0; + + // Interrupt stacks can all be 0 + tss.ist1 = 0; + tss.ist2 = 0; + tss.ist3 = 0; + tss.ist4 = 0; + tss.ist5 = 0; + tss.ist6 = 0; + tss.ist7 = 0; + + // Ports TODO when setting up userspace drivers come back to this + tss.io_bitmap_offset = 0; + + // Split the base into 4 parts (16 bits, 8 bits, 8 bits, 32 bits) + auto base = (uint64_t) &tss; + uint16_t base_1 = base & 0xFFFF; + uint8_t base_2 = (base >> 16) & 0xFF; + uint8_t base_3 = (base >> 24) & 0xFF; + uint32_t base_4 = (base >> 32) & 0xFFFFFFFF; + + uint16_t limit_low = sizeof(tss); + + // Flags: 1 - Type = 0x9, Descriptor Privilege Level = 0, Present = 1 + // 2 - Available = 0, Granularity = 0 + uint8_t flags_1 = 0x89; + uint8_t flags_2 = 0; + + // Create the TSS descriptors + uint64_t tss_descriptor_low = (uint64_t) base_3 << 56 | (uint64_t) flags_2 << 48 | (uint64_t) flags_1 << 40 | (uint64_t) base_2 << 32 | (uint64_t) base_1 << 16 | (uint64_t) limit_low; + uint64_t tss_descriptor_high = base_4; + + // Store in the GDT + gdt->table[5] = tss_descriptor_low; + gdt->table[6] = tss_descriptor_high; + + // Load the TSS + Logger::DEBUG() << "Loading TSS: 0x0" << tss_descriptor_low << " 0x0" << tss_descriptor_high << " at 0x" << (uint64_t) &tss << "\n"; + asm volatile("ltr %%ax" : : "a" (0x28)); + + //TODO: For smp - load the TSS for each core or find a better way to do this } /** @@ -277,24 +267,24 @@ void CPU::init_tss(GlobalDescriptorTable* gdt) { cpu_status_t* CPU::prepare_for_panic(cpu_status_t* status) { - // If it may have occurred in a process, switch to the avoidable state - if(Scheduler::system_scheduler() != nullptr && Scheduler::current_process() != nullptr){ + // If it may have occurred in a process, switch to the avoidable state + if (Scheduler::system_scheduler() != nullptr && Scheduler::current_process() != nullptr) { - // Get the current process - Process* process = Scheduler::current_process(); + // Get the current process + Process* process = Scheduler::current_process(); - // If the faulting address is in lower half just kill the process and move on - if(status && !memory::PhysicalMemoryManager::in_higher_region(status->rip)){ - Logger::ERROR() << "CPU Panicked in process " << process->name.c_str() << " at 0x" << status->rip << " - killing process\n"; - return Scheduler::system_scheduler()->force_remove_process(process); - } - } + // If the faulting address is in lower half just kill the process and move on + if (status && !memory::PhysicalMemoryManager::in_higher_region(status->rip)) { + Logger::ERROR() << "CPU Panicked in process " << process->name.c_str() << " at 0x" << status->rip << " - killing process\n"; + return Scheduler::system_scheduler()->force_remove_process(process); + } - // We are panicking - is_panicking = true; - - return nullptr; + // Otherwise occurred whilst the kernel was doing something for the process + } + // We are panicking + is_panicking = true; + return nullptr; } /** @@ -302,62 +292,60 @@ cpu_status_t* CPU::prepare_for_panic(cpu_status_t* status) { */ void CPU::init_sse() { - // Get the CR0 register - uint64_t cr0; - asm volatile("mov %%cr0, %0" : "=r" (cr0)); - - // Get the CR4 register - uint64_t cr4; - asm volatile("mov %%cr4, %0" : "=r" (cr4)); - + // Get the CR0 register + uint64_t cr0; + asm volatile("mov %%cr0, %0" : "=r" (cr0)); - // Check if FPU is supported - ASSERT(check_cpu_feature(CPU_FEATURE_EDX::FPU), "FPU not supported - needed for SSE"); + // Get the CR4 register + uint64_t cr4; + asm volatile("mov %%cr4, %0" : "=r" (cr4)); - // Clear the emulation flag, task switch flags and enable the monitor coprocessor, native exception bits - cr0 |= (1 << 1); - cr0 &= ~(1 << 2); - cr0 &= ~(1 << 3); - cr0 |= (1 << 5); - asm volatile("mov %0, %%cr0" : : "r" (cr0)); + // Check if FPU is supported + ASSERT(check_cpu_feature(CPU_FEATURE_EDX::FPU), "FPU not supported - needed for SSE"); - // Enable the FPU - asm volatile("fninit"); + // Clear the emulation flag, task switch flags and enable the monitor coprocessor, native exception bits + cr0 |= (1 << 1); + cr0 &= ~(1 << 2); + cr0 &= ~(1 << 3); + cr0 |= (1 << 5); + asm volatile("mov %0, %%cr0" : : "r" (cr0)); - // Check if SSE is supported - ASSERT(check_cpu_feature(CPU_FEATURE_EDX::SSE), "SSE not supported"); + // Enable the FPU + asm volatile("fninit"); - // Enable FSAVE, FSTORE and SSE instructions - cr4 |= (1 << 9); - cr4 |= (1 << 10); - asm volatile("mov %0, %%cr4" : : "r" (cr4)); + // Check if SSE is supported + ASSERT(check_cpu_feature(CPU_FEATURE_EDX::SSE), "SSE not supported"); + // Enable FSAVE, FSTORE and SSE instructions + cr4 |= (1 << 9); + cr4 |= (1 << 10); + asm volatile("mov %0, %%cr4" : : "r" (cr4)); - // Check if XSAVE is supported - s_xsave = check_cpu_feature(CPU_FEATURE_ECX::XSAVE) && check_cpu_feature(CPU_FEATURE_ECX::OSXSAVE); - Logger::DEBUG() << "XSAVE: " << (s_xsave ? "Supported" : "Not Supported") << "\n"; - if(!s_xsave) return; + // Check if XSAVE is supported + s_xsave = check_cpu_feature(CPU_FEATURE_ECX::XSAVE) && check_cpu_feature(CPU_FEATURE_ECX::OSXSAVE); + Logger::DEBUG() << "XSAVE: " << (s_xsave ? "Supported" : "Not Supported") << "\n"; + if (!s_xsave) return; - // Enable the XSAVE and XRESTORE instructions - cr4 |= (1 << 18); - asm volatile("mov %0, %%cr4" : : "r" (cr4)); + // Enable the XSAVE and XRESTORE instructions + cr4 |= (1 << 18); + asm volatile("mov %0, %%cr4" : : "r" (cr4)); - // Set the SSE and x87 bits - uint64_t xcr0; - asm volatile("xgetbv" : "=a" (xcr0) : "c" (0)); - xcr0 |= 0x7; - asm volatile("xsetbv" : : "c" (0), "a" (xcr0)); + // Set the SSE and x87 bits + uint64_t xcr0; + asm volatile("xgetbv" : "=a" (xcr0) : "c" (0)); + xcr0 |= 0x7; + asm volatile("xsetbv" : : "c" (0), "a" (xcr0)); - // Check if AVX is supported - s_avx = check_cpu_feature(CPU_FEATURE_ECX::AVX); - Logger::DEBUG() << "AVX: " << (s_avx ? "Supported" : "Not Supported") << "\n"; - if(!s_avx) return; + // Check if AVX is supported + s_avx = check_cpu_feature(CPU_FEATURE_ECX::AVX); + Logger::DEBUG() << "AVX: " << (s_avx ? "Supported" : "Not Supported") << "\n"; + if (!s_avx) return; - // Enable the AVX instructions - cr4 |= (1 << 14); - asm volatile("mov %0, %%cr4" : : "r" (cr4)); + // Enable the AVX instructions + cr4 |= (1 << 14); + asm volatile("mov %0, %%cr4" : : "r" (cr4)); - Logger::DEBUG() << "SSE Enabled\n"; + Logger::DEBUG() << "SSE Enabled\n"; } /** @@ -368,13 +356,12 @@ void CPU::init_sse() { */ bool CPU::check_cpu_feature(CPU_FEATURE_ECX feature) { - // Get the CPUID - uint32_t eax, ebx, ecx, edx; - cpuid(0x1, &eax, &ebx, &ecx, &edx); - - // Check the feature - return ecx & (uint32_t)feature; + // Get the CPUID + uint32_t eax, ebx, ecx, edx; + cpuid(0x1, &eax, &ebx, &ecx, &edx); + // Check the feature + return ecx & (uint32_t) feature; } /** @@ -385,13 +372,12 @@ bool CPU::check_cpu_feature(CPU_FEATURE_ECX feature) { */ bool CPU::check_cpu_feature(CPU_FEATURE_EDX feature) { - // Get the CPUID - uint32_t eax, ebx, ecx, edx; - cpuid(0x1, &eax, &ebx, &ecx, &edx); - - // Check the feature - return edx & (uint32_t)feature; + // Get the CPUID + uint32_t eax, ebx, ecx, edx; + cpuid(0x1, &eax, &ebx, &ecx, &edx); + // Check the feature + return edx & (uint32_t) feature; } /** @@ -400,13 +386,13 @@ bool CPU::check_cpu_feature(CPU_FEATURE_EDX feature) { */ bool CPU::check_nx() { - // Get the EFER MSR - uint64_t efer = read_msr(0xC0000080); + // Get the EFER MSR + uint64_t efer = read_msr(0xC0000080); - // Check if the NX flag is supported (bit 11) - bool supported = efer & (1 << 11); - Logger::DEBUG() << "NX: " << (supported ? "Supported" : "Not Supported") << "\n"; + // Check if the NX flag is supported (bit 11) + bool supported = efer & (1 << 11); + Logger::DEBUG() << "NX: " << (supported ? "Supported" : "Not Supported") << "\n"; - // Return if the NX flag is supported - return supported; + // Return if the NX flag is supported + return supported; } diff --git a/kernel/src/system/gdt.cpp b/kernel/src/system/gdt.cpp index 5134ce51..1a39b286 100644 --- a/kernel/src/system/gdt.cpp +++ b/kernel/src/system/gdt.cpp @@ -11,68 +11,66 @@ using namespace MaxOS::system; GlobalDescriptorTable::GlobalDescriptorTable() { - Logger::INFO() << "Setting up Global Descriptor Table\n"; + Logger::INFO() << "Setting up Global Descriptor Table\n"; - // Null descriptor - table[0] = 0; + // Null descriptor + table[0] = 0; - // Kernel Code Segment descriptor - uint64_t kernel_cs = 0; - kernel_cs |= (uint64_t)DescriptorFlags::Write; - kernel_cs |= (uint64_t)DescriptorFlags::Execute; - kernel_cs |= (uint64_t)DescriptorFlags::CodeOrDataSegment; - kernel_cs |= (uint64_t)DescriptorFlags::Present; - kernel_cs |= (uint64_t)DescriptorFlags::LongMode; - table[1] = kernel_cs; + // Kernel Code Segment descriptor + uint64_t kernel_cs = 0; + kernel_cs |= (uint64_t) DescriptorFlags::Write; + kernel_cs |= (uint64_t) DescriptorFlags::Execute; + kernel_cs |= (uint64_t) DescriptorFlags::CodeOrDataSegment; + kernel_cs |= (uint64_t) DescriptorFlags::Present; + kernel_cs |= (uint64_t) DescriptorFlags::LongMode; + table[1] = kernel_cs; - // Kernel Data Segment descriptor - uint64_t kernel_ds = 0; - kernel_ds |= (uint64_t)DescriptorFlags::Write; - kernel_ds |= (uint64_t)DescriptorFlags::CodeOrDataSegment; - kernel_ds |= (uint64_t)DescriptorFlags::Present; - table[2] = kernel_ds; + // Kernel Data Segment descriptor + uint64_t kernel_ds = 0; + kernel_ds |= (uint64_t) DescriptorFlags::Write; + kernel_ds |= (uint64_t) DescriptorFlags::CodeOrDataSegment; + kernel_ds |= (uint64_t) DescriptorFlags::Present; + table[2] = kernel_ds; - // User code segment descriptor (Change the privilege level to 3) - uint64_t user_cs = 0; - user_cs |= (uint64_t)DescriptorFlags::Write; - user_cs |= (uint64_t)DescriptorFlags::Execute; - user_cs |= (uint64_t)DescriptorFlags::CodeOrDataSegment; - user_cs |= (uint64_t)DescriptorFlags::Present; - user_cs |= (uint64_t)DescriptorFlags::LongMode; - user_cs |= (3ULL << 45); - table[3] = user_cs; + // User code segment descriptor (Change the privilege level to 3) + uint64_t user_cs = 0; + user_cs |= (uint64_t) DescriptorFlags::Write; + user_cs |= (uint64_t) DescriptorFlags::Execute; + user_cs |= (uint64_t) DescriptorFlags::CodeOrDataSegment; + user_cs |= (uint64_t) DescriptorFlags::Present; + user_cs |= (uint64_t) DescriptorFlags::LongMode; + user_cs |= (3ULL << 45); + table[3] = user_cs; - // User data segment descriptor (Change the privilege level to 3) - uint64_t user_ds = 0; - user_ds |= (uint64_t)DescriptorFlags::Write; - user_ds |= (uint64_t)DescriptorFlags::CodeOrDataSegment; - user_ds |= (uint64_t)DescriptorFlags::Present; - user_ds |= (3ULL << 45); - table[4] = user_ds; + // User data segment descriptor (Change the privilege level to 3) + uint64_t user_ds = 0; + user_ds |= (uint64_t) DescriptorFlags::Write; + user_ds |= (uint64_t) DescriptorFlags::CodeOrDataSegment; + user_ds |= (uint64_t) DescriptorFlags::Present; + user_ds |= (3ULL << 45); + table[4] = user_ds; - // Reserve space for the TSS - table[5] = 0; - table[6] = 0; + // Reserve space for the TSS + table[5] = 0; + table[6] = 0; - Logger::DEBUG() << "Created GDT entries\n"; + Logger::DEBUG() << "Created GDT entries\n"; - // Load the GDT - gdtr_t gdtr = { - .size = sizeof(table) - 1, - .address = (uint64_t)table, - }; - asm volatile("lgdt %0" : : "m"(gdtr)); - Logger::DEBUG() << "Loaded GDT of limit 0x" << (uint64_t)gdtr.size << " and address 0x" << (uint64_t)gdtr.address << "\n"; + // Load the GDT + gdtr_t gdtr = { + .size = sizeof(table) - 1, + .address = (uint64_t) table, + }; + asm volatile("lgdt %0" : : "m"(gdtr)); + Logger::DEBUG() << "Loaded GDT of limit 0x" << (uint64_t) gdtr.size << " and address 0x" << (uint64_t) gdtr.address << "\n"; - // Reload the segment registers - asm volatile("mov %0, %%ds" : : "r"(0x10)); - asm volatile("mov %0, %%es" : : "r"(0x10)); - asm volatile("mov %0, %%fs" : : "r"(0x10)); - asm volatile("mov %0, %%gs" : : "r"(0x10)); - asm volatile("mov %0, %%ss" : : "r"(0x10)); - Logger::DEBUG() << "Reloaded segment registers\n"; + // Reload the segment registers + asm volatile("mov %0, %%ds" : : "r"(0x10)); + asm volatile("mov %0, %%es" : : "r"(0x10)); + asm volatile("mov %0, %%fs" : : "r"(0x10)); + asm volatile("mov %0, %%gs" : : "r"(0x10)); + asm volatile("mov %0, %%ss" : : "r"(0x10)); + Logger::DEBUG() << "Reloaded segment registers\n"; } -GlobalDescriptorTable::~GlobalDescriptorTable() -{ -} \ No newline at end of file +GlobalDescriptorTable::~GlobalDescriptorTable() = default; \ No newline at end of file diff --git a/kernel/src/system/multiboot.cpp b/kernel/src/system/multiboot.cpp index 65597c4a..4b23fac5 100644 --- a/kernel/src/system/multiboot.cpp +++ b/kernel/src/system/multiboot.cpp @@ -15,110 +15,103 @@ Multiboot::Multiboot(unsigned long address, unsigned long magic) : start_address(address) { - // Confirm the bootloader - ASSERT(magic == MULTIBOOT2_BOOTLOADER_MAGIC, "Multiboot2 Bootloader Not Detected"); - Logger::DEBUG() << "Multiboot2 Bootloader Detected at 0x" << (uint64_t)address << "\n"; + // Confirm the bootloader + ASSERT(magic == MULTIBOOT2_BOOTLOADER_MAGIC, "Multiboot2 Bootloader Not Detected"); + Logger::DEBUG() << "Multiboot2 Bootloader Detected at 0x" << (uint64_t) address << "\n"; - multiboot_tag* tag = start_tag(); + multiboot_tag* tag = start_tag(); - // Loop through the tags and load them - while (true) { + // Loop through the tags and load them + while (true) { - // Handle the tag - switch (tag -> type) { + // Handle the tag + switch (tag->type) { - case MULTIBOOT_TAG_TYPE_END: - end_address = (unsigned long)PhysicalMemoryManager::to_lower_region((uint64_t)tag); - return; + case MULTIBOOT_TAG_TYPE_END: + end_address = (unsigned long) PhysicalMemoryManager::to_lower_region((uint64_t) tag); + return; - //0xffffffff80394dd8 - //0xFFFFFFFF80000000 + case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: + m_framebuffer = (multiboot_tag_framebuffer*) tag; + break; - case MULTIBOOT_TAG_TYPE_FRAMEBUFFER: - m_framebuffer = (multiboot_tag_framebuffer *)tag; - break; + case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: + m_basic_meminfo = (multiboot_tag_basic_meminfo*) tag; + break; - case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO: - m_basic_meminfo = (multiboot_tag_basic_meminfo *)tag; - break; + case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: + m_bootloader_name = (multiboot_tag_string*) tag; + Logger::DEBUG() << "Bootloader: " << m_bootloader_name->string << "\n"; + break; - case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME: - m_bootloader_name = (multiboot_tag_string *)tag; - Logger::DEBUG() << "Bootloader: " << m_bootloader_name->string << "\n"; - break; + case MULTIBOOT_TAG_TYPE_BOOTDEV: + multiboot_tag_bootdev* bootdev; + bootdev = (multiboot_tag_bootdev*) tag; + Logger::DEBUG() << "Boot device: drive=0x" << (uint64_t) bootdev->biosdev << ", partition=0x" << (uint64_t) bootdev->part << "\n"; + break; - case MULTIBOOT_TAG_TYPE_BOOTDEV: - multiboot_tag_bootdev *bootdev; - bootdev = (multiboot_tag_bootdev *)tag; - Logger::DEBUG() << "Boot device: 0x" << (uint64_t) bootdev->biosdev << ", 0x" << (uint64_t) bootdev->slice << ", 0x" << (uint64_t) bootdev->part << " of type 0x" << (uint64_t) bootdev->type << "\n"; - break; + case MULTIBOOT_TAG_TYPE_MMAP: - case MULTIBOOT_TAG_TYPE_MMAP: + // If there is not already a mmap tag, set it + if (m_mmap == nullptr) + m_mmap = (multiboot_tag_mmap*) tag; - // If there is not already a mmap tag, set it - if (m_mmap == nullptr) - m_mmap = (multiboot_tag_mmap *)tag; + break; - break; + case MULTIBOOT_TAG_TYPE_ACPI_OLD: + m_old_acpi = (multiboot_tag_old_acpi*) tag; + break; - case MULTIBOOT_TAG_TYPE_ACPI_OLD: - m_old_acpi = (multiboot_tag_old_acpi *)tag; - break; + case MULTIBOOT_TAG_TYPE_ACPI_NEW: + m_new_acpi = (multiboot_tag_new_acpi*) tag; + break; - case MULTIBOOT_TAG_TYPE_ACPI_NEW: - m_new_acpi = (multiboot_tag_new_acpi *)tag; - break; + case MULTIBOOT_TAG_TYPE_MODULE: + multiboot_tag_module* module; + module = (multiboot_tag_module*) tag; + Logger::DEBUG() << "Module: start=0x" << (uint64_t) module->mod_start << ", end=0x" << (uint64_t) module->mod_end << ", cmdline=" << module->cmdline << "\n"; + m_module = module; + break; + } - case MULTIBOOT_TAG_TYPE_MODULE: - multiboot_tag_module *module; - module = (multiboot_tag_module *)tag; - Logger::DEBUG() << "Module: start=0x" << (uint64_t) module->mod_start << ", end=0x" << (uint64_t) module->mod_end << ", cmdline=" << module->cmdline << "\n"; - m_module = module; - break; - } - - // Move to the next tag - tag = (struct multiboot_tag *) ((multiboot_uint8_t *) tag + ((tag->size + 7) & ~7)); - } + // Move to the next tag + tag = (struct multiboot_tag*) ((multiboot_uint8_t*) tag + ((tag->size + 7) & ~7)); + } } Multiboot::~Multiboot() = default; -multiboot_tag_framebuffer *Multiboot::framebuffer() { - - return m_framebuffer; +multiboot_tag_framebuffer* Multiboot::framebuffer() { + return m_framebuffer; } -multiboot_tag_basic_meminfo *Multiboot::basic_meminfo() { - - return m_basic_meminfo; +multiboot_tag_basic_meminfo* Multiboot::basic_meminfo() { + return m_basic_meminfo; } -multiboot_tag_string *Multiboot::bootloader_name() { - - return m_bootloader_name; +multiboot_tag_string* Multiboot::bootloader_name() { + return m_bootloader_name; } -multiboot_tag_mmap *Multiboot::mmap() { - - return m_mmap; +multiboot_tag_mmap* Multiboot::mmap() { + return m_mmap; } -multiboot_tag_old_acpi *Multiboot::old_acpi() { +multiboot_tag_old_acpi* Multiboot::old_acpi() { - return m_old_acpi; + return m_old_acpi; } -multiboot_tag_new_acpi *Multiboot::new_acpi() { +multiboot_tag_new_acpi* Multiboot::new_acpi() { - return m_new_acpi; + return m_new_acpi; } /** @@ -128,24 +121,24 @@ multiboot_tag_new_acpi *Multiboot::new_acpi() { */ bool Multiboot::is_reserved(multiboot_uint64_t address) { - // Loop through the tags checking if the address is reserved - for(multiboot_tag* tag = start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag *) ((multiboot_uint8_t *) tag + ((tag->size + 7) & ~7))) { + // Loop through the tags checking if the address is reserved + for (multiboot_tag* tag = start_tag(); tag->type != MULTIBOOT_TAG_TYPE_END; tag = (struct multiboot_tag*) ((multiboot_uint8_t*) tag + ((tag->size + 7) & ~7))) { - // Check if the tag is a module or mmap - if(tag -> type != MULTIBOOT_TAG_TYPE_MODULE && tag -> type != MULTIBOOT_TAG_TYPE_MMAP) - continue; + // Check if the tag is a module or mmap + if (tag->type != MULTIBOOT_TAG_TYPE_MODULE && tag->type != MULTIBOOT_TAG_TYPE_MMAP) + continue; - // Get the module tag - auto* module = (struct multiboot_tag_module*)tag; + // Get the module tag + auto* module = (struct multiboot_tag_module*) tag; - // Check if the address is within the module - if(address >= module -> mod_start && address < module -> mod_end) - return true; - } + // Check if the address is within the module + if (address >= module->mod_start && address < module->mod_end) + return true; + } - // Not part of multiboot - return false; + // Not part of multiboot + return false; } @@ -154,7 +147,7 @@ bool Multiboot::is_reserved(multiboot_uint64_t address) { * * @return The start tag */ -multiboot_tag *Multiboot::start_tag() const { +multiboot_tag* Multiboot::start_tag() const { - return (multiboot_tag*)(start_address + PhysicalMemoryManager::s_higher_half_kernel_offset + 8); + return (multiboot_tag*) (start_address + PhysicalMemoryManager::s_higher_half_kernel_offset + 8); } diff --git a/kernel/src/system/syscalls.cpp b/kernel/src/system/syscalls.cpp index c6f9ca71..a3fc57d1 100644 --- a/kernel/src/system/syscalls.cpp +++ b/kernel/src/system/syscalls.cpp @@ -5,6 +5,7 @@ #include #include +using namespace syscore; using namespace MaxOS; using namespace MaxOS::common; using namespace MaxOS::hardwarecommunication; @@ -14,34 +15,30 @@ using namespace MaxOS::memory; SyscallManager::SyscallManager() -: InterruptHandler(0x80) +: InterruptHandler(0x80) { - // Clear the args - Logger::INFO() << "Setting up Syscalls \n"; - m_current_args = new syscall_args_t; - - // Register the handlers - set_syscall_handler(SyscallType::CLOSE_PROCESS, syscall_close_process); - set_syscall_handler(SyscallType::KLOG, syscall_klog); - set_syscall_handler(SyscallType::CREATE_SHARED_MEMORY, syscall_create_shared_memory); - set_syscall_handler(SyscallType::OPEN_SHARED_MEMORY, syscall_open_shared_memory); - set_syscall_handler(SyscallType::ALLOCATE_MEMORY, syscall_allocate_memory); - set_syscall_handler(SyscallType::FREE_MEMORY, syscall_free_memory); - set_syscall_handler(SyscallType::CREATE_IPC_ENDPOINT, syscall_create_ipc_endpoint); - set_syscall_handler(SyscallType::SEND_IPC_MESSAGE, syscall_send_ipc_message); - set_syscall_handler(SyscallType::REMOVE_IPC_ENDPOINT, syscall_remove_ipc_endpoint); - set_syscall_handler(SyscallType::THREAD_YIELD, syscall_thread_yield); - set_syscall_handler(SyscallType::THREAD_SLEEP, syscall_thread_sleep); + // Clear the args + Logger::INFO() << "Setting up Syscalls \n"; + m_current_args = new syscall_args_t; + + // Register the handlers + set_syscall_handler(SyscallType::CLOSE_PROCESS, syscall_close_process); + set_syscall_handler(SyscallType::KLOG, syscall_klog); + set_syscall_handler(SyscallType::ALLOCATE_MEMORY, syscall_allocate_memory); + set_syscall_handler(SyscallType::FREE_MEMORY, syscall_free_memory); + set_syscall_handler(SyscallType::RESOURCE_CREATE, syscall_resource_create); + set_syscall_handler(SyscallType::RESOURCE_OPEN, syscall_resource_open); + set_syscall_handler(SyscallType::RESOURCE_CLOSE, syscall_resource_close); + set_syscall_handler(SyscallType::RESOURCE_WRITE, syscall_resource_write); + set_syscall_handler(SyscallType::RESOURCE_READ, syscall_resource_read); + set_syscall_handler(SyscallType::THREAD_YIELD, syscall_thread_yield); + set_syscall_handler(SyscallType::THREAD_SLEEP, syscall_thread_sleep); + set_syscall_handler(SyscallType::THREAD_CLOSE, syscall_thread_close); } -SyscallManager::~SyscallManager() -{ - // Delete the args - delete m_current_args; - -} +SyscallManager::~SyscallManager() = default; /** * @brief Loads the args from the registers and delegates the syscall to the relevant handler if defined @@ -51,39 +48,39 @@ SyscallManager::~SyscallManager() */ cpu_status_t* SyscallManager::handle_interrupt(cpu_status_t* status) { - // Get the args from the cpu state - m_current_args -> arg0 = status -> rdi; - m_current_args -> arg1 = status -> rsi; - m_current_args -> arg2 = status -> rdx; - m_current_args -> arg3 = status -> r10; - m_current_args -> arg4 = status -> r8; - m_current_args -> arg5 = status -> r9; - m_current_args -> return_value = 0; - m_current_args -> return_state = status; - - // Call the handler - uint64_t syscall = status -> rax; - if(m_syscall_handlers[syscall] != nullptr) - m_current_args = m_syscall_handlers[syscall](m_current_args); - else - Logger::ERROR() << "Syscall " << syscall << " not found\n"; - - // If there is a specific return state, use that - if(m_current_args -> return_state != status) - return m_current_args -> return_state; - - // Update the cpu state - status -> rdi = m_current_args -> arg0; - status -> rsi = m_current_args -> arg1; - status -> rdx = m_current_args -> arg2; - status -> r10 = m_current_args -> arg3; - status -> r8 = m_current_args -> arg4; - status -> r9 = m_current_args -> arg5; - status -> rax = m_current_args -> return_value; - - - // Return the status - return status; + // Get the args from the cpu state + m_current_args->arg0 = status->rdi; + m_current_args->arg1 = status->rsi; + m_current_args->arg2 = status->rdx; + m_current_args->arg3 = status->r10; + m_current_args->arg4 = status->r8; + m_current_args->arg5 = status->r9; + m_current_args->return_value = 0; + m_current_args->return_state = status; + + // Call the handler + uint64_t syscall = status->rax; + if (m_syscall_handlers[syscall] != nullptr) + m_current_args = m_syscall_handlers[syscall](m_current_args); + else + Logger::ERROR() << "Syscall " << syscall << " not found\n"; + + // If there is a specific return state, use that + if (m_current_args->return_state != status) + return m_current_args->return_state; + + // Update the cpu state + status->rdi = m_current_args->arg0; + status->rsi = m_current_args->arg1; + status->rdx = m_current_args->arg2; + status->r10 = m_current_args->arg3; + status->r8 = m_current_args->arg4; + status->r9 = m_current_args->arg5; + status->rax = m_current_args->return_value; + + + // Return the status + return status; } /** @@ -92,8 +89,9 @@ cpu_status_t* SyscallManager::handle_interrupt(cpu_status_t* status) { * @param syscall The syscall ID number * @param handler The handler to set */ -void SyscallManager::set_syscall_handler(uint8_t syscall, syscall_func_t handler) { - m_syscall_handlers[syscall] = handler; +void SyscallManager::set_syscall_handler(SyscallType syscall, syscall_func_t handler) { + + m_syscall_handlers[(uint8_t) syscall] = handler; } /** @@ -101,8 +99,9 @@ void SyscallManager::set_syscall_handler(uint8_t syscall, syscall_func_t handler * * @param syscall The syscall ID number */ -void SyscallManager::remove_syscall_handler(uint8_t syscall) { - m_syscall_handlers[syscall] = nullptr; +void SyscallManager::remove_syscall_handler(SyscallType syscall) { + + m_syscall_handlers[(uint8_t) syscall] = nullptr; } @@ -112,211 +111,191 @@ void SyscallManager::remove_syscall_handler(uint8_t syscall) { * @param args Arg0 = pid Arg1 = exit code * @return Nothing */ -system::syscall_args_t* SyscallManager::syscall_close_process(system::syscall_args_t *args) { - - // Get the args - uint64_t pid = args -> arg0; - int exit_code = (int)args -> arg1; +syscall_args_t* SyscallManager::syscall_close_process(system::syscall_args_t* args) { - // Get the process if it is 0 then it is the current process - Process* process = pid == 0 ? Scheduler::current_process() : Scheduler::get_process(pid); + // Get the args + uint64_t pid = args->arg0; + int exit_code = (int) args->arg1; - // Close the process - Scheduler::system_scheduler() -> remove_process(process); + // Close the process + Process* process = pid == 0 ? Scheduler::current_process() : Scheduler::get_process(pid); + Scheduler::system_scheduler()->remove_process(process); - // Schedule the next process - cpu_status_t* next_process = Scheduler::system_scheduler() -> schedule_next(args -> return_state); - args -> return_state = next_process; + // Schedule the next process + cpu_status_t* next_process = Scheduler::system_scheduler()->schedule_next(args->return_state); + args->return_state = next_process; - // Done - return args; + // Done + return args; } -syscall_args_t *SyscallManager::syscall_klog(syscall_args_t *args) { +syscall_args_t* SyscallManager::syscall_klog(syscall_args_t* args) { - char* message = (char*)args -> arg0; + char* message = (char*) args->arg0; - // If the first two characters are %h then no header - if(message[0] == '%' && message[1] == 'h') - Logger::INFO() << message + 2; - else - Logger::INFO() << ANSI_COLOURS[FG_Blue] << "(" << Scheduler::current_process() -> name.c_str() << ":" << Scheduler::current_thread() -> tid << "): " << ANSI_COLOURS[Reset] << message; + // If the first two characters are %h then no header + if (message[0] == '%' && message[1] == 'h') + Logger::Out() << message + 2; + else + Logger::INFO() << ANSI_COLOURS[FG_Blue] << "(" << Scheduler::current_process()->name.c_str() << ":" << Scheduler::current_thread()->tid << "): " << ANSI_COLOURS[Reset] << message; - return args; + return args; } - - /** - * @brief System call to create a shared memory block (To close, free the memory, it is automatically handled when the process exits) + * @brief System call to free a block of memory * - * @param args Arg0 = size, Arg1 = name - * @return The virtual address of the shared memory block or null if failed + * @param args Arg0 = size + * @return The address of the memory block or null if failed */ -syscall_args_t* SyscallManager::syscall_create_shared_memory(syscall_args_t *args) { - - // Extract the arguments - size_t size = args->arg0; - char* name = (char*)args->arg1; - - // Ensure they are valid - if(size == 0 || name == nullptr) - return nullptr; +syscall_args_t* SyscallManager::syscall_allocate_memory(syscall_args_t* args) { - // Create the memory block - SharedMemory* new_block = Scheduler::scheduler_ipc() ->alloc_shared_memory(size, name); - - // Load the block - void* virtual_address = MemoryManager::s_current_memory_manager -> vmm() ->load_shared_memory(new_block -> physical_address(), size); - - // Return to the user - args -> return_value = (uint64_t)virtual_address; - return args; + // Malloc the memory + size_t size = args->arg0; + void* address = MemoryManager::malloc(size); + // Return the address + args->return_value = (uint64_t)address; + return args; } /** - * @brief System call to open a shared memory block (To close, free the memory, it is automatically handled when the process exits) + * @brief System call to free a block of memory * - * @param args Arg0 = name - * @return The virtual address of the shared memory block or null if failed + * @param args Arg0 = address + * @return Nothing */ -syscall_args_t * SyscallManager::syscall_open_shared_memory(syscall_args_t *args) { - - // Extract the arguments - uint64_t name = args->arg0; +syscall_args_t* SyscallManager::syscall_free_memory(syscall_args_t* args) { - // Ensure they are valid - if(name == 0) - return nullptr; - - // Get the block (don't care if null as that is caught in the load_shared_memory function) - SharedMemory* block = Scheduler::scheduler_ipc() -> get_shared_memory((char*)name); - - // Load the block - void* virtual_address = MemoryManager::s_current_memory_manager -> vmm() ->load_shared_memory(block -> physical_address(), block -> size()); - - // Return to the user - args -> return_value = (uint64_t)virtual_address; - return args; + // Free the memory + void* address = (void*) args->arg0; + MemoryManager::free(address); + return args; } /** - * @brief System call to free a block of memory + * @brief System call to create a resource * - * @param args Arg0 = size - * @return The address of the memory block or null if failed + * @param args Arg0 = ResourceType Arg1 = Name Arg2 = Flags + * @return 1 if resource was created sucessfully, 0 otherwise */ -syscall_args_t* SyscallManager::syscall_allocate_memory(syscall_args_t *args) { +syscall_args_t* SyscallManager::syscall_resource_create(syscall_args_t* args) { - // Get the size - size_t size = args -> arg0; + // Parse params + auto type = (ResourceType)args->arg0; + auto name = (char*)args->arg1; + auto flags = (size_t)args->arg2; - // Malloc the memory - void* address = MemoryManager::malloc(size); - - // Return the address - args -> return_value = (uint64_t)address; - return args; + // Try to create the resource + auto resource = GlobalResourceRegistry::get_registry(type)->create_resource(name, flags); + // Handle response + args->return_value = resource ? 1 : 0; + return args; } /** - * @brief System call to free a block of memory + * @brief System call to open a resource * - * @param args Arg0 = address - * @return Nothing + * @param args Arg0 = ResourceType Arg1 = Name Arg2 = Flags + * @return The handle of the resource or 0 if failed */ -syscall_args_t* SyscallManager::syscall_free_memory(syscall_args_t *args) { - - // Get the address - void* address = (void*)args -> arg0; - - // Free the memory - MemoryManager::free(address); +syscall_args_t* SyscallManager::syscall_resource_open(syscall_args_t* args) { - // Done - return args; + // Parse params + auto type = (ResourceType)args->arg0; + auto name = (char*)args->arg1; + auto flags = (size_t)args->arg2; + // Open the resource + args->return_value = Scheduler::current_process()->resource_manager.open_resource(type, name, flags); + return args; } /** - * @brief System call to create an IPC endpoint + * @brief System call to close a resource * - * @param args Arg0 = name - * @return The IPC endpoint buffer linked list address + * @param args Arg0 = Handle Arg1 = Flags + * @return Nothing */ -system::syscall_args_t* SyscallManager::syscall_create_ipc_endpoint(system::syscall_args_t *args) { - - // Get the name - char* name = (char*)args -> arg0; - if(name == nullptr) - return nullptr; - - // Create the endpoint - SharedMessageEndpoint* endpoint = Scheduler::scheduler_ipc() -> create_message_endpoint(name); +syscall_args_t* SyscallManager::syscall_resource_close(syscall_args_t* args) { - // Return the endpoint - args -> return_value = (uint64_t)endpoint -> queue(); - return args; + // Parse params + auto handle = (uint64_t )args->arg0; + auto flags = (size_t)args->arg1; + // Close the resource + Scheduler::current_process()->resource_manager.close_resource(handle, flags); + return args; } /** - * @brief System call to send an IPC message + * @brief System call to write to a resource * - * @param args Arg0 = endpoint name, Arg1 = message, Arg2 = size - * @return Nothing + * @param args Arg0 = Handle Arg1 = Buffer Arg2 = Size Arg3 = Flags + * @return The number of bytes written or 0 if failed */ -system::syscall_args_t* SyscallManager::syscall_send_ipc_message(system::syscall_args_t *args) { - - // Get the args - char* endpoint = (char*)args -> arg0; - void* message = (void*)args -> arg1; - size_t size = args -> arg2; - - // Validate the args - if(endpoint == nullptr || message == nullptr || size == 0) - return nullptr; - - // Send the message - Scheduler::scheduler_ipc() ->get_message_endpoint(endpoint) -> queue_message(message, size); - - // All done - return args; +syscall_args_t* SyscallManager::syscall_resource_write(syscall_args_t* args) { + + // Parse params + auto handle = (uint64_t )args->arg0; + auto buffer = (void*)args->arg1; + auto size = (size_t)args->arg2; + auto flags = (size_t)args->arg3; + + // Open the resource + auto resource = Scheduler::current_process()->resource_manager.get_resource(handle); + if(!resource){ + args->return_value = 0; + return args; + } + + // Write to the resource + args->return_value = resource->write(buffer,size,flags); + return args; } /** - * @brief System call to remove an IPC endpoint + * @brief System call to read from a resource * - * @param args Arg0 = endpoint name - * @return Nothing + * @param args Arg0 = Handle Arg1 = Buffer Arg2 = Size Arg3 = Flags + * @return The number of bytes read or 0 if failed */ -system::syscall_args_t * SyscallManager::syscall_remove_ipc_endpoint(system::syscall_args_t *args) { - - // Remove the endpoint - Scheduler::scheduler_ipc() -> free_message_endpoint((char*)args -> arg0); - - // Done - return args; - +syscall_args_t* SyscallManager::syscall_resource_read(syscall_args_t* args) { + + // Parse params + auto handle = (uint64_t )args->arg0; + auto buffer = (void*)args->arg1; + auto size = (size_t)args->arg2; + auto flags = (size_t)args->arg3; + + // Open the resource + auto resource = Scheduler::current_process()->resource_manager.get_resource(handle); + if(!resource){ + args->return_value = 0; + return args; + } + + // Write to the resource + args->return_value = resource->read(buffer,size,flags); + return args; } - /** * @brief System call to yield the current process * * @param args Nothing * @return Nothing */ -system::syscall_args_t* SyscallManager::syscall_thread_yield(system::syscall_args_t *args) { +syscall_args_t* SyscallManager::syscall_thread_yield(syscall_args_t* args) { - // Yield - cpu_status_t* next_process = Scheduler::system_scheduler() -> yield(); - args -> return_state = next_process; + // Yield + Scheduler::current_thread()->execution_state = args->return_state; + cpu_status_t* next_process = Scheduler::system_scheduler()->yield(); + args->return_state = next_process; - return args; + return args; } /** @@ -325,21 +304,20 @@ system::syscall_args_t* SyscallManager::syscall_thread_yield(system::syscall_arg * @param args Arg0 = milliseconds * @return Nothing */ -system::syscall_args_t* SyscallManager::syscall_thread_sleep(system::syscall_args_t *args) { - - // Get the milliseconds - size_t milliseconds = args -> arg0; +syscall_args_t* SyscallManager::syscall_thread_sleep(syscall_args_t* args) { - // Store the updated state in the thread as the scheduler will not have the updated state when switching to the next thread - Scheduler::current_thread() -> execution_state = args -> return_state; + // Get the milliseconds + size_t milliseconds = args->arg0; - // Sleep the thread - cpu_status_t* next_thread = Scheduler::current_thread() -> sleep(milliseconds); - args -> return_state = next_thread; + // Store the updated state in the thread as the scheduler will not have the updated state when switching to the next thread + Scheduler::current_thread()->execution_state = args->return_state; - // Done - return args; + // Sleep the thread + cpu_status_t* next_thread = Scheduler::current_thread()->sleep(milliseconds); + args->return_state = next_thread; + // Done + return args; } /** @@ -348,24 +326,22 @@ system::syscall_args_t* SyscallManager::syscall_thread_sleep(system::syscall_arg * @param args Arg0 = tid Arg1 = exit code * @return Nothing */ -system::syscall_args_t* SyscallManager::syscall_thread_close(system::syscall_args_t *args) { - +syscall_args_t* SyscallManager::syscall_thread_close(syscall_args_t* args) { - // Get the args - uint64_t tid = args -> arg0; - int exit_code = (int)args -> arg1; + // Get the args + uint64_t tid = args->arg0; + int exit_code = (int) args->arg1; - // Get the thread if it is 0 then it is the current thread - Thread* thread = tid == 0 ? Scheduler::current_thread() : Scheduler::get_thread(tid); + // Get the thread if it is 0 then it is the current thread + Thread* thread = tid == 0 ? Scheduler::current_thread() : Scheduler::get_thread(tid); - // Close the thread - Scheduler::get_process(thread -> parent_pid) -> remove_thread(tid); + // Close the thread + Scheduler::get_process(thread->parent_pid)->remove_thread(tid); - // Schedule the next thread - cpu_status_t* next_thread = Scheduler::system_scheduler() -> schedule_next(args -> return_state); - args -> return_state = next_thread; + // Schedule the next thread + cpu_status_t* next_thread = Scheduler::system_scheduler()->schedule_next(args->return_state); + args->return_state = next_thread; - // Done - return args; - -} + // Done + return args; +} \ No newline at end of file diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt new file mode 100644 index 00000000..f5eb0cdb --- /dev/null +++ b/libraries/CMakeLists.txt @@ -0,0 +1,2 @@ +# The libraries +add_subdirectory(syscore) \ No newline at end of file diff --git a/libraries/syscore/CMakeLists.txt b/libraries/syscore/CMakeLists.txt new file mode 100644 index 00000000..4c1d4ec0 --- /dev/null +++ b/libraries/syscore/CMakeLists.txt @@ -0,0 +1,20 @@ +# NOTE: Cant use MAKE_LIBRARY() as dependency of libc + +# Set compiler flags +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffreestanding -fno-exceptions -fno-rtti -fpermissive -nostdlib -Wall -Wextra -mno-red-zone -mno-80387 -mno-mmx -fno-use-cxa-atexit") + +# Disable noisy warnings +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pointer-arith -Wno-address-of-packed-member -Wno-trigraphs -Wno-unused-variable -Wno-unused-function") + +# Collect sources +FILE(GLOB_RECURSE SYSTEM_SRCS src/*.cpp src/*.s) + +# Build as a dynamic library +ADD_LIBRARY(syscore SHARED ${SYSTEM_SRCS}) +TARGET_INCLUDE_DIRECTORIES(syscore PUBLIC include) +INSTALL(TARGETS syscore LIBRARY DESTINATION ${CMAKE_SOURCE_DIR}/filesystem/os/lib) + +# Build as a static library +ADD_LIBRARY(syscore_static STATIC ${SYSTEM_SRCS}) +TARGET_INCLUDE_DIRECTORIES(syscore_static PUBLIC include) +INSTALL(TARGETS syscore_static DESTINATION ${CMAKE_SOURCE_DIR}/filesystem/os/lib) \ No newline at end of file diff --git a/libraries/syscore/include/common.h b/libraries/syscore/include/common.h new file mode 100644 index 00000000..b7bb7412 --- /dev/null +++ b/libraries/syscore/include/common.h @@ -0,0 +1,28 @@ +// +// Created by 98max on 9/1/2025. +// + +#ifndef SYSCORE_COMMON_H +#define SYSCORE_COMMON_H + +namespace syscore{ + + /** + * @brief Gets the length of a string + * + * @param str The string to get the length of + * @return The length of the string + */ + inline int strlen(const char *str) { + + if(str == nullptr) + return 0; + + int len = 0; + for (; str[len] != '\0'; len++); + return len; + } + +} + +#endif //SYSCORE_COMMON_H diff --git a/libraries/syscore/include/filesystem/directory.h b/libraries/syscore/include/filesystem/directory.h new file mode 100644 index 00000000..8df9d749 --- /dev/null +++ b/libraries/syscore/include/filesystem/directory.h @@ -0,0 +1,50 @@ +// +// Created by 98max on 9/1/2025. +// + +#ifndef SYSCORE_FILESYSTEM_DIRECTORY_H +#define SYSCORE_FILESYSTEM_DIRECTORY_H + +#include +#include +#include +#include + + +namespace syscore{ + namespace filesystem{ + + enum class DirectoryFlags{ + READ_ENTRIES, + READ_ENTRIES_SIZE, + WRITE_NAME, + WRITE_NEW_FILE, + WRITE_NEW_DIR, + WRITE_REMOVE_FILE, + WRITE_REMOVE_DIR + }; + + typedef struct EntryInformation{ + size_t entry_length; + size_t size; + bool is_file; + char name[]; + } entry_information_t; + + uint64_t open_directory(const char* path); + void rename_directory(uint64_t handle, const char* name); + void close_directory(uint64_t handle); + + size_t directory_entries_size(uint64_t handle); + void directory_entries(uint64_t handle, void* buffer, size_t size); + + void new_file(uint64_t handle, const char* name); + void new_directory(uint64_t handle, const char* name); + + void remove_file(uint64_t handle, const char* name); + void remove_directory(uint64_t handle, const char* name); + + } +} + +#endif //SYSCORE_FILESYSTEM_DIRECTORY_H diff --git a/libraries/syscore/include/filesystem/file.h b/libraries/syscore/include/filesystem/file.h new file mode 100644 index 00000000..52f35b94 --- /dev/null +++ b/libraries/syscore/include/filesystem/file.h @@ -0,0 +1,46 @@ +// +// Created by 98max on 9/1/2025. +// + +#ifndef SYSCORE_FILESYSTEM_FILE_H +#define SYSCORE_FILESYSTEM_FILE_H + +#include +#include +#include +#include + +namespace syscore{ + namespace filesystem{ + + enum class FileFlags{ + DEFAULT, + READ_SIZE, + READ_OFFSET, + WRITE_SEEK_SET, + WRITE_SEEK_CUR, + WRITE_SEEK_END, + WRITE_NAME, + }; + + enum class SeekType{ + SET, + CURRENT, + END + }; + + uint64_t open_file(const char* path); + void close_file(uint64_t handle); + + size_t file_size(uint64_t handle); + size_t file_offset(uint64_t handle); + + size_t file_read(uint64_t handle, void* buffer, size_t size); + size_t file_write(uint64_t handle, void* buffer, size_t size); + + void rename_file(uint64_t handle, const char* name); + void seek_file(uint64_t handle, size_t position, SeekType seek_type); + } +} + +#endif //SYSCORE_FILESYSTEM_FILE_H diff --git a/libraries/syscore/include/ipc/messages.h b/libraries/syscore/include/ipc/messages.h new file mode 100644 index 00000000..cf42e5f1 --- /dev/null +++ b/libraries/syscore/include/ipc/messages.h @@ -0,0 +1,27 @@ +// +// Created by 98max on 8/31/2025. +// + +#ifndef SYSCORE_IPC_MESSAGES_H +#define SYSCORE_IPC_MESSAGES_H + +#include +#include +#include + +namespace syscore{ + namespace ipc{ + + uint64_t create_endpoint(const char* name); + uint64_t open_endpoint(const char* name); + void close_endpoint(uint64_t endpoint); + + void send_message(uint64_t endpoint, void* buffer, size_t size); + void read_message(uint64_t endpoint, void* buffer, size_t size); + } +} + + + + +#endif //IPC_MESSAGES_H diff --git a/libraries/syscore/include/ipc/sharedmemory.h b/libraries/syscore/include/ipc/sharedmemory.h new file mode 100644 index 00000000..3953517a --- /dev/null +++ b/libraries/syscore/include/ipc/sharedmemory.h @@ -0,0 +1,21 @@ +// +// Created by 98max on 8/31/2025. +// + +#ifndef SYSCORE_IPC_SHAREDMEMORY_H +#define SYSCORE_IPC_SHAREDMEMORY_H + +#include +#include +#include + +namespace syscore { + namespace ipc { + + void* create_shared_memory(const char* name, size_t size); + + void* open_shared_memory(const char* name); + } +} + +#endif //SYSCORE_IPC_SHAREDMEMORY_H diff --git a/libraries/syscore/include/syscalls.h b/libraries/syscore/include/syscalls.h new file mode 100644 index 00000000..30e4c5ce --- /dev/null +++ b/libraries/syscore/include/syscalls.h @@ -0,0 +1,60 @@ +// +// Created by 98max on 8/31/2025. +// + +#ifndef SYSCORE_SYSCALLS_H +#define SYSCORE_SYSCALLS_H + +#include +#include + +namespace syscore{ + + enum class ResourceType{ + MESSAGE_ENDPOINT, + SHARED_MEMORY, + FILESYSTEM, + }; + + enum class ResourceErrorBase{ + SHOULD_BLOCK = 1, + + _END // Use this to extend errors + }; + int as_error(ResourceErrorBase code); + + enum class SyscallType{ + CLOSE_PROCESS, + KLOG, // TODO: Turn into open proc + ALLOCATE_MEMORY, + FREE_MEMORY, + RESOURCE_CREATE, + RESOURCE_OPEN, + RESOURCE_CLOSE, + RESOURCE_WRITE, + RESOURCE_READ, + THREAD_YIELD, + THREAD_SLEEP, + THREAD_CLOSE, + }; + + void* make_syscall(SyscallType type, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5); + + void close_process(uint64_t pid, int status); + void klog(const char* message); + + void* allocate_memory(size_t size); + void free_memory(void* address); + + bool resource_create(ResourceType type, const char* name, size_t flags); + uint64_t resource_open(ResourceType type, const char* name, size_t flags); + void resource_close(uint64_t handle, size_t flags); + size_t resource_write(uint64_t handle, const void* buffer, size_t size, size_t flags); + size_t resource_read(uint64_t handle, void* buffer, size_t size, size_t flags); + + void thread_yield(); + void thread_sleep(uint64_t time); + void thread_exit(); +} + +#endif //SYSCORE_SYSCALLS_H diff --git a/libraries/syscore/src/filesystem/directory.cpp b/libraries/syscore/src/filesystem/directory.cpp new file mode 100644 index 00000000..d0fa5887 --- /dev/null +++ b/libraries/syscore/src/filesystem/directory.cpp @@ -0,0 +1,107 @@ +// +// Created by 98max on 9/1/2025. +// + +#include + +namespace syscore{ + namespace filesystem{ + + + /** + * @brief Opens a directory from a path (must end in /) + * + * @param path The path to the directory + * @return The handle of the opened directory or 0 if it failed + */ + uint64_t open_directory(const char* path){ + return resource_open(ResourceType::FILESYSTEM, path, 0); + } + + /** + * @brief Rename the directory + * + * @param handle The directory to rename + * @param name What to rename the directory to + */ + void rename_directory(uint64_t handle, const char* name){ + resource_write(handle, name, strlen(name), (size_t)DirectoryFlags::WRITE_NAME); + } + + /** + * @brief Closes an open directory + * + * @param handle The directory open + */ + void close_directory(uint64_t handle){ + resource_close(handle, 0); + } + + /** + * @brief Gets the size required to hold the list of directory entries + * + * @param handle The directory to list + * @return The size of the total entries + */ + size_t directory_entries_size(uint64_t handle){ + return resource_read(handle, 0,0, (size_t)DirectoryFlags::READ_ENTRIES_SIZE); + } + + /** + * @brief Reads the contents of a directory (as a list of entry_information_t) + * + * @param handle The directory to read + * @param buffer Where to read the list + * @param size How big is the buffer + */ + void directory_entries(uint64_t handle, void* buffer, size_t size){ + + resource_read(handle, buffer, size,(size_t)DirectoryFlags::READ_ENTRIES); + } + + /** + * @brief Creates a new file in the directory + * + * @param handle The directory to create the file in + * @param name The name of the new file + */ + void new_file(uint64_t handle, const char* name){ + + resource_write(handle, name, strlen(name), (size_t)DirectoryFlags::WRITE_NEW_FILE); + } + + /** + * @brief Creates a new subdirectory in the directory + * + * @param handle The directory to create the subdirectory in + * @param name The name of the new directory + */ + void new_directory(uint64_t handle, const char* name){ + + resource_write(handle, name, strlen(name), (size_t)DirectoryFlags::WRITE_NEW_DIR); + } + + /** + * @brief Removes a file from the directory + * + * @param handle The directory to remove the file from + * @param name The name of the file to remove + */ + void remove_file(uint64_t handle, const char* name){ + + resource_write(handle, name, strlen(name), (size_t)DirectoryFlags::WRITE_REMOVE_FILE); + } + + /** + * @brief Removes a subdirectory from the directory + * + * @param handle The directory to remove the subdirectory from + * @param name The name of the subdirectory to remove + */ + void remove_directory(uint64_t handle, const char* name){ + + resource_write(handle, name, strlen(name), (size_t)DirectoryFlags::WRITE_REMOVE_DIR); + } + + } +} \ No newline at end of file diff --git a/libraries/syscore/src/filesystem/file.cpp b/libraries/syscore/src/filesystem/file.cpp new file mode 100644 index 00000000..81f28191 --- /dev/null +++ b/libraries/syscore/src/filesystem/file.cpp @@ -0,0 +1,59 @@ +// +// Created by 98max on 9/1/2025. +// + +#include + +namespace syscore { + namespace filesystem { + + uint64_t open_file(const char* path){ + return resource_open(ResourceType::FILESYSTEM, path, 0); + } + + void close_file(uint64_t handle){ + resource_close(handle, 0); + } + + size_t file_size(uint64_t handle){ + return resource_read(handle, 0, 0, (size_t)FileFlags::READ_SIZE); + } + + size_t file_offset(uint64_t handle){ + return resource_read(handle, 0, 0, (size_t)FileFlags::READ_OFFSET); + } + + size_t file_read(uint64_t handle, void* buffer, size_t size){ + return resource_read(handle, buffer, size, (size_t)FileFlags::DEFAULT); + } + + size_t file_write(uint64_t handle, void* buffer, size_t size){ + return resource_write(handle, buffer, size, (size_t)FileFlags::DEFAULT); + } + + void rename_file(uint64_t handle, const char* name){ + + resource_write(handle, name, strlen(name), (size_t)FileFlags::WRITE_NAME); + } + + void seek_file(uint64_t handle, size_t position, SeekType seek_type){ + + switch (seek_type) { + + case SeekType::SET: + resource_write(handle, 0, position, (size_t)FileFlags::WRITE_SEEK_SET); + break; + + case SeekType::CURRENT: + resource_write(handle, 0, position, (size_t)FileFlags::WRITE_SEEK_CUR); + break; + + case SeekType::END: + resource_write(handle, 0, position, (size_t)FileFlags::WRITE_SEEK_END); + break; + + } + } + + } +} \ No newline at end of file diff --git a/libraries/syscore/src/ipc/messages.cpp b/libraries/syscore/src/ipc/messages.cpp new file mode 100644 index 00000000..6aa3f50a --- /dev/null +++ b/libraries/syscore/src/ipc/messages.cpp @@ -0,0 +1,73 @@ +// +// Created by 98max on 8/31/2025. +// + +#include + +namespace syscore{ + namespace ipc{ + + /** + * @brief Create a new endpoint for sending and receiving messages + * + * @param name The name of the endpoint + * @return The handle of the new endpoint or 0 if it failed + */ + uint64_t create_endpoint(const char* name){ + + // Create the resource + if(!resource_create(ResourceType::MESSAGE_ENDPOINT, name, 0)) + return 0; + + return open_endpoint(name); + } + + /** + * @brief Opens an endpoint under a specific name + * + * @param name The name of the endpoint to open + * @return The endpoint handle or 0 if it failed to open + */ + uint64_t open_endpoint(const char* name){ + return (uint64_t) resource_open(ResourceType::MESSAGE_ENDPOINT, name, 0); + } + + /** + * @brief Closes an endpoint + * + * @param endpoint The endpoint handle to close + */ + void close_endpoint(uint64_t endpoint){ + + if(endpoint) + resource_close(endpoint, 0); + + } + + /** + * @brief Queues a message on the endpoint (FIFO) + * + * @param endpoint The endpoint handle + * @param buffer The message + * @param size The size of the message + */ + void send_message(uint64_t endpoint, void* buffer, size_t size){ + + if(endpoint) + resource_write(endpoint, buffer, size, 0); + + } + + /** + * @brief Fetches the oldest message on the endpoint (FIFO) + * + * @param endpoint The endpoint handle + * @param buffer Where to read the message into + * @param size How much of the message to read + */ + void read_message(uint64_t endpoint, void* buffer, size_t size){ + if(endpoint) + resource_read(endpoint, buffer, size, 0); + } + } +} \ No newline at end of file diff --git a/libraries/syscore/src/ipc/sharedmemory.cpp b/libraries/syscore/src/ipc/sharedmemory.cpp new file mode 100644 index 00000000..530e3d5a --- /dev/null +++ b/libraries/syscore/src/ipc/sharedmemory.cpp @@ -0,0 +1,41 @@ +// +// Created by 98max on 8/31/2025. +// + +#include + +namespace syscore{ + namespace ipc{ + + /** + * @brief Create a new region of shared memory + * + * @param name The name of the region + * @param size The size of the region + * @return The start address of the region or nullptr if it failed to create + */ + void* create_shared_memory(const char* name, size_t size){ + + // Create the resource + if(!resource_create(ResourceType::SHARED_MEMORY, name, size)) + return nullptr; + + return open_shared_memory(name); + } + + void* open_shared_memory(const char* name){ + + // Open the resource + uint64_t handle = resource_open(ResourceType::SHARED_MEMORY, name, 0); + if(!handle) + return nullptr; + + // Get the address + auto address = resource_read(handle, nullptr, 0, 0); + if(!address) + return nullptr; + + return (void*)address; + } + }; +} diff --git a/libraries/syscore/src/syscalls.cpp b/libraries/syscore/src/syscalls.cpp new file mode 100644 index 00000000..536ef3f2 --- /dev/null +++ b/libraries/syscore/src/syscalls.cpp @@ -0,0 +1,187 @@ +// +// Created by 98max on 8/31/2025. +// + +#include + +namespace syscore{ + + int as_error(ResourceErrorBase code){ + return -1 * (int)code; + } + + /** + * @brief Make a syscall + * + * @param type The type of syscall + * @param arg0 The first argument + * @param arg1 The second argument + * @param arg2 The third argument + * @param arg3 The fourth argument + * @param arg4 The fifth argument + * @param arg5 The sixth argument + * @return The result of the syscall + */ + void* make_syscall(SyscallType type, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5){ + + void* result; + asm volatile( + "mov %[a0], %%rdi\n\t" // arg0 -> rdi + "mov %[a1], %%rsi\n\t" // arg1 -> rsi + "mov %[a2], %%rdx\n\t" // arg2 -> rdx + "mov %[a3], %%r10\n\t" // arg3 -> r10 + "mov %[a4], %%r8\n\t" // arg4 -> r8 + "mov %[a5], %%r9\n\t" // arg5 -> r9 + "mov %[num], %%rax\n\t" // syscall number -> rax + "int $0x80\n\t" + : "=a"(result) + : [num] "r"((uint64_t)type), + [a0] "r"(arg0), + [a1] "r"(arg1), + [a2] "r"(arg2), + [a3] "r"(arg3), + [a4] "r"(arg4), + [a5] "r"(arg5) + : "rdi", "rsi", "rdx", "r10", "r8", "r9", "memory" + ); + + return result; + + } + + /** + * @brief Close a process + * + * @param pid The process ID (0 for current process) + * @param status The exit status + */ + void close_process(uint64_t pid, int status){ + make_syscall(SyscallType::CLOSE_PROCESS, pid, (uint64_t)status, 0, 0, 0, 0); + } + + /** + * @brief Log a message to the kernel log + * + * @param message The message to log + */ + void klog(const char* message){ + make_syscall(SyscallType::KLOG, (uint64_t)message, 0, 0, 0, 0, 0); + } + + /** + * @brief Allocate memory + * + * @param size The size of memory to allocate + * @return Pointer to the allocated memory + */ + void* allocate_memory(size_t size){ + return make_syscall(SyscallType::ALLOCATE_MEMORY, size, 0, 0, 0, 0, 0); + } + + /** + * @brief Free allocated memory + * + * @param address The address of the memory to free + */ + void free_memory(void* address){ + make_syscall(SyscallType::FREE_MEMORY, (uint64_t)address, 0, 0, 0, 0, 0); + } + + /** + * @brief Create a resource + * + * @param type The type of resource + * @param name The name of the resource + * @param flags Optional flags to pass (unused by default but resource type specific) + * @return The handle id of the resource or 0 if failed + */ + bool resource_create(ResourceType type, const char* name, size_t flags){ + return (bool)make_syscall(SyscallType::RESOURCE_CREATE, (uint64_t)type, (uint64_t)name, flags, 0, 0, 0); + } + + /** + * @brief Open an existing resource + * + * @param type The type of resource + * @param name The name of the resource + * @param flags Optional flags to pass + * @return The handle id of the resource or 0 if failed + */ + uint64_t resource_open(ResourceType type, const char* name, size_t flags){ + return (uint64_t)make_syscall(SyscallType::RESOURCE_OPEN, (uint64_t)type, (uint64_t)name, flags, 0, 0, 0); + } + + /** + * @brief Close a resource + * + * @param handle The handle number of the resource + * @param flags Flags to pass to the closing + */ + void resource_close(uint64_t handle, size_t flags){ + make_syscall(SyscallType::RESOURCE_CLOSE, handle, flags, 0, 0, 0, 0); + } + + /** + * @brief Write a certain amount of bytes to a resource + * + * @param handle The handle number of the resource + * @param buffer The buffer to read from + * @param size The number of bytes to write + * @param flags The flags to pass to the writing + * @return The number of bytes successfully written + */ + size_t resource_write(uint64_t handle, const void* buffer, size_t size, size_t flags){ + + int response = (int)(uintptr_t)make_syscall(SyscallType::RESOURCE_WRITE, handle, (uint64_t)buffer, size, flags, 0, 0); + while (response == as_error(ResourceErrorBase::SHOULD_BLOCK)){ + thread_yield(); + response = (int)(uintptr_t)make_syscall(SyscallType::RESOURCE_WRITE, handle, (uint64_t)buffer, size, flags, 0, 0); + } + + return response; + } + + /** + * @brief Read a certain amount of bytes from a resource + * + * @param handle The handle number of the resource + * @param buffer The buffer to read into + * @param size The number of bytes to read + * @param flags The flags to pass to the reading + * @return The number of bytes successfully read + */ + size_t resource_read(uint64_t handle, void* buffer, size_t size, size_t flags){ + int response = (int)(uintptr_t)make_syscall(SyscallType::RESOURCE_READ, handle, (uint64_t)buffer, size, flags, 0, 0); + while (response == as_error(ResourceErrorBase::SHOULD_BLOCK)){ + thread_yield(); + response = (int)(uintptr_t)make_syscall(SyscallType::RESOURCE_READ, handle, (uint64_t)buffer, size, flags, 0, 0); + } + + return response; + } + + /** + * @brief Yield the current thread's execution + * + */ + void thread_yield(){ + make_syscall(SyscallType::THREAD_YIELD, 0, 0, 0, 0, 0, 0); + } + + /** + * @brief Sleep the current thread for a certain amount of time + * + * @param time The time to sleep in milliseconds + */ + void thread_sleep(uint64_t time){ + make_syscall(SyscallType::THREAD_SLEEP, time, 0, 0, 0, 0, 0); + } + + /** + * @brief Exit the current thread + * + */ + void thread_exit(){ + make_syscall(SyscallType::THREAD_CLOSE, 0, 0, 0, 0, 0, 0); + } +} \ No newline at end of file diff --git a/programs/Example/CMakeLists.txt b/programs/Example/CMakeLists.txt index 36ec7b11..02d8e382 100644 --- a/programs/Example/CMakeLists.txt +++ b/programs/Example/CMakeLists.txt @@ -12,7 +12,7 @@ ADD_EXECUTABLE(test.elf ${PROJ_SRCS}) # Set the linker info SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/programs/Example/src/linker.ld) SET_TARGET_PROPERTIES(test.elf PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) -TARGET_LINK_LIBRARIES(test.elf gcc) +TARGET_LINK_LIBRARIES(test.elf gcc syscore_static) TARGET_LINK_OPTIONS(test.elf PRIVATE -T ${LINKER_SCRIPT} -nostdlib -n) # Install the executable diff --git a/programs/Example/src/main.cpp b/programs/Example/src/main.cpp index d19bca07..b582ae37 100644 --- a/programs/Example/src/main.cpp +++ b/programs/Example/src/main.cpp @@ -3,101 +3,79 @@ // #include #include +#include +#include -// Write using a syscall (int 0x80 with syscall 0x01 for write) -void write(const char* data) -{ - // don't care abt length for now - asm volatile("int $0x80" : : "a" (0x01), "b" (data)); -} - -void write_hex(uint64_t data) -{ - // Convert to hex - char buffer[20]; - buffer[0] = '0'; - buffer[1] = 'x'; - buffer[18] = '\n'; - buffer[19] = '\0'; - - // Convert to hex - for(int i = 0; i < 16; i++) - { - // Get the current nibble - uint8_t nibble = (data >> (60 - i * 4)) & 0xF; - - // Convert to hex - buffer[2 + i] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10; - } +using namespace syscore; +using namespace syscore::ipc; +using namespace syscore::filesystem; - // Write the hex - write(buffer); +// Write using a syscall (int 0x80 with syscall 0x01 for write) +void write(const char* data) { + // don't care abt length for now + asm volatile("int $0x80" : : "a" (0x01), "b" (data)); } -// Create a shared memory block (int 0x80 with syscall 0x02 for create shared memory, result in rax) -void* create_shared_memory(uint64_t size, const char* name) -{ - void* result = nullptr; - asm volatile("int $0x80" : "=a" (result) : "a" (0x02), "b" (size), "c" (name)); - return result; +void write_hex(uint64_t data) { + // Convert to hex + char buffer[20]; + buffer[0] = '0'; + buffer[1] = 'x'; + buffer[18] = '\n'; + buffer[19] = '\0'; + + // Convert to hex + for (int i = 0; i < 16; i++) { + // Get the current nibble + uint8_t nibble = (data >> (60 - i * 4)) & 0xF; + + // Convert to hex + buffer[2 + i] = nibble < 10 ? '0' + nibble : 'A' + nibble - 10; + } + + // Write the hex + write(buffer); } -typedef struct SharedMessage{ - void* message_buffer; - size_t message_size; - uintptr_t next_message; -} ipc_message_t; - -typedef struct SharedMessageQueue{ - ipc_message_t* messages; -} ipc_message_queue_t; - -void* make_message_queue(char* name) -{ - void* result = nullptr; - asm volatile("int $0x80" : "=a" (result) : "a" (0x06), "b" (name)); - return result; -} +void yield() { -void yield() -{ - asm volatile("int $0x80" : : "a" (0x09)); + asm volatile("int $0x80" : : "a" (0x09)); } -extern "C" [[noreturn]] void _start(void) -{ +extern "C" [[noreturn]] void _start(void) { - // Write to the console - write("MaxOS Test Program v3\n"); + // Write to the console + write("MaxOS Test Program v3\n"); - // Create a message endpoint - ipc_message_queue_t* message_queue = (ipc_message_queue_t *)make_message_queue("TestQueue"); - if (!message_queue) - { - write("Failed to create message queue\n"); - while (true) - yield(); - } + uint64_t fd = open_file("/test/a.txt"); + write("fd: \n"); + write_hex(fd); - write("Message queue created: \n"); - write_hex((uint64_t)message_queue); - - // Process events forever: - write("Waiting for messages\n"); - while(true) - if(message_queue->messages == nullptr) - yield(); - else{ - - - // Store the message - ipc_message_t* message = message_queue->messages; - write("Message received: \n"); - write_hex((uint64_t)message); - write((char*)message->message_buffer); - - // Move to the next message - message_queue->messages = (ipc_message_t*)message->next_message; + // Read Tests +// uint8_t buffer[50] = {}; +// file_read(fd, buffer, 50); +// write("contents: \n"); +// write((char*)buffer); +// write("%h\n"); +// +// write("offset: \n"); +// write_hex(file_offset(fd)); +// +// write("size: \n"); +// write_hex(file_size(fd)); +// +// write("new offset: \n"); +// seek_file(fd, 0, SeekType::SET); +// write_hex(file_offset(fd)); + + // Write tests +// const char* message = "Heyyyy kernel"; +// write("writing: \n"); +// file_write(fd, (void*)message, strlen(message)); +// +// write("renaming: \n"); +// rename_file(fd, "b.txt"); - } + while (true) + yield(); } \ No newline at end of file diff --git a/programs/Example2/CMakeLists.txt b/programs/Example2/CMakeLists.txt index 8cb46b50..0ee4de51 100644 --- a/programs/Example2/CMakeLists.txt +++ b/programs/Example2/CMakeLists.txt @@ -10,7 +10,7 @@ ADD_EXECUTABLE(test1.elf ${PROJ_SRCS}) # Set the linker info SET(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/programs/Example/src/linker.ld) SET_TARGET_PROPERTIES(test1.elf PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) -TARGET_LINK_LIBRARIES(test1.elf gcc) +TARGET_LINK_LIBRARIES(test1.elf gcc syscore_static) TARGET_LINK_OPTIONS(test1.elf PRIVATE -T ${LINKER_SCRIPT} -nostdlib -n) # Install the executable diff --git a/programs/Example2/src/main.cpp b/programs/Example2/src/main.cpp index f4b14ec3..82257ebb 100644 --- a/programs/Example2/src/main.cpp +++ b/programs/Example2/src/main.cpp @@ -3,6 +3,13 @@ // #include #include +#include +#include +#include + +using namespace syscore; +using namespace syscore::ipc; +using namespace syscore::filesystem; // Write using a syscall (int 0x80 with syscall 0x01 for write) void write(const char* data, uint64_t length = 0) @@ -34,54 +41,6 @@ void write_hex(uint64_t data) write(buffer); } -// Create a shared memory block (int 0x80 with syscall 0x03, taking a string as the name) -void* open_shared_memory(char* name) -{ - void* result = nullptr; - - while (result == nullptr) - asm volatile("int $0x80" : "=a" (result) : "a" (0x03), "b" (name)); - return result; -} - -void setstring(char* str, const char* message) -{ - while(*message != '\0') - *str++ = *message++; - *str = '\0'; -} - -typedef struct TestSharedMemoryBlock -{ - char message[100]; - -} TestSharedMemoryBlock; - -typedef struct SharedMessage{ - void* message_buffer; - size_t message_size; - uintptr_t next_message; -} ipc_message_t; - -void send_message(const char* endpoint, void* message, size_t size) -{ - asm volatile( - "mov %[endpoint], %%rdi\n\t" // arg0: endpoint - "mov %[message], %%rsi\n\t" // arg1: message - "mov %[size], %%rdx\n\t" // arg2: size - "mov $0, %%r10\n\t" // arg3: if not used, set to 0 - "mov $0, %%r8\n\t" // arg4: if not used, set to 0 - "mov $0, %%r9\n\t" // arg5: if not used, set to 0 - "mov $0x07, %%rax\n\t" // syscall number for send_message - "int $0x80\n\t" - : - : [endpoint] "r"(endpoint), - [message] "r"(message), - [size] "r"(size) - : "rax", "rdi", "rsi", "rdx", "r10", "r8", "r9" - ); -} - void close() { // syscall 0, arg0 = pid (0 for current process), arg1 = exit code @@ -96,7 +55,6 @@ void close() ); } - void wait(uint64_t ms) { // syscall 0x010, arg0 = milliseconds @@ -112,33 +70,38 @@ void wait(uint64_t ms) extern "C" int _start(int argc, char** argv) { + // Write to the console + write("MaxOS Test Program v3\n"); - // Print the args - write("Args:\n"); - for(int i = 0; i < argc; i++) - { - - // Write the arg - write(argv[i]); - } - write("%h\n"); - - // Write to the console - write("MaxOS Test Program v3.1\n"); - - const char* message = "Message from process 2\n"; - const char* endpoint = "TestQueue"; + uint64_t dd = open_directory("/test/abc/"); + write("dd: \n"); + write_hex(dd); - // Wait 4seconds - wait(4000); - write("Waited 4 seconds\n"); - - // Send a message via IPC - send_message(endpoint, (void*)message, 24); + // Read Tests +// write("size: \n"); +// write_hex(directory_entries_size(dd)); +// +// uint8_t buffer[163]; +// directory_entries(dd, buffer, 163); +// size_t offset = 0; +// while (offset < 163) +// { +// auto* entry = (entry_information_t*)(buffer + offset); +// write(entry->name); +// write("%h\n"); +// offset += entry->entry_length; +// } + + // Write Tests +// new_file(dd, "c.txt"); +// new_directory(dd, "sub"); +// +// remove_file(dd, "c.txt"); +// remove_directory(dd, "sub"); +// rename_directory(dd, "def"); - // Close the process - write("Closing process\n"); - close(); + while (true) + thread_yield(); } \ No newline at end of file diff --git a/toolchain/MaxOS.sh b/toolchain/MaxOS.sh index 1ac98412..73611058 100755 --- a/toolchain/MaxOS.sh +++ b/toolchain/MaxOS.sh @@ -40,6 +40,9 @@ STAT_EXC=stat MOUNT_DIR="/mnt" +# Filesystem type: "FAT" or "EXT2" +FILESYSTEM_TYPE="EXT2" + # Set a variable if this is MacOS IS_MACOS=0 if [[ ($(uname) == "Darwin") ]]; then diff --git a/toolchain/copy_filesystem.sh b/toolchain/copy_filesystem.sh index ab477aee..15b6e20d 100755 --- a/toolchain/copy_filesystem.sh +++ b/toolchain/copy_filesystem.sh @@ -1,9 +1,29 @@ #!/bin/bash +#TODO: rsync Mac, unmount/remount mac + SCRIPTDIR=$(dirname "$BASH_SOURCE") source $SCRIPTDIR/MaxOS.sh -msg "Copying boot files to image" +# Parse the args +REVERSE=0 +while [ "$#" -gt "0" ]; do + case "$1" in + --reverse) + REVERSE=1 + shift 1 + ;; + *) + warn "Error: Unknown argument $1" + ;; + esac +done + +if [ "$REVERSE" -ne 1 ]; then + msg "Copying boot files to image" +else + msg "Pulling changes made during run" +fi # Bootscript maps 8MB of kernel memory so ensure that the elf file is less than 8MB KERNEL_SIZE=$($STAT_EXC -c %s "$SCRIPTDIR/../filesystem/boot/MaxOSk64") @@ -13,27 +33,50 @@ fi DESTINATION="$MOUNT_DIR/MaxOS_img_1" -# MacOS uses an ISO for now + +: "${USE_ISO:=0}" +# Produce an ISO? default to no if [ "$USE_ISO" -eq 1 ]; then + + # Cant pull changes from the iso + if [ "$REVERSE" -ne 1 ]; then + exit 0 + fi + DESTINATION="$SCRIPTDIR/../iso" if [ ! -d "$DESTINATION" ]; then mkdir -p "$DESTINATION" fi fi -#Copy filesystem -msg "Copying filesystem to image" -sudo rm -rf "$DESTINATION/boot" && sudo cp -r $SCRIPTDIR/../filesystem/boot $DESTINATION -sudo rm -rf "$DESTINATION/os" && sudo cp -r $SCRIPTDIR/../filesystem/os $DESTINATION -sudo rm -rf "$DESTINATION/user" && sudo cp -r $SCRIPTDIR/../filesystem/user $DESTINATION +# Linux: setup loop device to write to +dev="" +IMAGE="../MaxOS.img" +if [ "$IS_MACOS" -ne 1 ]; then + msg "Mounting img" + dev=$(sudo losetup --find --show --partscan "$IMAGE") + sudo mount "$dev"p1 "$MOUNT_DIR/MaxOS_img_1" + sudo mount "$dev"p2 "$MOUNT_DIR/MaxOS_img_2" +fi -# Sync filesystem -msg "Syncing filesystem" -sudo sync +# Syncing local filesystem +if [ "$REVERSE" -ne 1 ]; then + msg "Copying filesystem to image" + sudo rsync --no-o --no-g -a --delete -c "$SCRIPTDIR/../filesystem/" "$MOUNT_DIR/MaxOS_img_1/" +else + msg "Copying changes on image to local filesystem" + sudo rsync --itemize-changes --chown=$(id -un):$(id -gn) -a --delete -c "$MOUNT_DIR/MaxOS_img_1/" "$SCRIPTDIR/../filesystem/" +fi # Create the iso if [ "$USE_ISO" -eq 1 ]; then msg "Creating ISO" i686-elf-grub-mkrescue --modules="part_msdos fat normal" --output="$SCRIPTDIR/../MaxOS.iso" $DESTINATION || fail "Failed to create rescue ISO" sudo rm -rf $DESTINATION +fi + +# Linux: clean up the loop device +if [ "$IS_MACOS" -ne 1 ]; then + sudo umount "$dev"p1 "$dev"p2 + sudo losetup -d "$dev" fi \ No newline at end of file diff --git a/toolchain/create_disk_img.sh b/toolchain/create_disk_img.sh index f18400c2..5f1308f3 100755 --- a/toolchain/create_disk_img.sh +++ b/toolchain/create_disk_img.sh @@ -3,19 +3,22 @@ SCRIPTDIR=$(dirname "$BASH_SOURCE") source $SCRIPTDIR/MaxOS.sh -#TODO: Scalibitlity for partition size and amount of partitions +# TODO: Scalability for partition size and amount of partitions +# TODO: Better loop device handling + +IMAGE="../MaxOS.img" # If the disk image already exists no need to setup -if [ -f ../MaxOS.img ]; then +if [ -f "$IMAGE" ]; then msg "Image already exists" exit 0 fi # Remove the disk on failure TODO: Also unmount the image -ON_FAIL="rm -f ../MaxOS.img" +ON_FAIL="rm -f $IMAGE" #Create a 2GB image -qemu-img create ../MaxOS.img 2G || fail "Could not create image" +qemu-img create "$IMAGE" 2G || fail "Could not create image" #Partion & Mount the image dev="" @@ -25,23 +28,35 @@ if [ "$IS_MACOS" -eq 1 ]; then dev=${dev_arr[0]} sudo diskutil partitionDisk $dev MBRFormat "MS-DOS FAT32" "BOOT" 1G "MS-DOS FAT32" "DATA" R else - fdisk ../MaxOS.img -u=cylinders << EOF + + if [ "$FILESYSTEM_TYPE" = "FAT" ]; then + TYPE_CODE="b" # FAT32 + else + TYPE_CODE="83" # EXT2 + fi + + fdisk "$IMAGE" -u=cylinders << EOF o n p 1 1 130 + t + $TYPE_CODE + a + 1 n p 2 131 243 - a - 1 + t + 2 + $TYPE_CODE w EOF - + dev=$(sudo losetup --find --show --partscan "$IMAGE") fi #Try and unmount the old mount points @@ -55,8 +70,7 @@ fi # Get the partions part1="" part2="" -if [ -f ../MaxOS.img ]; then - msg "MacOS: Image mounted already" +if [ "$IS_MACOS" -eq 1 ]; then part1="${dev}s1" part2="${dev}s2" sudo diskutil unmount /Volumes/BOOT @@ -64,32 +78,51 @@ if [ -f ../MaxOS.img ]; then else msg "Attaching image to loop device" - sudo losetup -D - sudo losetup --partscan /dev/loop0 ../MaxOS.img || fail "Could not mount image to loop device" + part1="${dev}p1" + part2="${dev}p2" fi +msg "${part1}" +msg "${part2}" ## IMAGE 1 msg "Creating filesystem for partition 1" sudo mkdir -p "$MOUNT_DIR/MaxOS_img_1" || fail "Could not create mount point" -if [ "$IS_MACOS" -eq 1 ]; then - sudo diskutil unmount "$part1" || warn "Couldn't unmount $part1 before formatting" - sudo mount -t msdos "$part1" "$MOUNT_DIR/MaxOS_img_1" || fail "Could not mount partition 1" +if [ "$FILESYSTEM_TYPE" = "FAT" ]; then + if [ "$IS_MACOS" -eq 1 ]; then + sudo diskutil unmount "$part1" || warn "Couldn't unmount $part1 before formatting" + sudo mount -t msdos "$part1" "$MOUNT_DIR/MaxOS_img_1" || fail "Could not mount partition 1" + else + sudo mkfs.vfat -F 32 "$part1" || fail "Could not create FAT32 filesystem" + sudo mount "$part1" "$MOUNT_DIR/MaxOS_img_1" || fail "Could not mount image to mount point" + fi else - sudo mkfs.vfat -F 32 /dev/loop0p1 || fail "Could not create filesystem" - sudo mount -o loop /dev/loop0p1 "$MOUNT_DIR/MaxOS_img_1" || fail "Could not mount image to mount point" + if [ "$IS_MACOS" -eq 1 ]; then + fail "EXT2 is not supported on macOS by default" + else + sudo mkfs.ext2 "$part1" || fail "Could not create EXT2 filesystem" + sudo mount "$part1" "$MOUNT_DIR/MaxOS_img_1" || fail "Could not mount image to mount point" + fi fi - ## IMAGE 2 msg "Creating filesystem for partition 2" -sudo mkdir -p "$MOUNT_DIR/MaxOS_img_2" || fail "Could not create mount point" -if [ "$IS_MACOS" -eq 1 ]; then - sudo diskutil unmount "$part2" || warn "Couldn't unmount $part2 before formatting" - sudo mount -t msdos "$part2" "$MOUNT_DIR/MaxOS_img_2" || fail "Could not mount partition 2" +sudo mkdir -p "$MOUNT_DIR/MaxOS_img_2" || fail "Could not create mount point" + +if [ "$FILESYSTEM_TYPE" = "FAT" ]; then + if [ "$IS_MACOS" -eq 1 ]; then + sudo diskutil unmount "$part2" || warn "Couldn't unmount $part2 before formatting" + sudo mount -t msdos "$part2" "$MOUNT_DIR/MaxOS_img_2" || fail "Could not mount partition 2" + else + sudo mkfs.vfat -F 32 "$part2" || fail "Could not create FAT32 filesystem" + sudo mount "$part2" "$MOUNT_DIR/MaxOS_img_2" || fail "Could not mount image to mount point" + fi else - sudo diskutil unmount "$part2" || warn "Couldn't unmount $part2 before formatting" - sudo mkfs.vfat -F 32 /dev/loop0p2 || fail "Could not create filesystem" - sudo mount -o loop /dev/loop0p2 "$MOUNT_DIR/MaxOS_img_2" || fail "Could not mount image to mount point" + if [ "$IS_MACOS" -eq 1 ]; then + fail "EXT2 is not supported on macOS by default" + else + sudo mkfs.ext2 "$part2" || fail "Could not create EXT2 filesystem" + sudo mount "$part2" "$MOUNT_DIR/MaxOS_img_2" || fail "Could not mount image to mount point" + fi fi # Sync @@ -97,8 +130,17 @@ msg "Syncing filesystem" sync sudo sync +# Define grub modules +GRUB_MODULES="normal part_msdos biosdisk echo multiboot2" +if [ "$FILESYSTEM_TYPE" = "FAT" ]; then + GRUB_MODULES="$GRUB_MODULES fat" +fi + +if [ "$FILESYSTEM_TYPE" = "EXT2" ]; then + GRUB_MODULES="$GRUB_MODULES ext2" +fi + #Install grub to the image -GRUB_MODULES="normal part_msdos fat biosdisk echo multiboot2" if [ "$IS_MACOS" -eq 1 ]; then msg "Installing GRUB manually on macOS" @@ -124,5 +166,8 @@ if [ "$IS_MACOS" -eq 1 ]; then sync sudo sync else + msg "Installing GRUB to disk image: $dev" sudo grub-install --root-directory="$MOUNT_DIR/MaxOS_img_1" --no-floppy --modules="$GRUB_MODULES" "$dev" || fail "Could not install grub" + sudo umount "${part1}" "${part2}" + sudo losetup -d "$dev" fi \ No newline at end of file diff --git a/toolchain/fix_scripts_github.sh b/toolchain/fix_scripts_github.sh index 90df4e3b..4ac90b64 100755 --- a/toolchain/fix_scripts_github.sh +++ b/toolchain/fix_scripts_github.sh @@ -1 +1,2 @@ dos2unix * +dos2unix pre_process/* diff --git a/toolchain/make_cross_compiler.sh b/toolchain/make_cross_compiler.sh index 7f318466..1252c22f 100755 --- a/toolchain/make_cross_compiler.sh +++ b/toolchain/make_cross_compiler.sh @@ -35,6 +35,7 @@ if [ "$1" != "--no-deps" ]; then cmake \ nasm \ telnet \ + rsync \ || fail "Couldn't install dependencies" fi @@ -170,6 +171,6 @@ cd ../ ls # Setup the first version of the kernel -cd post_process +cd pre_process ./version.sh --force cd ../ \ No newline at end of file diff --git a/toolchain/post_process/checks.sh b/toolchain/pre_process/checks.sh similarity index 100% rename from toolchain/post_process/checks.sh rename to toolchain/pre_process/checks.sh diff --git a/toolchain/post_process/progress.sh b/toolchain/pre_process/progress.sh similarity index 59% rename from toolchain/post_process/progress.sh rename to toolchain/pre_process/progress.sh index b25de2f4..ecf60d18 100755 --- a/toolchain/post_process/progress.sh +++ b/toolchain/pre_process/progress.sh @@ -8,4 +8,6 @@ TOTAL=$(grep -r "Logger::INFO" "${SCRIPTDIR}/../../kernel/src/" | wc -l) msg "Total INFO: ${TOTAL}" # Store in the source file -$SED_EXC -i "s/static const uint8_t s_progress_total { 100 };/static const uint8_t s_progress_total { ${TOTAL} };/" "${SCRIPTDIR}/../../kernel/include/common/logger.h" \ No newline at end of file +$SED_EXC -i \ + -E "s/(s_progress_total[[:space:]]*=[[:space:]]*)[0-9]+;/\1${TOTAL};/" \ + "${SCRIPTDIR}/../../kernel/src/common/logger.cpp" \ No newline at end of file diff --git a/toolchain/post_process/run.sh b/toolchain/pre_process/run.sh similarity index 100% rename from toolchain/post_process/run.sh rename to toolchain/pre_process/run.sh diff --git a/toolchain/post_process/version.sh b/toolchain/pre_process/version.sh similarity index 100% rename from toolchain/post_process/version.sh rename to toolchain/pre_process/version.sh diff --git a/toolchain/run_gdb.sh b/toolchain/run_gdb.sh index 15b53334..6f2367b6 100755 --- a/toolchain/run_gdb.sh +++ b/toolchain/run_gdb.sh @@ -2,50 +2,26 @@ SCRIPTDIR=$(dirname "$BASH_SOURCE") source $SCRIPTDIR/MaxOS.sh -IP=localhost -IN_WSL=0 -if command -v wslpath >/dev/null; then - msg "WSL detected." +msg "QEMU GDB Runner Started" - # Get the ip from ipconfig.exe - LOCAL_IP=${LOCAL_IP:-`ipconfig.exe | grep -im1 'IPv4 Address' | cut -d ':' -f2`} +while true; do - echo "WSL IP is: ${LOCAL_IP}" - IP=${LOCAL_IP} + msg "Waiting for GDB to start" + while true; do + GDB_PID=$(pgrep -fx '/usr/bin/gdb.*') + if [[ -n "$GDB_PID" ]]; then + msg "GDB Started with PID $GDB_PID" + tmux send-keys -t 0 "make install gdb" C-m + break + fi + sleep 0.2 + done - # Strip the carriage return - IP=${IP%$'\r'} -fi + msg "Waiting for debug session to end" + while kill -0 "$GDB_PID" 2>/dev/null; do + sleep 0.2 + done + taskkill.exe /IM "qemu-system-x86_64.exe" /F +done -# Make the GDB .init file -cat > ~/.gdbinit <