Skip to content

Commit 82e42ad

Browse files
committed
feat: add paging support
1 parent e572209 commit 82e42ad

File tree

6 files changed

+255
-0
lines changed

6 files changed

+255
-0
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Zeronix - A Minimal Kernel
3+
* Copyright (C) 2024-present Viktor Popp and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
19+
#pragma once
20+
#include "limine/limine.h"
21+
#include <stdint.h>
22+
23+
#define PMLE_PRESENT 1
24+
#define PMLE_WRITE (1 << 1)
25+
#define PMLE_NOT_EXECUTABLE (1ull << 63)
26+
27+
extern uint64_t get_pml4();
28+
extern uint64_t load_pml4(uint64_t *pml4);
29+
30+
#define PMLT_MASK 0x1ff
31+
32+
#define PML4_INDEX(a) ((a >> 39) & PMLT_MASK)
33+
#define PML3_INDEX(a) ((a >> 30) & PMLT_MASK)
34+
#define PML2_INDEX(a) ((a >> 21) & PMLT_MASK)
35+
#define PML1_INDEX(a) ((a >> 12) & PMLT_MASK)
36+
37+
#define PAGE_ADDR_MASK 0xFFFFFFFFFFFFF000ULL
38+
#define PAGE_GET_ADDR(a) ((a) & PAGE_ADDR_MASK)
39+
40+
void paging_init(volatile struct limine_memmap_request *memmap_request);
41+
42+
void paging_map_region(uint64_t *pml4, uint64_t phys_start, uint64_t virt_start, uint64_t len,
43+
uint64_t flags);
44+
void paging_unmap_region(uint64_t *pml4, uint64_t virt_start, uint64_t len);

kernel/src/arch/x86/paging.asm

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; Zeronix - A Minimal Kernel
2+
; Copyright (C) 2024-present Viktor Popp and contributors
3+
;
4+
; This program is free software: you can redistribute it and/or modify
5+
; it under the terms of the GNU General Public License as published by
6+
; the Free Software Foundation, either version 3 of the License, or
7+
; (at your option) any later version.
8+
;
9+
; This program is distributed in the hope that it will be useful,
10+
; but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
; GNU General Public License for more details.
13+
;
14+
; You should have received a copy of the GNU General Public License
15+
; along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
global get_pml4
18+
get_pml4:
19+
mov rax, cr3
20+
ret
21+
22+
global load_pml4
23+
load_pml4:
24+
mov cr3, rdi
25+
ret

kernel/src/arch/x86/paging.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Zeronix - A Minimal Kernel
3+
* Copyright (C) 2024-present Viktor Popp and contributors
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
*/
18+
#include "arch/paging.h"
19+
#include "core/panic.h"
20+
#include "lib/align.h"
21+
#include "limine/limine.h"
22+
#include "mem/mmutil.h"
23+
#include "mem/pmm.h"
24+
#include "util/log.h"
25+
#include <stdint.h>
26+
#include <string.h>
27+
28+
uint64_t *global_kernel_pml4;
29+
30+
void _unmap(uint64_t *pml4, uint64_t virt);
31+
void _map(uint64_t *pml4, uint64_t phys, uint64_t virt, uint64_t flags);
32+
uint64_t *_get_or_create_pmlt(uint64_t *pmlt, uint64_t pmlt_idx, uint64_t flags);
33+
uint64_t *_get_pmlt(uint64_t *pmlt, uint64_t pmlt_idx);
34+
35+
void paging_init(volatile struct limine_memmap_request *memmap_request)
36+
{
37+
uint64_t *kernel_pml4 = HIGHER_HALF(palloc(1));
38+
39+
log_trace("Limine's PML4 sits at: 0x%llx", get_pml4());
40+
log_trace("__kernel_start:\t 0x%p", &__kernel_start);
41+
log_trace("__kernel_text:\t 0x%p -> 0x%p", &__kernel_text_start, &__kernel_text_end);
42+
log_trace("__kernel_data:\t 0x%p -> 0x%p", &__kernel_data_start, &__kernel_data_end);
43+
log_trace("__kernel_rodata:\t 0x%p -> 0x%p", &__kernel_rodata_start, &__kernel_rodata_end);
44+
log_trace("__limine_requests: 0x%p -> 0x%p", &__limine_requests_start, &__limine_requests_end);
45+
log_trace("__kernel_end:\t 0x%p", &__kernel_end);
46+
47+
uint64_t text_len = (uint64_t)&__kernel_text_end - (uint64_t)&__kernel_text_start;
48+
paging_map_region(kernel_pml4, (uint64_t)&__kernel_text_start - VIRT_BASE + PHYS_BASE,
49+
(uint64_t)&__kernel_text_start, text_len, PMLE_PRESENT | PMLE_WRITE);
50+
51+
uint64_t rodata_len = (uint64_t)&__kernel_rodata_end - (uint64_t)&__kernel_rodata_start;
52+
paging_map_region(kernel_pml4, (uint64_t)&__kernel_rodata_start - VIRT_BASE + PHYS_BASE,
53+
(uint64_t)&__kernel_rodata_start, rodata_len,
54+
PMLE_PRESENT | PMLE_NOT_EXECUTABLE);
55+
56+
// map the .data section and everything else
57+
uint64_t data_len = (uint64_t)&__kernel_data_end - (uint64_t)&__kernel_data_start;
58+
uint64_t other_len = (uint64_t)&__kernel_end - (uint64_t)&__kernel_data_end;
59+
paging_map_region(kernel_pml4, (uint64_t)&__kernel_data_start - VIRT_BASE + PHYS_BASE,
60+
(uint64_t)&__kernel_data_start, data_len + other_len,
61+
PMLE_PRESENT | PMLE_WRITE | PMLE_NOT_EXECUTABLE);
62+
63+
uint64_t reqs_len = (uint64_t)&__limine_requests_end - (uint64_t)&__limine_requests_start;
64+
paging_map_region(kernel_pml4, (uint64_t)&__limine_requests_start - VIRT_BASE + PHYS_BASE,
65+
(uint64_t)&__limine_requests_start, reqs_len,
66+
PMLE_PRESENT | PMLE_WRITE | PMLE_NOT_EXECUTABLE);
67+
68+
// map the higher half
69+
for (uint64_t i = 0; i < memmap_request->response->entry_count; i++)
70+
{
71+
struct limine_memmap_entry *e = memmap_request->response->entries[i];
72+
paging_map_region(kernel_pml4, e->base, (uint64_t)HIGHER_HALF(e->base), e->length,
73+
PMLE_PRESENT | PMLE_WRITE);
74+
}
75+
76+
log_debug("Zinix PML4 sits at: 0x%llp", kernel_pml4);
77+
global_kernel_pml4 = (uint64_t *)PHYSICAL(kernel_pml4);
78+
load_pml4(global_kernel_pml4);
79+
log_info("Initialized Paging");
80+
}
81+
82+
void paging_map_region(uint64_t *pml4, uint64_t phys_start, uint64_t virt_start, uint64_t len,
83+
uint64_t flags)
84+
{
85+
uint64_t pages = ALIGN_UP(len, FRAME_SIZE) / FRAME_SIZE;
86+
for (uint64_t i = 0; i < pages; i++)
87+
{
88+
uint64_t phys = phys_start + (i * FRAME_SIZE);
89+
uint64_t virt = virt_start + (i * FRAME_SIZE);
90+
_map(pml4, phys, virt, flags);
91+
}
92+
}
93+
94+
void paging_unmap_region(uint64_t *pml4, uint64_t virt_start, uint64_t len)
95+
{
96+
uint64_t pages = ALIGN_UP(len, FRAME_SIZE) / FRAME_SIZE;
97+
for (uint64_t i = 0; i < pages; i++)
98+
{
99+
uint64_t virt = virt_start + (i * FRAME_SIZE);
100+
_unmap(pml4, virt);
101+
}
102+
}
103+
104+
void _map(uint64_t *pml4, uint64_t phys, uint64_t virt, uint64_t flags)
105+
{
106+
uint64_t pml4_idx = PML4_INDEX(virt);
107+
uint64_t pml3_idx = PML3_INDEX(virt);
108+
uint64_t pml2_idx = PML2_INDEX(virt);
109+
uint64_t pml1_idx = PML1_INDEX(virt);
110+
111+
uint64_t *pml3 = _get_or_create_pmlt(pml4, pml4_idx, flags);
112+
uint64_t *pml2 = _get_or_create_pmlt(pml3, pml3_idx, flags);
113+
uint64_t *pml1 = _get_or_create_pmlt(pml2, pml2_idx, flags);
114+
115+
// finally set the the phys addr in the pml1
116+
pml1[pml1_idx] = PAGE_GET_ADDR(phys) | flags;
117+
118+
// invalidate the virtual address
119+
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
120+
}
121+
122+
void _unmap(uint64_t *pml4, uint64_t virt)
123+
{
124+
uint64_t pml4_idx = PML4_INDEX(virt);
125+
uint64_t pml3_idx = PML3_INDEX(virt);
126+
uint64_t pml2_idx = PML2_INDEX(virt);
127+
uint64_t pml1_idx = PML1_INDEX(virt);
128+
129+
uint64_t *pml3 = _get_pmlt(pml4, pml4_idx);
130+
uint64_t *pml2 = _get_pmlt(pml3, pml3_idx);
131+
uint64_t *pml1 = _get_pmlt(pml2, pml2_idx);
132+
133+
pml1[pml1_idx] = 0;
134+
135+
// invalidate the virtual address
136+
__asm__ volatile("invlpg (%0)" : : "r"(virt) : "memory");
137+
}
138+
139+
uint64_t *_get_or_create_pmlt(uint64_t *pmlt, uint64_t pmlt_idx, uint64_t flags)
140+
{
141+
bool newly_created = false;
142+
143+
// if the pmlt and pmlt_idx hasn't isn't present allocate it
144+
if (!(pmlt[pmlt_idx] && PMLE_PRESENT))
145+
{
146+
pmlt[pmlt_idx] = (uint64_t)palloc(1) | flags;
147+
newly_created = true;
148+
}
149+
150+
// get the physical address of the next pmlt
151+
uint64_t page_addr = PAGE_GET_ADDR(pmlt[pmlt_idx]);
152+
153+
if (!page_addr)
154+
{
155+
panic(NULL, "failed to allocate space for new PMLT");
156+
}
157+
158+
// convert it to a virtual address
159+
uint64_t *table_vaddr = (uint64_t *)HIGHER_HALF(page_addr);
160+
161+
// if the next pmlt is newly created zero it
162+
if (newly_created)
163+
{
164+
memset(table_vaddr, 0x00, FRAME_SIZE);
165+
}
166+
167+
return table_vaddr;
168+
}
169+
170+
uint64_t *_get_pmlt(uint64_t *pmlt, uint64_t pmlt_idx)
171+
{
172+
uint64_t page_addr = PAGE_GET_ADDR(pmlt[pmlt_idx]);
173+
174+
if (!page_addr)
175+
{
176+
panic(NULL, "failed to get page address at index %llx in the PMLT at %llx", pmlt_idx, pmlt);
177+
}
178+
179+
return (uint64_t *)HIGHER_HALF(page_addr);
180+
}

kernel/src/boot/limine.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ __attribute__((used, section(".limine_requests"))) volatile struct limine_memmap
3737
__attribute__((
3838
used, section(".limine_requests"))) volatile struct limine_hhdm_request limine_hhdm_request = {
3939
.id = LIMINE_HHDM_REQUEST_ID, .revision = 0};
40+
41+
__attribute__((used, section(".limine_requests"))) volatile struct limine_executable_address_request
42+
limine_address_request = {.id = LIMINE_EXECUTABLE_ADDRESS_REQUEST_ID, .revision = 0};

kernel/src/boot/limine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ extern volatile uint64_t limine_base_revision[3];
2525
extern volatile struct limine_framebuffer_request limine_framebuffer_request;
2626
extern volatile struct limine_memmap_request limine_memmap_request;
2727
extern volatile struct limine_hhdm_request limine_hhdm_request;
28+
extern volatile struct limine_executable_address_request limine_address_request;

kernel/src/kernel.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
#include "arch/debug.h"
1919
#include "arch/interrupts.h"
20+
#include "arch/paging.h"
2021
#include "arch/system.h"
2122
#include "boot/limine.h"
2223
#include "mem/pmm.h"
@@ -29,6 +30,7 @@ void kstart()
2930
architecture_initialize();
3031

3132
pmm_init(&limine_memmap_request, &limine_hhdm_request);
33+
paging_init(&limine_memmap_request);
3234

3335
log_info("Welcome to Zinix");
3436
log_info("If you are running in QEMU without graphics press `C-a x` to exit");

0 commit comments

Comments
 (0)