diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/include/mm/paging.h | 47 | ||||
| -rw-r--r-- | src/mm/paging.asm | 25 | ||||
| -rw-r--r-- | src/mm/paging.c | 127 |
3 files changed, 199 insertions, 0 deletions
diff --git a/src/include/mm/paging.h b/src/include/mm/paging.h new file mode 100644 index 0000000..7ff9e7a --- /dev/null +++ b/src/include/mm/paging.h @@ -0,0 +1,47 @@ +#ifndef PAGING_H +#define PAGING_H + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +/* + * Virtual address bits meaning + * + * [ 31-22 | 21-12 | 11-00 ] + * [ Page directory Idx | Page table Idx | Offset within page ] + * + * + */ +#define PAGING_DIRECTORY_BITS (22) +#define PAGING_TABLE_BITS (12) +#define PAGING_TABLE_MASK (0x3ff) +#define PAGING_PAGE_OFFSET_MASK (0xfff) + +/* Page directory control bits */ +#define PAGING_CACHE_DISABLED (1 << 4) +#define PAGING_WRITE_THROUGH (1 << 3) +#define PAGING_USER_ACCESS (1 << 2) +#define PAGING_IS_WRITABLE (1 << 1) +#define PAGING_IS_PRESENT (1 << 0) + +#define PAGING_ENTRIES_PER_TABLE 1024 + +struct page_directory +{ + uint32_t *directory; +}; + +struct page_directory * paging_new_directory(uint8_t flags); +uint32_t * paging_get_directory(struct page_directory *pd); +void paging_switch(struct page_directory *directory); + +void enable_paging(); + +bool paging_is_aligned(void *addr); +int paging_map_vaddr(struct page_directory *directory, + void *vaddr, + void *paddr, + uint32_t val); + +#endif /* PAGING_H */ diff --git a/src/mm/paging.asm b/src/mm/paging.asm new file mode 100644 index 0000000..7f79489 --- /dev/null +++ b/src/mm/paging.asm @@ -0,0 +1,25 @@ +[BITS 32] + +section .asm + + global paging_load_directory + global enable_paging + + paging_load_directory: + push ebp + mov ebp, esp + mov eax, [ebp + 8] + + mov cr3, eax + + pop ebp + ret + + enable_paging: + push ebp + mov ebp, esp + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + pop ebp + ret diff --git a/src/mm/paging.c b/src/mm/paging.c new file mode 100644 index 0000000..04a353c --- /dev/null +++ b/src/mm/paging.c @@ -0,0 +1,127 @@ +#include <mm/paging.h> +#include <mm/kernel_heap.h> +#include <toxic/errno.h> +#include <toxic/config.h> +#include <toxic/vga.h> + +void paging_load_directory(uint32_t *directory); + +static struct page_directory *current_directory; + +struct page_directory* +paging_new_directory(uint8_t flags) +{ + int i, j; + int offset = 0; + + /* Allocate the global directory */ + struct page_directory *new_directory = kzalloc(sizeof(struct page_directory)); + uint32_t *directory = kzalloc(sizeof(uint32_t) * PAGING_ENTRIES_PER_TABLE); + + /* Allocate a new page table for each page directory entry */ + for (i = 0; i < PAGING_ENTRIES_PER_TABLE; i++) { + uint32_t *page_table = kzalloc(sizeof(uint32_t) * PAGING_ENTRIES_PER_TABLE); + + /* + * Each PTE is initialized now with the linear physical address. + * I don't think this is a good idea, but I'll change it later. + * + * In theory we should not allocate memory for PTEs not used, so + * this is in general a waste of space. A page table should be allocated only + * when we actually need to allocate space there. + * + * Also, PTEs should point to NULL instead of defaulting to a + * physical address. + */ + for (j = 0; j < PAGING_ENTRIES_PER_TABLE; j++) + page_table[j] = (offset + (j * PAGE_SIZE)) | flags; + + offset += (PAGING_ENTRIES_PER_TABLE * PAGE_SIZE); + + /* + * Set the whole directory as WRITABLE so we don't + * prevent any page to be writable + */ + directory[i] = (uint32_t)page_table | flags | PAGING_IS_WRITABLE; + } + + new_directory->directory = directory; + return new_directory; +} + +bool +paging_is_aligned(void *addr) +{ + return ((uint32_t)addr % PAGE_SIZE) == 0; +} + +uint32_t * +paging_get_directory(struct page_directory *pd) +{ + return pd->directory; +} + +void +paging_switch(struct page_directory *directory) +{ + paging_load_directory(paging_get_directory(directory)); + current_directory = directory; +} + +int +paging_get_indexes( + void *vaddr, + uint32_t *dir_idx, + uint32_t *table_idx) +{ + int ret = 0; + + if (!paging_is_aligned(vaddr)) { + ret = -EINVAL; + goto out; + } + + *dir_idx = (uint32_t)vaddr >> PAGING_DIRECTORY_BITS; + *table_idx = ((uint32_t)vaddr >> PAGING_TABLE_BITS) & PAGING_TABLE_MASK; + +out: + return ret; +} + +/* + * Configure a PTE to map virtual address vaddr to physical address paddr + * setting the entry bits to flags + */ +int +paging_map_vaddr( + struct page_directory *page_dir, + void *vaddr, + void *paddr, + uint32_t flags) +{ + uint32_t *directory; + uint32_t *page_table; + uint32_t dir_idx = 0; + uint32_t table_idx; + uint32_t dir_entry; + uint32_t pte = (uint32_t)paddr; + int ret = 0; + + if (!paging_is_aligned(vaddr) || !paging_is_aligned(paddr)) { + return -EINVAL; + } + + ret = paging_get_indexes(vaddr, &dir_idx, &table_idx); + if (ret < 0) + return ret; + + directory = paging_get_directory(page_dir); + dir_entry = (uint32_t)directory[dir_idx]; + + page_table = (uint32_t *)(dir_entry & (~PAGING_PAGE_OFFSET_MASK)); + page_table[table_idx] = pte | flags; + + return ret; +} + + |
