/// @file paging.h /// @brief Implementation of a memory paging management. /// @copyright (c) 2014-2022 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "mem/zone_allocator.h" #include "proc_access.h" #include "kernel.h" #include "stddef.h" #include "boot.h" /// Size of a page. #define PAGE_SIZE 4096U /// The start of the process area. #define PROCAREA_START_ADDR 0x00000000 /// The end of the process area (and start of the kernel area). #define PROCAREA_END_ADDR 0xC0000000 /// @brief An entry of a page directory. typedef struct page_dir_entry_t { unsigned int present : 1; ///< TODO: Comment. unsigned int rw : 1; ///< TODO: Comment. unsigned int user : 1; ///< TODO: Comment. unsigned int w_through : 1; ///< TODO: Comment. unsigned int cache : 1; ///< TODO: Comment. unsigned int accessed : 1; ///< TODO: Comment. unsigned int reserved : 1; ///< TODO: Comment. unsigned int page_size : 1; ///< TODO: Comment. unsigned int global : 1; ///< TODO: Comment. unsigned int available : 3; ///< TODO: Comment. unsigned int frame : 20; ///< TODO: Comment. } page_dir_entry_t; /// @brief An entry of a page table. typedef struct page_table_entry_t { unsigned int present : 1; ///< TODO: Comment. unsigned int rw : 1; ///< TODO: Comment. unsigned int user : 1; ///< TODO: Comment. unsigned int w_through : 1; ///< TODO: Comment. unsigned int cache : 1; ///< TODO: Comment. unsigned int accessed : 1; ///< TODO: Comment. unsigned int dirty : 1; ///< TODO: Comment. unsigned int zero : 1; ///< TODO: Comment. unsigned int global : 1; ///< TODO: Comment. unsigned int kernel_cow : 1; ///< TODO: Comment. unsigned int available : 2; ///< TODO: Comment. unsigned int frame : 20; ///< TODO: Comment. } page_table_entry_t; /// @brief Flags associated with virtual memory areas. enum MEMMAP_FLAGS { MM_USER = 0x1, ///< Area belongs to user. MM_GLOBAL = 0x2, ///< Area is global. MM_RW = 0x4, ///< Area has user read/write perm. MM_PRESENT = 0x8, ///< Area is valid. // Kernel flags MM_COW = 0x10, ///< Area is copy on write. MM_UPDADDR = 0x20, ///< Check? }; /// @brief A page table. /// @details /// It contains 1024 entries which can be addressed by 10 bits (log_2(1024)). typedef struct page_table_t { page_table_entry_t pages[1024]; ///< Array of pages. } __attribute__((aligned(PAGE_SIZE))) page_table_t; /// @brief A page directory. /// @details In the two-level paging, this is the first level. typedef struct page_directory_t { /// We need a table that contains virtual address, so that we can /// actually get to the tables (size: 1024 * 4 = 4096 byte). page_dir_entry_t entries[1024]; } __attribute__((aligned(PAGE_SIZE))) page_directory_t; /// @brief Virtual Memory Area, used to store details of a process segment. typedef struct vm_area_struct_t { /// Memory descriptor associated. struct mm_struct_t *vm_mm; /// Start address of the segment, inclusive. uint32_t vm_start; /// End address of the segment, exclusive. uint32_t vm_end; /// List of memory areas. list_head vm_list; /// Permissions. pgprot_t vm_page_prot; /// Flags. unsigned short vm_flags; /// rbtree node. // struct rb_node vm_rb; } vm_area_struct_t; /// @brief Memory Descriptor, used to store details about the memory of a user process. typedef struct mm_struct_t { /// List of memory area (vm_area_struct reference). list_head mmap_list; // /// rbtree of memory area. // struct rb_root mm_rb; /// Last memory area used. vm_area_struct_t *mmap_cache; /// Process page directory. page_directory_t *pgd; /// Number of memory area. int map_count; /// List of mm_struct. list_head mm_list; /// CODE start. uint32_t start_code; /// CODE end. uint32_t end_code; /// DATA start. uint32_t start_data; /// DATA end. uint32_t end_data; /// HEAP start. uint32_t start_brk; /// HEAP end. uint32_t brk; /// STACK start. uint32_t start_stack; /// ARGS start. uint32_t arg_start; /// ARGS end. uint32_t arg_end; /// ENVIRONMENT start. uint32_t env_start; /// ENVIRONMENT end. uint32_t env_end; /// Number of mapped pages. unsigned int total_vm; } mm_struct_t; /// @brief Cache used to store page tables. extern kmem_cache_t *pgtbl_cache; /// @brief Initializes paging /// @param info Information coming from bootloader. void paging_init(boot_info_t *info); /// @brief Provide access to the main page directory. /// @return A pointer to the main page directory. page_directory_t *paging_get_main_directory(); /// @brief Provide access to the current paging directory. /// @return A pointer to the current page directory. static inline page_directory_t *paging_get_current_directory() { return (page_directory_t *)get_cr3(); } /// @brief Switches paging directory, the pointer must be a physical address. /// @param dir A pointer to the new page directory. static inline void paging_switch_directory(page_directory_t *dir) { set_cr3((uintptr_t)dir); } /// @brief Switches paging directory, the pointer can be a lowmem address. /// @param dir A pointer to the new page directory. void paging_switch_directory_va(page_directory_t *dir); /// @brief Invalidate a single tlb page (the one that maps the specified virtual address) /// @param addr The address of the page table. void paging_flush_tlb_single(unsigned long addr); /// @brief Enables paging. static inline void paging_enable() { // Clear the PSE bit from cr4. set_cr4(bitmask_clear(get_cr4(), CR4_PSE)); // Set the PG bit in cr0. set_cr0(bitmask_set(get_cr0(), CR0_PG)); } /// @brief Returns if paging is enabled. /// @return 1 if paging is enables, 0 otherwise. static inline int paging_is_enabled() { return bitmask_check(get_cr0(), CR0_PG); } /// @brief Handles a page fault. /// @param f The interrupt stack frame. void page_fault_handler(pt_regs *f); /// @brief Gets a page from a virtual address /// @param pgdir The target page directory. /// @param virt_start The virtual address to query /// @param size A pointer to the requested size of the data, size is updated if physical memory is not contiguous /// @return Pointer to the page. page_t *mem_virtual_to_page(page_directory_t *pgdir, uint32_t virt_start, size_t *size); /// @brief Creates a virtual to physical mapping, incrementing pages usage counters. /// @param pgd The target page directory. /// @param virt_start The virtual address to map to. /// @param phy_start The physical address to map. /// @param size The size of the segment. /// @param flags The flags for the memory range. void mem_upd_vm_area(page_directory_t *pgd, uint32_t virt_start, uint32_t phy_start, size_t size, uint32_t flags); /// @brief Clones a range of pages between two distinct page tables /// @param src_pgd The source page directory. /// @param dst_pgd The dest page directory. /// @param src_start The source virtual address for the clone. /// @param dst_start The destination virtual address for the clone. /// @param size The size of the segment. /// @param flags The flags for the new dst memory range. void mem_clone_vm_area(page_directory_t *src_pgd, page_directory_t *dst_pgd, uint32_t src_start, uint32_t dst_start, size_t size, uint32_t flags); /// @brief Create a virtual memory area. /// @param mm The memory descriptor which will contain the new segment. /// @param virt_start The virtual address to map to. /// @param size The size of the segment. /// @param pgflags The flags for the new memory area. /// @param gfpflags The Get Free Pages flags. /// @return The virtual address of the starting point of the segment. uint32_t create_vm_area(mm_struct_t *mm, uint32_t virt_start, size_t size, uint32_t pgflags, uint32_t gfpflags); /// @brief Clone a virtual memory area, using copy on write if specified /// @param mm The memory descriptor which will contain the new segment. /// @param area The area to clone /// @param cow Whether to use copy-on-write or just copy everything. /// @param gfpflags The Get Free Pages flags. /// @return Zero on success. uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags); /// @brief Creates the main memory descriptor. /// @param stack_size The size of the stack in byte. /// @return The Memory Descriptor created. mm_struct_t *create_blank_process_image(size_t stack_size); /// @brief Create a Memory Descriptor. /// @param mmp The memory map to clone /// @return The Memory Descriptor created. mm_struct_t *clone_process_image(mm_struct_t *mmp); /// @brief Free Memory Descriptor with all the memory segment contained. /// @param mm The Memory Descriptor to free. void destroy_process_image(mm_struct_t *mm);