mm: put all code into separate files
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Define the object files
|
# Define the object files
|
||||||
OBJS = bin/boot.o bin/kernel.o bin/idt.o bin/isr.o bin/screen.o bin/utils.o bin/string.o bin/stdio.o bin/disk.o bin/pic.o bin/pit.o bin/keyboard.o bin/paging.o bin/fat.o bin/task.o bin/switch.o
|
OBJS = bin/boot.o bin/kernel.o bin/idt.o bin/isr.o bin/screen.o \
|
||||||
|
bin/utils.o bin/string.o bin/stdio.o bin/disk.o bin/pic.o bin/pit.o \
|
||||||
|
bin/keyboard.o bin/paging.o bin/heap.o bin/page_alloc.o bin/page_tables.o \
|
||||||
|
bin/fat.o bin/task.o bin/switch.o
|
||||||
|
|
||||||
# Define the compiler and assembler
|
# Define the compiler and assembler
|
||||||
CC = gcc -D__is_katauos
|
CC = gcc -D__is_katauos
|
||||||
@@ -93,6 +96,15 @@ bin/keyboard.o: src/drivers/keyboard.c
|
|||||||
bin/paging.o: src/mm/paging.c
|
bin/paging.o: src/mm/paging.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
bin/heap.o: src/mm/heap.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
bin/page_alloc.o: src/mm/page_alloc.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
bin/page_tables.o: src/mm/page_tables.c
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
bin/fat.o: src/drivers/fat.c
|
bin/fat.o: src/drivers/fat.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
|||||||
+24
-2
@@ -1,11 +1,33 @@
|
|||||||
#ifndef PAGING_H
|
#ifndef PAGING_H
|
||||||
#define PAGING_H
|
#define PAGING_H
|
||||||
|
|
||||||
|
#include "../include/stdio.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
extern uint32_t kernel_page_directory[1024] __attribute__((aligned(4096)));
|
||||||
|
extern uint32_t kernel_page_table[1024] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
|
|
||||||
|
//paging.c
|
||||||
|
void enablePaging();
|
||||||
|
void loadPageDirectory(uint32_t* page_directory);
|
||||||
void paging_init();
|
void paging_init();
|
||||||
|
void test_paging();
|
||||||
|
|
||||||
|
//page_alloc.c
|
||||||
|
void init_allocator();
|
||||||
|
void* alloc_page();
|
||||||
|
void free_page(void* physaddr);
|
||||||
|
|
||||||
|
//page_tables.c
|
||||||
|
void *get_physaddr(void *virtualaddr);
|
||||||
|
void map_page(void* physaddr, void* virtualaddr, unsigned int flags);
|
||||||
|
void map_kernel_page(void* virtualaddr, unsigned int flags);
|
||||||
|
|
||||||
|
//heap.c
|
||||||
void heap_init();
|
void heap_init();
|
||||||
void* heap_alloc(uint32_t size);
|
void* heap_alloc(uint32_t size);
|
||||||
void heap_free(void* ptr);
|
void heap_free(void* ptr);
|
||||||
void test_paging();
|
|
||||||
void init_allocator();
|
|
||||||
#endif
|
#endif
|
||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#include "../include/paging.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../include/stdio.h"
|
||||||
|
|
||||||
|
// Структура заголовка блока памяти
|
||||||
|
typedef struct Block {
|
||||||
|
uint32_t size; // Размер блока (в байтах, включая заголовок)
|
||||||
|
uint32_t is_free; // 1 = свободен, 0 = занят
|
||||||
|
struct Block* next; // Указатель на следующий блок
|
||||||
|
} Block;
|
||||||
|
|
||||||
|
// Структура для управления кучей
|
||||||
|
typedef struct {
|
||||||
|
void* start; // Начало кучи
|
||||||
|
uint32_t size; // Текущий размер кучи в байтах
|
||||||
|
Block* first; // Первый блок в куче
|
||||||
|
} Heap;
|
||||||
|
|
||||||
|
Heap kernel_heap;
|
||||||
|
|
||||||
|
void heap_init() {
|
||||||
|
kernel_heap.start = (void*)0xD0000000; // Начало кучи в ядре
|
||||||
|
kernel_heap.size = 0x1000; // Начальный размер — 1 страница (4 КБ)
|
||||||
|
printf("before map_kernel_page\n");
|
||||||
|
// Отображаем первую страницу
|
||||||
|
map_kernel_page(kernel_heap.start, 0x2); // writable, supervisor
|
||||||
|
printf("after map_kernel_page\n");
|
||||||
|
// Создаём первый блок, который занимает всю страницу
|
||||||
|
Block* initial_block = (Block*)kernel_heap.start;
|
||||||
|
initial_block->size = 0x1000 - sizeof(Block); // Размер без учёта заголовка
|
||||||
|
initial_block->is_free = 1; // Свободен
|
||||||
|
initial_block->next = (Block*)0; // Пока нет следующего
|
||||||
|
|
||||||
|
kernel_heap.first = initial_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* heap_alloc(uint32_t size) {
|
||||||
|
// Выравниваем размер до 4 байт
|
||||||
|
size = (size + 3) & ~3;
|
||||||
|
|
||||||
|
Block* current = kernel_heap.first;
|
||||||
|
Block* prev = (Block*)0;
|
||||||
|
|
||||||
|
// Ищем подходящий свободный блок
|
||||||
|
while (current) {
|
||||||
|
if (current->is_free && current->size >= size) {
|
||||||
|
if (current->size >= size + sizeof(Block) + 4) {
|
||||||
|
Block* new_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
||||||
|
new_block->size = current->size - size - sizeof(Block);
|
||||||
|
new_block->is_free = 1;
|
||||||
|
new_block->next = current->next;
|
||||||
|
|
||||||
|
current->size = size;
|
||||||
|
current->next = new_block;
|
||||||
|
}
|
||||||
|
current->is_free = 0;
|
||||||
|
return (void*)((uint32_t)current + sizeof(Block));
|
||||||
|
}
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нет подходящего блока — выделяем нужное количество страниц
|
||||||
|
uint32_t total_size = size + sizeof(Block); // Размер с заголовком
|
||||||
|
uint32_t pages_needed = (total_size + 0xFFF) / 0x1000; // Округляем вверх до страниц
|
||||||
|
uint32_t alloc_size = pages_needed * 0x1000; // Сколько байт выделяем
|
||||||
|
|
||||||
|
void* new_page = (void*)((uint32_t)kernel_heap.start + kernel_heap.size);
|
||||||
|
for (uint32_t i = 0; i < pages_needed; i++) {
|
||||||
|
map_kernel_page((void*)((uint32_t)new_page + i * 0x1000), 0x2);
|
||||||
|
}
|
||||||
|
kernel_heap.size += alloc_size;
|
||||||
|
|
||||||
|
Block* new_block = (Block*)new_page;
|
||||||
|
new_block->size = alloc_size - sizeof(Block);
|
||||||
|
new_block->is_free = 1;
|
||||||
|
new_block->next = (Block*)0;
|
||||||
|
|
||||||
|
if (prev) prev->next = new_block;
|
||||||
|
if (!kernel_heap.first) kernel_heap.first = new_block;
|
||||||
|
|
||||||
|
// Теперь current указывает на новый блок, продолжаем цикл
|
||||||
|
current = new_block;
|
||||||
|
if (current->size >= size) {
|
||||||
|
if (current->size >= size + sizeof(Block) + 4) {
|
||||||
|
Block* split_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
||||||
|
split_block->size = current->size - size - sizeof(Block);
|
||||||
|
split_block->is_free = 1;
|
||||||
|
split_block->next = current->next;
|
||||||
|
|
||||||
|
current->size = size;
|
||||||
|
current->next = split_block;
|
||||||
|
}
|
||||||
|
current->is_free = 0;
|
||||||
|
return (void*)((uint32_t)current + sizeof(Block));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если что-то пошло не так, возвращаем NULL
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void heap_free(void* ptr) {
|
||||||
|
if (!ptr || (uint32_t)ptr < (uint32_t)kernel_heap.start) return;
|
||||||
|
|
||||||
|
// Находим блок по указателю (указатель указывает после заголовка)
|
||||||
|
Block* block = (Block*)((uint32_t)ptr - sizeof(Block));
|
||||||
|
block->is_free = 1;
|
||||||
|
|
||||||
|
// Слияние с предыдущим блоком, если он свободен
|
||||||
|
Block* current = kernel_heap.first;
|
||||||
|
Block* prev = (Block*)0;
|
||||||
|
while (current && current != block) {
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
if (prev && prev->is_free) {
|
||||||
|
prev->size += sizeof(Block) + block->size;
|
||||||
|
prev->next = block->next;
|
||||||
|
block = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Слияние с следующим блоком, если он свободен
|
||||||
|
if (block->next && block->next->is_free) {
|
||||||
|
block->size += sizeof(Block) + block->next->size;
|
||||||
|
block->next = block->next->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "../include/paging.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../include/stdio.h"
|
||||||
|
|
||||||
|
uint8_t page_bitmap[512]; // 1 бит на страницу
|
||||||
|
|
||||||
|
void init_allocator() {
|
||||||
|
int i;
|
||||||
|
// Помечаем все страницы как свободные (0)
|
||||||
|
for (i = 0; i < 512; i++) {
|
||||||
|
page_bitmap[i] = 0;
|
||||||
|
}
|
||||||
|
// Первые 4 МБ уже заняты ядром (0x00000000–0x003FFFFF)
|
||||||
|
for (i = 0; i < 1024 / 8; i++) { // 1024 страницы = 128 байт
|
||||||
|
page_bitmap[i] = 0xFF; // Все биты = 1 (занято)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* alloc_page() {
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; i < 512; i++) {
|
||||||
|
if (page_bitmap[i] != 0xFF) {
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (!(page_bitmap[i] & (1 << j))) {
|
||||||
|
page_bitmap[i] |= (1 << j);
|
||||||
|
return (void*)(((i * 8) + j) * 0x1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("no bitches? no free pages?\n");
|
||||||
|
return (void*)0; // Нет свободных страниц
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_page(void* physaddr) {
|
||||||
|
uint32_t page_num = (uint32_t)physaddr / 0x1000;
|
||||||
|
uint32_t byte_idx = page_num / 8;
|
||||||
|
uint32_t bit_idx = page_num % 8;
|
||||||
|
page_bitmap[byte_idx] &= ~(1 << bit_idx); // Сбрасываем бит
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
#include "../include/paging.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void *get_physaddr(void *virtualaddr) {
|
||||||
|
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
|
||||||
|
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
|
||||||
|
|
||||||
|
unsigned long *pd = (unsigned long *)0xFFFFF000;
|
||||||
|
// Here you need to check whether the PD entry is present.
|
||||||
|
|
||||||
|
unsigned long *pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
// Here you need to check whether the PT entry is present.
|
||||||
|
|
||||||
|
return (void *)((pt[ptindex] & ~0xFFF) + ((unsigned long)virtualaddr & 0xFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
void map_page(void* physaddr, void* virtualaddr, unsigned int flags) {
|
||||||
|
// Убеждаемся, что адреса выровнены по 4 КБ
|
||||||
|
uint32_t phys = (uint32_t)physaddr & ~0xFFF; // Обнуляем младшие 12 бит
|
||||||
|
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF;
|
||||||
|
|
||||||
|
// Вычисляем индексы
|
||||||
|
uint32_t pdindex = virt >> 22; // Индекс в каталоге страниц
|
||||||
|
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
||||||
|
|
||||||
|
uint32_t *pd = (uint32_t *)0xFFFFF000; // Адрес каталога страниц в виртуальной памяти
|
||||||
|
|
||||||
|
// Проверяем, существует ли таблица страниц
|
||||||
|
if (!(pd[pdindex] & 0x1)) { // Бит 0 — "present"
|
||||||
|
// Если таблицы нет, создаём новую
|
||||||
|
void *new_pt = alloc_page();
|
||||||
|
if (!new_pt) while (1); // Нет памяти
|
||||||
|
pd[pdindex] = (uint32_t)new_pt | 0x3; // present, writable
|
||||||
|
|
||||||
|
// Очищаем новую таблицу
|
||||||
|
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
pt[i] = 0; // Все страницы "не присутствуют"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем адрес таблицы страниц
|
||||||
|
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
|
||||||
|
// Устанавливаем отображение
|
||||||
|
pt[ptindex] = phys | (flags & 0xFFF) | 0x1; // Флаги + present
|
||||||
|
|
||||||
|
// Сбрасываем TLB для этого адреса
|
||||||
|
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void map_kernel_page(void* virtualaddr, unsigned int flags) {
|
||||||
|
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF; // Выравниваем по 4 КБ
|
||||||
|
if (virt < 0xC0000000) while(1); // Ошибка: ядро только выше 3 ГБ
|
||||||
|
|
||||||
|
uint32_t pdindex = virt >> 22; // Индекс в каталоге
|
||||||
|
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
||||||
|
|
||||||
|
uint32_t* pd = (uint32_t*)0xFFFFF000; // Каталог страниц
|
||||||
|
|
||||||
|
// Проверяем, существует ли таблица страниц
|
||||||
|
if (!(pd[pdindex] & 0x1)) {
|
||||||
|
void* new_pt = alloc_page();
|
||||||
|
if (!new_pt) while(1); // Нет памяти
|
||||||
|
|
||||||
|
// Убеждаемся, что адрес выровнен
|
||||||
|
if ((uint32_t)new_pt & 0xFFF) while(1); // Ошибка выравнивания
|
||||||
|
|
||||||
|
pd[pdindex] = ((uint32_t)new_pt) | 0x3; // Временно записываем в каталог
|
||||||
|
asm volatile("invlpg (%0)" : : "r" ((uint32_t)&pd[pdindex]) : "memory"); // Инвалидируем TLB
|
||||||
|
|
||||||
|
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
pt[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем таблицу страниц
|
||||||
|
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
void* phys = alloc_page();
|
||||||
|
if (!phys) while(1);
|
||||||
|
|
||||||
|
// Проверяем, не занята ли страница
|
||||||
|
if (pt[ptindex] & 0x1) while(1); // Ошибка: страница уже отображена
|
||||||
|
|
||||||
|
pt[ptindex] = (uint32_t)phys | (flags & 0xFFF) | 0x1; // supervisor, present
|
||||||
|
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
||||||
|
}
|
||||||
+14
-438
@@ -1,6 +1,8 @@
|
|||||||
#include "../include/paging.h"
|
#include "../include/paging.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "../include/stdio.h"
|
|
||||||
|
uint32_t kernel_page_directory[1024] __attribute__((aligned(4096)));
|
||||||
|
uint32_t kernel_page_table[1024] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
void enablePaging() {
|
void enablePaging() {
|
||||||
asm volatile (
|
asm volatile (
|
||||||
@@ -27,78 +29,7 @@ void loadPageDirectory(uint32_t* page_directory) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t kernel_page_directory[1024] __attribute__((aligned(4096)));
|
void paging_init() {
|
||||||
uint32_t kernel_page_table[1024] __attribute__((aligned(4096)));
|
|
||||||
|
|
||||||
// 16 МБ = 16 * 1024 * 1024 байт = 4096 страниц (по 4 КБ)
|
|
||||||
// 4096 бит = 512 байт
|
|
||||||
uint8_t page_bitmap[512]; // 1 бит на страницу
|
|
||||||
|
|
||||||
/**
|
|
||||||
page_bitmap — массив, где каждый бит соответствует странице (0 = свободна, 1 = занята).
|
|
||||||
init_allocator помечает первые 4 МБ как занятые (там ядро).
|
|
||||||
alloc_page ищет первый свободный бит и возвращает адрес страницы.
|
|
||||||
free_page освобождает страницу, сбрасывая бит.
|
|
||||||
**/
|
|
||||||
|
|
||||||
void init_allocator() {
|
|
||||||
int i;
|
|
||||||
// Помечаем все страницы как свободные (0)
|
|
||||||
for (i = 0; i < 512; i++) {
|
|
||||||
page_bitmap[i] = 0;
|
|
||||||
}
|
|
||||||
// Первые 4 МБ уже заняты ядром (0x00000000–0x003FFFFF)
|
|
||||||
for (i = 0; i < 1024 / 8; i++) { // 1024 страницы = 128 байт
|
|
||||||
page_bitmap[i] = 0xFF; // Все биты = 1 (занято)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Выделяем свободную страницу
|
|
||||||
void* alloc_page() {
|
|
||||||
// printf("alloc_page called\n");
|
|
||||||
int i, j;
|
|
||||||
for (i = 0; i < 512; i++) {
|
|
||||||
if (page_bitmap[i] != 0xFF) {
|
|
||||||
for (j = 0; j < 8; j++) {
|
|
||||||
if (!(page_bitmap[i] & (1 << j))) {
|
|
||||||
page_bitmap[i] |= (1 << j);
|
|
||||||
return (void*)(((i * 8) + j) * 0x1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("no bitches? no free pages?\n");
|
|
||||||
return (void*)0; // Нет свободных страниц
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_paging() {
|
|
||||||
uint32_t* pd = (uint32_t*)0xFFFFF000;
|
|
||||||
uint32_t expected = (uint32_t)kernel_page_table | 0x3;
|
|
||||||
// Игнорируем дополнительные флаги, проверяем только базовый адрес и present
|
|
||||||
if ((pd[0] & ~0xFFF) == (expected & ~0xFFF)) {
|
|
||||||
// Успех
|
|
||||||
} else {
|
|
||||||
printf("RECURSION BROKEN!!\n");
|
|
||||||
printf("pd[0] = 0x%X\n");
|
|
||||||
printf("(uint32_t)first_page_table | 0x3 = %X", (uint32_t)kernel_page_table | 0x3);
|
|
||||||
while (1); // Рекурсия сломана
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Освобождаем страницу
|
|
||||||
void free_page(void *physaddr) {
|
|
||||||
uint32_t page_num = (uint32_t)physaddr / 0x1000;
|
|
||||||
uint32_t byte_idx = page_num / 8;
|
|
||||||
uint32_t bit_idx = page_num % 8;
|
|
||||||
page_bitmap[byte_idx] &= ~(1 << bit_idx); // Сбрасываем бит
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Теперь первые 4 МБ физической памяти отображаются на виртуальный адрес 0xC0000000 (3 ГБ), а не на 0x00000000.
|
|
||||||
Это оставляет нижние 3 ГБ (0x00000000–0xBFFFFFFF) свободными для пользовательских процессов.
|
|
||||||
**/
|
|
||||||
void paging_init()
|
|
||||||
{
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
// Проверяем физические адреса
|
// Проверяем физические адреса
|
||||||
@@ -133,371 +64,16 @@ void paging_init()
|
|||||||
enablePaging();
|
enablePaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *get_physaddr(void *virtualaddr) {
|
void test_paging() {
|
||||||
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
|
uint32_t* pd = (uint32_t*)0xFFFFF000;
|
||||||
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
|
uint32_t expected = (uint32_t)kernel_page_table | 0x3;
|
||||||
|
// Игнорируем дополнительные флаги, проверяем только базовый адрес и present
|
||||||
unsigned long *pd = (unsigned long *)0xFFFFF000;
|
if ((pd[0] & ~0xFFF) == (expected & ~0xFFF)) {
|
||||||
// Here you need to check whether the PD entry is present.
|
// Успех
|
||||||
|
|
||||||
unsigned long *pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
|
|
||||||
// Here you need to check whether the PT entry is present.
|
|
||||||
|
|
||||||
return (void *)((pt[ptindex] & ~0xFFF) + ((unsigned long)virtualaddr & 0xFFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Проверяем, существует ли таблица страниц (бит present в записи каталога).
|
|
||||||
Используем invlpg (inline assembly) для сброса TLB, чтобы процессор увидел изменения.
|
|
||||||
Пока вместо выделения новой таблицы страниц я добавил бесконечный цикл (while(1)). Позже мы заменим это аллокатором.
|
|
||||||
**/
|
|
||||||
|
|
||||||
void map_page(void *physaddr, void *virtualaddr, unsigned int flags) {
|
|
||||||
// Убеждаемся, что адреса выровнены по 4 КБ
|
|
||||||
uint32_t phys = (uint32_t)physaddr & ~0xFFF; // Обнуляем младшие 12 бит
|
|
||||||
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF;
|
|
||||||
|
|
||||||
// Вычисляем индексы
|
|
||||||
uint32_t pdindex = virt >> 22; // Индекс в каталоге страниц
|
|
||||||
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
|
||||||
|
|
||||||
uint32_t *pd = (uint32_t *)0xFFFFF000; // Адрес каталога страниц в виртуальной памяти
|
|
||||||
|
|
||||||
// Проверяем, существует ли таблица страниц
|
|
||||||
if (!(pd[pdindex] & 0x1)) { // Бит 0 — "present"
|
|
||||||
// Если таблицы нет, создаём новую (для простоты пока паникуем)
|
|
||||||
void *new_pt = alloc_page();
|
|
||||||
if (!new_pt) while (1); // Нет памяти
|
|
||||||
pd[pdindex] = (uint32_t)new_pt | 0x3; // present, writable
|
|
||||||
|
|
||||||
// Очищаем новую таблицу
|
|
||||||
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
|
||||||
for (int i = 0; i < 1024; i++) {
|
|
||||||
pt[i] = 0; // Все страницы "не присутствуют"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем адрес таблицы страниц
|
|
||||||
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
|
||||||
|
|
||||||
// Устанавливаем отображение
|
|
||||||
pt[ptindex] = phys | (flags & 0xFFF) | 0x1; // Флаги + present
|
|
||||||
|
|
||||||
// Сбрасываем TLB для этого адреса
|
|
||||||
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
//map_kernel_page((void*)0xC0100000, 0x2); — отобразит страницу в ядре.
|
|
||||||
void map_kernel_page(void* virtualaddr, unsigned int flags) {
|
|
||||||
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF; // Выравниваем по 4 КБ
|
|
||||||
if (virt < 0xC0000000) while(1); // Ошибка: ядро только выше 3 ГБ
|
|
||||||
|
|
||||||
uint32_t pdindex = virt >> 22; // Индекс в каталоге
|
|
||||||
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
|
||||||
|
|
||||||
uint32_t* pd = (uint32_t*)0xFFFFF000; // Каталог страниц
|
|
||||||
|
|
||||||
// Проверяем, существует ли таблица страниц
|
|
||||||
if (!(pd[pdindex] & 0x1)) {
|
|
||||||
void* new_pt = alloc_page();
|
|
||||||
if (!new_pt) while(1); // Нет памяти
|
|
||||||
|
|
||||||
// Убеждаемся, что адрес выровнен (опционально, если alloc_page() не гарантирует)
|
|
||||||
if ((uint32_t)new_pt & 0xFFF) while(1); // Ошибка выравнивания
|
|
||||||
|
|
||||||
pd[pdindex] = ((uint32_t)new_pt) | 0x3; // Временно записываем в каталог
|
|
||||||
asm volatile("invlpg (%0)" : : "r" ((uint32_t)&pd[pdindex]) : "memory"); // Инвалидируем TLB
|
|
||||||
|
|
||||||
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
|
||||||
for (int i = 0; i < 1024; i++) {
|
|
||||||
pt[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем таблицу страниц
|
|
||||||
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
|
||||||
void* phys = alloc_page();
|
|
||||||
if (!phys) while(1);
|
|
||||||
|
|
||||||
// Проверяем, не занята ли страница
|
|
||||||
if (pt[ptindex] & 0x1) while(1); // Ошибка: страница уже отображена
|
|
||||||
|
|
||||||
pt[ptindex] = (uint32_t)phys | (flags & 0xFFF) | 0x1; // supervisor, present
|
|
||||||
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Dalshe idyot to chto napisal grok dlya processov v buduschem
|
|
||||||
**/
|
|
||||||
/*
|
|
||||||
// Структура для процесса
|
|
||||||
typedef struct {
|
|
||||||
uint32_t page_directory[1024] __attribute__((aligned(4096))); // Каталог страниц процесса
|
|
||||||
uint32_t pid; // ID процесса (для примера)
|
|
||||||
} Process;
|
|
||||||
|
|
||||||
// Создание нового процесса
|
|
||||||
//Создаёт новый каталог страниц для процесса, копируя ядро (768–1023 записи) и оставляя нижние 3 ГБ пустыми.
|
|
||||||
Process* create_process(uint32_t pid) {
|
|
||||||
Process* proc = (Process*)alloc_page(); // Выделяем страницу под структуру
|
|
||||||
if (!proc) return (Process*)0; // Нет памяти
|
|
||||||
|
|
||||||
int i;
|
|
||||||
// Копируем каталог ядра в каталог процесса
|
|
||||||
for (i = 0; i < 1024; i++) {
|
|
||||||
proc->page_directory[i] = kernel_page_directory[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Нижние 3 ГБ (0-767) изначально пустые
|
|
||||||
for (i = 0; i < 768; i++) {
|
|
||||||
proc->page_directory[i] = 0x00000002; // not present
|
|
||||||
}
|
|
||||||
|
|
||||||
proc->pid = pid;
|
|
||||||
return proc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Переключение на процесс
|
|
||||||
//Переключает процессор на каталог страниц процесса.
|
|
||||||
void switch_to_process(Process* proc) {
|
|
||||||
loadPageDirectory(proc->page_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Выделяет страницу в пользовательском пространстве (ниже 0xC0000000) с флагами user-level (бит 2 = 1).
|
|
||||||
void map_user_page(Process* proc, void* virtualaddr, unsigned int flags) {
|
|
||||||
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF;
|
|
||||||
if (virt >= 0xC0000000) while(1); // Ошибка: пользователь не может трогать ядро!
|
|
||||||
|
|
||||||
uint32_t pdindex = virt >> 22;
|
|
||||||
uint32_t ptindex = (virt >> 12) & 0x3FF;
|
|
||||||
|
|
||||||
// Проверяем, есть ли таблица страниц
|
|
||||||
if (!(proc->page_directory[pdindex] & 0x1)) {
|
|
||||||
void* new_pt = alloc_page();
|
|
||||||
if (!new_pt) while(1); // Нет памяти
|
|
||||||
|
|
||||||
// Новая таблица: user, writable, present (0x7 = 111b)
|
|
||||||
proc->page_directory[pdindex] = ((uint32_t)new_pt) | 0x7;
|
|
||||||
|
|
||||||
// Очищаем таблицу
|
|
||||||
uint32_t* pt = (uint32_t*)new_pt;
|
|
||||||
for (int i = 0; i < 1024; i++) {
|
|
||||||
pt[i] = 0; // Все страницы "не присутствуют"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем таблицу страниц
|
|
||||||
uint32_t* pt = (uint32_t*)(proc->page_directory[pdindex] & ~0xFFF);
|
|
||||||
void* phys = alloc_page();
|
|
||||||
if (!phys) while(1); // Нет памяти
|
|
||||||
|
|
||||||
// Отображаем страницу: user, flags, present
|
|
||||||
pt[ptindex] = (uint32_t)phys | (flags & 0xFFF) | 0x5; // 0x5 = 101b (present, user)
|
|
||||||
|
|
||||||
// Сбрасываем TLB
|
|
||||||
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Пример использования
|
|
||||||
void test_process() {
|
|
||||||
Process* proc = create_process(1); // Создаём процесс с PID 1
|
|
||||||
map_user_page(proc, (void*)0x1000, 0x6); // Отображаем страницу на 0x1000 (writable, user)
|
|
||||||
switch_to_process(proc); // Переключаемся на процесс
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Структура заголовка блока памяти
|
|
||||||
typedef struct Block {
|
|
||||||
uint32_t size; // Размер блока (в байтах, включая заголовок)
|
|
||||||
uint32_t is_free; // 1 = свободен, 0 = занят
|
|
||||||
struct Block* next; // Указатель на следующий блок
|
|
||||||
} Block;
|
|
||||||
|
|
||||||
// Структура для управления кучей
|
|
||||||
typedef struct {
|
|
||||||
void* start; // Начало кучи
|
|
||||||
uint32_t size; // Текущий размер кучи в байтах
|
|
||||||
Block* first; // Первый блок в куче
|
|
||||||
} Heap;
|
|
||||||
|
|
||||||
Heap kernel_heap;
|
|
||||||
|
|
||||||
void heap_init() {
|
|
||||||
kernel_heap.start = (void*)0xD0000000; // Начало кучи в ядре
|
|
||||||
kernel_heap.size = 0x1000; // Начальный размер — 1 страница (4 КБ)
|
|
||||||
printf("before map_kernel_page\n");
|
|
||||||
// Отображаем первую страницу
|
|
||||||
map_kernel_page(kernel_heap.start, 0x2); // writable, supervisor
|
|
||||||
printf("after map_kernel_page\n");
|
|
||||||
// Создаём первый блок, который занимает всю страницу
|
|
||||||
Block* initial_block = (Block*)kernel_heap.start;
|
|
||||||
initial_block->size = 0x1000 - sizeof(Block); // Размер без учёта заголовка
|
|
||||||
initial_block->is_free = 1; // Свободен
|
|
||||||
initial_block->next = (Block*)0; // Пока нет следующего
|
|
||||||
|
|
||||||
kernel_heap.first = initial_block;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Выделение памяти из кучи
|
|
||||||
//Ищет свободный блок подходящего размера. Если блок большой, делит его.
|
|
||||||
//Если свободных блоков нет, добавляет страницу.
|
|
||||||
void* heap_alloc(uint32_t size) {
|
|
||||||
// Выравниваем размер до 4 байт
|
|
||||||
size = (size + 3) & ~3;
|
|
||||||
|
|
||||||
Block* current = kernel_heap.first;
|
|
||||||
Block* prev = (Block*)0;
|
|
||||||
|
|
||||||
// Ищем подходящий свободный блок
|
|
||||||
while (current) {
|
|
||||||
if (current->is_free && current->size >= size) {
|
|
||||||
if (current->size >= size + sizeof(Block) + 4) {
|
|
||||||
Block* new_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
|
||||||
new_block->size = current->size - size - sizeof(Block);
|
|
||||||
new_block->is_free = 1;
|
|
||||||
new_block->next = current->next;
|
|
||||||
|
|
||||||
current->size = size;
|
|
||||||
current->next = new_block;
|
|
||||||
}
|
|
||||||
current->is_free = 0;
|
|
||||||
return (void*)((uint32_t)current + sizeof(Block));
|
|
||||||
}
|
|
||||||
prev = current;
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Нет подходящего блока — выделяем нужное количество страниц
|
|
||||||
uint32_t total_size = size + sizeof(Block); // Размер с заголовком
|
|
||||||
uint32_t pages_needed = (total_size + 0xFFF) / 0x1000; // Округляем вверх до страниц
|
|
||||||
uint32_t alloc_size = pages_needed * 0x1000; // Сколько байт выделяем
|
|
||||||
|
|
||||||
void* new_page = (void*)((uint32_t)kernel_heap.start + kernel_heap.size);
|
|
||||||
for (uint32_t i = 0; i < pages_needed; i++) {
|
|
||||||
map_kernel_page((void*)((uint32_t)new_page + i * 0x1000), 0x2);
|
|
||||||
}
|
|
||||||
kernel_heap.size += alloc_size;
|
|
||||||
|
|
||||||
Block* new_block = (Block*)new_page;
|
|
||||||
new_block->size = alloc_size - sizeof(Block);
|
|
||||||
new_block->is_free = 1;
|
|
||||||
new_block->next = (Block*)0;
|
|
||||||
|
|
||||||
if (prev) prev->next = new_block;
|
|
||||||
if (!kernel_heap.first) kernel_heap.first = new_block;
|
|
||||||
|
|
||||||
// Теперь current указывает на новый блок, продолжаем цикл
|
|
||||||
current = new_block;
|
|
||||||
if (current->size >= size) {
|
|
||||||
if (current->size >= size + sizeof(Block) + 4) {
|
|
||||||
Block* split_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
|
||||||
split_block->size = current->size - size - sizeof(Block);
|
|
||||||
split_block->is_free = 1;
|
|
||||||
split_block->next = current->next;
|
|
||||||
|
|
||||||
current->size = size;
|
|
||||||
current->next = split_block;
|
|
||||||
}
|
|
||||||
current->is_free = 0;
|
|
||||||
return (void*)((uint32_t)current + sizeof(Block));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Если что-то пошло не так, возвращаем NULL
|
|
||||||
return (void*)0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Освобождение памяти
|
|
||||||
//Помечает блок как свободный и пытается объединить его с соседними свободными блоками,
|
|
||||||
//чтобы уменьшить фрагментацию.
|
|
||||||
void heap_free(void* ptr) {
|
|
||||||
if (!ptr || (uint32_t)ptr < (uint32_t)kernel_heap.start) return;
|
|
||||||
|
|
||||||
// Находим блок по указателю (указатель указывает после заголовка)
|
|
||||||
Block* block = (Block*)((uint32_t)ptr - sizeof(Block));
|
|
||||||
block->is_free = 1;
|
|
||||||
|
|
||||||
// Слияние с предыдущим блоком, если он свободен
|
|
||||||
Block* current = kernel_heap.first;
|
|
||||||
Block* prev = (Block*)0;
|
|
||||||
while (current && current != block) {
|
|
||||||
prev = current;
|
|
||||||
current = current->next;
|
|
||||||
}
|
|
||||||
if (prev && prev->is_free) {
|
|
||||||
prev->size += sizeof(Block) + block->size;
|
|
||||||
prev->next = block->next;
|
|
||||||
block = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Слияние с следующим блоком, если он свободен
|
|
||||||
if (block->next && block->next->is_free) {
|
|
||||||
block->size += sizeof(Block) + block->next->size;
|
|
||||||
block->next = block->next->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Функция проверки страниц
|
|
||||||
void test_pages() {
|
|
||||||
// Тест 1: Проверка ядра
|
|
||||||
void* kernel_addr = (void*)0xC0100000;
|
|
||||||
map_kernel_page(kernel_addr, 0x2); // writable, supervisor
|
|
||||||
|
|
||||||
// Записываем значение в страницу
|
|
||||||
uint32_t* kernel_ptr = (uint32_t*)kernel_addr;
|
|
||||||
*kernel_ptr = 0xDEADBEEF;
|
|
||||||
|
|
||||||
// Проверяем, что значение записалось
|
|
||||||
if (*kernel_ptr == 0xDEADBEEF) {
|
|
||||||
// Успех! (в реальной ОС тут можно вывести сообщение через UART или VGA)
|
|
||||||
} else {
|
} else {
|
||||||
while (1); // Ошибка
|
printf("RECURSION BROKEN!!\n");
|
||||||
}
|
printf("pd[0] = 0x%X\n");
|
||||||
/*
|
printf("(uint32_t)first_page_table | 0x3 = %X", (uint32_t)kernel_page_table | 0x3);
|
||||||
// Тест 2: Проверка пользовательского процесса
|
while (1); // Рекурсия сломана
|
||||||
Process* proc = create_process(1); // Создаём процесс
|
|
||||||
void* user_addr = (void*)0x1000;
|
|
||||||
map_user_page(proc, user_addr, 0x6); // writable, user
|
|
||||||
|
|
||||||
// Переключаемся на процесс
|
|
||||||
switch_to_process(proc);
|
|
||||||
|
|
||||||
// Записываем значение
|
|
||||||
uint32_t* user_ptr = (uint32_t*)user_addr;
|
|
||||||
*user_ptr = 0xCAFEBABE;
|
|
||||||
|
|
||||||
// Проверяем
|
|
||||||
if (*user_ptr == 0xCAFEBABE) {
|
|
||||||
// Успех!
|
|
||||||
} else {
|
|
||||||
while (1); // Ошибка
|
|
||||||
}
|
|
||||||
|
|
||||||
// Возвращаемся в ядро
|
|
||||||
switch_to_process((Process*)0); // Предполагаем, что 0 вернёт нас к kernel_page_directory
|
|
||||||
*/
|
|
||||||
// Тест 3: Проверка кучи
|
|
||||||
heap_init();
|
|
||||||
void* heap_ptr1 = heap_alloc(16);
|
|
||||||
uint32_t* heap_data1 = (uint32_t*)heap_ptr1;
|
|
||||||
*heap_data1 = 0x12345678;
|
|
||||||
|
|
||||||
void* heap_ptr2 = heap_alloc(200);
|
|
||||||
uint32_t* heap_data2 = (uint32_t*)heap_ptr2;
|
|
||||||
*heap_data2 = 0x87654321;
|
|
||||||
|
|
||||||
heap_free(heap_ptr1);
|
|
||||||
void* heap_ptr3 = heap_alloc(12); // Должно взять место ptr1
|
|
||||||
|
|
||||||
if (*heap_data1 == 0x12345678 && *heap_data2 == 0x87654321 &&
|
|
||||||
heap_ptr3 == heap_ptr1) {
|
|
||||||
// Успех!
|
|
||||||
} else {
|
|
||||||
while (1); // Ошибка
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
0xD0000000: Произвольный адрес в ядре (выше 0xC0000000), выбран для кучи, чтобы не пересекаться с другими данными.
|
|
||||||
0xFFFFF000: Виртуальный адрес каталога страниц благодаря рекурсивному отображению (последняя запись указывает на себя).
|
|
||||||
0xFFC00000: Начало области, где лежат все таблицы страниц, доступные через рекурсию.
|
|
||||||
**/
|
|
||||||
|
|||||||
@@ -0,0 +1,503 @@
|
|||||||
|
#include "../include/paging.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../include/stdio.h"
|
||||||
|
|
||||||
|
void enablePaging() {
|
||||||
|
asm volatile (
|
||||||
|
"mov %%cr0, %%eax\n"
|
||||||
|
"or $0x80000000, %%eax\n"
|
||||||
|
"mov %%eax, %%cr0\n"
|
||||||
|
"jmp 1f\n"
|
||||||
|
"1:\n"
|
||||||
|
"mov %%cr3, %%eax\n" // Перезагрузка CR3 для сброса TLB
|
||||||
|
"mov %%eax, %%cr3\n"
|
||||||
|
"nop\n"
|
||||||
|
:
|
||||||
|
:
|
||||||
|
: "eax", "memory"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadPageDirectory(uint32_t* page_directory) {
|
||||||
|
asm volatile (
|
||||||
|
"mov %0, %%cr3\n" // Загружаем физический адрес каталога страниц в CR3
|
||||||
|
: // Нет выходных операндов
|
||||||
|
: "r" (page_directory) // Входной операнд — адрес каталога
|
||||||
|
: // Нет изменяемых регистров
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t kernel_page_directory[1024] __attribute__((aligned(4096)));
|
||||||
|
uint32_t kernel_page_table[1024] __attribute__((aligned(4096)));
|
||||||
|
|
||||||
|
// 16 МБ = 16 * 1024 * 1024 байт = 4096 страниц (по 4 КБ)
|
||||||
|
// 4096 бит = 512 байт
|
||||||
|
uint8_t page_bitmap[512]; // 1 бит на страницу
|
||||||
|
|
||||||
|
/**
|
||||||
|
page_bitmap — массив, где каждый бит соответствует странице (0 = свободна, 1 = занята).
|
||||||
|
init_allocator помечает первые 4 МБ как занятые (там ядро).
|
||||||
|
alloc_page ищет первый свободный бит и возвращает адрес страницы.
|
||||||
|
free_page освобождает страницу, сбрасывая бит.
|
||||||
|
**/
|
||||||
|
|
||||||
|
void init_allocator() {
|
||||||
|
int i;
|
||||||
|
// Помечаем все страницы как свободные (0)
|
||||||
|
for (i = 0; i < 512; i++) {
|
||||||
|
page_bitmap[i] = 0;
|
||||||
|
}
|
||||||
|
// Первые 4 МБ уже заняты ядром (0x00000000–0x003FFFFF)
|
||||||
|
for (i = 0; i < 1024 / 8; i++) { // 1024 страницы = 128 байт
|
||||||
|
page_bitmap[i] = 0xFF; // Все биты = 1 (занято)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выделяем свободную страницу
|
||||||
|
void* alloc_page() {
|
||||||
|
// printf("alloc_page called\n");
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; i < 512; i++) {
|
||||||
|
if (page_bitmap[i] != 0xFF) {
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (!(page_bitmap[i] & (1 << j))) {
|
||||||
|
page_bitmap[i] |= (1 << j);
|
||||||
|
return (void*)(((i * 8) + j) * 0x1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("no bitches? no free pages?\n");
|
||||||
|
return (void*)0; // Нет свободных страниц
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_paging() {
|
||||||
|
uint32_t* pd = (uint32_t*)0xFFFFF000;
|
||||||
|
uint32_t expected = (uint32_t)kernel_page_table | 0x3;
|
||||||
|
// Игнорируем дополнительные флаги, проверяем только базовый адрес и present
|
||||||
|
if ((pd[0] & ~0xFFF) == (expected & ~0xFFF)) {
|
||||||
|
// Успех
|
||||||
|
} else {
|
||||||
|
printf("RECURSION BROKEN!!\n");
|
||||||
|
printf("pd[0] = 0x%X\n");
|
||||||
|
printf("(uint32_t)first_page_table | 0x3 = %X", (uint32_t)kernel_page_table | 0x3);
|
||||||
|
while (1); // Рекурсия сломана
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Освобождаем страницу
|
||||||
|
void free_page(void *physaddr) {
|
||||||
|
uint32_t page_num = (uint32_t)physaddr / 0x1000;
|
||||||
|
uint32_t byte_idx = page_num / 8;
|
||||||
|
uint32_t bit_idx = page_num % 8;
|
||||||
|
page_bitmap[byte_idx] &= ~(1 << bit_idx); // Сбрасываем бит
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Теперь первые 4 МБ физической памяти отображаются на виртуальный адрес 0xC0000000 (3 ГБ), а не на 0x00000000.
|
||||||
|
Это оставляет нижние 3 ГБ (0x00000000–0xBFFFFFFF) свободными для пользовательских процессов.
|
||||||
|
**/
|
||||||
|
void paging_init()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Проверяем физические адреса
|
||||||
|
uint32_t pd_phys = (uint32_t)kernel_page_directory;
|
||||||
|
uint32_t pt_phys = (uint32_t)kernel_page_table;
|
||||||
|
|
||||||
|
if (pd_phys & 0xFFF || pt_phys & 0xFFF) {
|
||||||
|
while (1); // Ошибка выравнивания
|
||||||
|
}
|
||||||
|
|
||||||
|
// Заполняем каталог страниц
|
||||||
|
for (i = 0; i < 1024; i++) {
|
||||||
|
kernel_page_directory[i] = 0x00000002; // not present
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identity mapping для 0x00000000–0x003FFFFF
|
||||||
|
for (i = 0; i < 1024; i++) {
|
||||||
|
kernel_page_table[i] = (i * 0x1000) | 0x3; // supervisor, present
|
||||||
|
}
|
||||||
|
kernel_page_directory[0] = pt_phys | 0x3;
|
||||||
|
|
||||||
|
// Рекурсивное отображение
|
||||||
|
kernel_page_directory[1023] = pd_phys | 0x3;
|
||||||
|
|
||||||
|
// Проверяем перед загрузкой
|
||||||
|
if (kernel_page_directory[0] != (pt_phys | 0x3) ||
|
||||||
|
kernel_page_directory[1023] != (pd_phys | 0x3)) {
|
||||||
|
printf("OSHIBKA ZAPISI BLYA\n");
|
||||||
|
while (1); // Ошибка записи
|
||||||
|
}
|
||||||
|
loadPageDirectory(kernel_page_directory);
|
||||||
|
enablePaging();
|
||||||
|
}
|
||||||
|
|
||||||
|
void *get_physaddr(void *virtualaddr) {
|
||||||
|
unsigned long pdindex = (unsigned long)virtualaddr >> 22;
|
||||||
|
unsigned long ptindex = (unsigned long)virtualaddr >> 12 & 0x03FF;
|
||||||
|
|
||||||
|
unsigned long *pd = (unsigned long *)0xFFFFF000;
|
||||||
|
// Here you need to check whether the PD entry is present.
|
||||||
|
|
||||||
|
unsigned long *pt = ((unsigned long *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
// Here you need to check whether the PT entry is present.
|
||||||
|
|
||||||
|
return (void *)((pt[ptindex] & ~0xFFF) + ((unsigned long)virtualaddr & 0xFFF));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Проверяем, существует ли таблица страниц (бит present в записи каталога).
|
||||||
|
Используем invlpg (inline assembly) для сброса TLB, чтобы процессор увидел изменения.
|
||||||
|
Пока вместо выделения новой таблицы страниц я добавил бесконечный цикл (while(1)). Позже мы заменим это аллокатором.
|
||||||
|
**/
|
||||||
|
|
||||||
|
void map_page(void *physaddr, void *virtualaddr, unsigned int flags) {
|
||||||
|
// Убеждаемся, что адреса выровнены по 4 КБ
|
||||||
|
uint32_t phys = (uint32_t)physaddr & ~0xFFF; // Обнуляем младшие 12 бит
|
||||||
|
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF;
|
||||||
|
|
||||||
|
// Вычисляем индексы
|
||||||
|
uint32_t pdindex = virt >> 22; // Индекс в каталоге страниц
|
||||||
|
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
||||||
|
|
||||||
|
uint32_t *pd = (uint32_t *)0xFFFFF000; // Адрес каталога страниц в виртуальной памяти
|
||||||
|
|
||||||
|
// Проверяем, существует ли таблица страниц
|
||||||
|
if (!(pd[pdindex] & 0x1)) { // Бит 0 — "present"
|
||||||
|
// Если таблицы нет, создаём новую (для простоты пока паникуем)
|
||||||
|
void *new_pt = alloc_page();
|
||||||
|
if (!new_pt) while (1); // Нет памяти
|
||||||
|
pd[pdindex] = (uint32_t)new_pt | 0x3; // present, writable
|
||||||
|
|
||||||
|
// Очищаем новую таблицу
|
||||||
|
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
pt[i] = 0; // Все страницы "не присутствуют"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем адрес таблицы страниц
|
||||||
|
uint32_t *pt = ((uint32_t *)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
|
||||||
|
// Устанавливаем отображение
|
||||||
|
pt[ptindex] = phys | (flags & 0xFFF) | 0x1; // Флаги + present
|
||||||
|
|
||||||
|
// Сбрасываем TLB для этого адреса
|
||||||
|
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
//map_kernel_page((void*)0xC0100000, 0x2); — отобразит страницу в ядре.
|
||||||
|
void map_kernel_page(void* virtualaddr, unsigned int flags) {
|
||||||
|
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF; // Выравниваем по 4 КБ
|
||||||
|
if (virt < 0xC0000000) while(1); // Ошибка: ядро только выше 3 ГБ
|
||||||
|
|
||||||
|
uint32_t pdindex = virt >> 22; // Индекс в каталоге
|
||||||
|
uint32_t ptindex = (virt >> 12) & 0x3FF; // Индекс в таблице страниц
|
||||||
|
|
||||||
|
uint32_t* pd = (uint32_t*)0xFFFFF000; // Каталог страниц
|
||||||
|
|
||||||
|
// Проверяем, существует ли таблица страниц
|
||||||
|
if (!(pd[pdindex] & 0x1)) {
|
||||||
|
void* new_pt = alloc_page();
|
||||||
|
if (!new_pt) while(1); // Нет памяти
|
||||||
|
|
||||||
|
// Убеждаемся, что адрес выровнен (опционально, если alloc_page() не гарантирует)
|
||||||
|
if ((uint32_t)new_pt & 0xFFF) while(1); // Ошибка выравнивания
|
||||||
|
|
||||||
|
pd[pdindex] = ((uint32_t)new_pt) | 0x3; // Временно записываем в каталог
|
||||||
|
asm volatile("invlpg (%0)" : : "r" ((uint32_t)&pd[pdindex]) : "memory"); // Инвалидируем TLB
|
||||||
|
|
||||||
|
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
pt[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем таблицу страниц
|
||||||
|
uint32_t* pt = ((uint32_t*)0xFFC00000) + (0x400 * pdindex);
|
||||||
|
void* phys = alloc_page();
|
||||||
|
if (!phys) while(1);
|
||||||
|
|
||||||
|
// Проверяем, не занята ли страница
|
||||||
|
if (pt[ptindex] & 0x1) while(1); // Ошибка: страница уже отображена
|
||||||
|
|
||||||
|
pt[ptindex] = (uint32_t)phys | (flags & 0xFFF) | 0x1; // supervisor, present
|
||||||
|
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Структура заголовка блока памяти
|
||||||
|
typedef struct Block {
|
||||||
|
uint32_t size; // Размер блока (в байтах, включая заголовок)
|
||||||
|
uint32_t is_free; // 1 = свободен, 0 = занят
|
||||||
|
struct Block* next; // Указатель на следующий блок
|
||||||
|
} Block;
|
||||||
|
|
||||||
|
// Структура для управления кучей
|
||||||
|
typedef struct {
|
||||||
|
void* start; // Начало кучи
|
||||||
|
uint32_t size; // Текущий размер кучи в байтах
|
||||||
|
Block* first; // Первый блок в куче
|
||||||
|
} Heap;
|
||||||
|
|
||||||
|
Heap kernel_heap;
|
||||||
|
|
||||||
|
void heap_init() {
|
||||||
|
kernel_heap.start = (void*)0xD0000000; // Начало кучи в ядре
|
||||||
|
kernel_heap.size = 0x1000; // Начальный размер — 1 страница (4 КБ)
|
||||||
|
printf("before map_kernel_page\n");
|
||||||
|
// Отображаем первую страницу
|
||||||
|
map_kernel_page(kernel_heap.start, 0x2); // writable, supervisor
|
||||||
|
printf("after map_kernel_page\n");
|
||||||
|
// Создаём первый блок, который занимает всю страницу
|
||||||
|
Block* initial_block = (Block*)kernel_heap.start;
|
||||||
|
initial_block->size = 0x1000 - sizeof(Block); // Размер без учёта заголовка
|
||||||
|
initial_block->is_free = 1; // Свободен
|
||||||
|
initial_block->next = (Block*)0; // Пока нет следующего
|
||||||
|
|
||||||
|
kernel_heap.first = initial_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выделение памяти из кучи
|
||||||
|
//Ищет свободный блок подходящего размера. Если блок большой, делит его.
|
||||||
|
//Если свободных блоков нет, добавляет страницу.
|
||||||
|
void* heap_alloc(uint32_t size) {
|
||||||
|
// Выравниваем размер до 4 байт
|
||||||
|
size = (size + 3) & ~3;
|
||||||
|
|
||||||
|
Block* current = kernel_heap.first;
|
||||||
|
Block* prev = (Block*)0;
|
||||||
|
|
||||||
|
// Ищем подходящий свободный блок
|
||||||
|
while (current) {
|
||||||
|
if (current->is_free && current->size >= size) {
|
||||||
|
if (current->size >= size + sizeof(Block) + 4) {
|
||||||
|
Block* new_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
||||||
|
new_block->size = current->size - size - sizeof(Block);
|
||||||
|
new_block->is_free = 1;
|
||||||
|
new_block->next = current->next;
|
||||||
|
|
||||||
|
current->size = size;
|
||||||
|
current->next = new_block;
|
||||||
|
}
|
||||||
|
current->is_free = 0;
|
||||||
|
return (void*)((uint32_t)current + sizeof(Block));
|
||||||
|
}
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нет подходящего блока — выделяем нужное количество страниц
|
||||||
|
uint32_t total_size = size + sizeof(Block); // Размер с заголовком
|
||||||
|
uint32_t pages_needed = (total_size + 0xFFF) / 0x1000; // Округляем вверх до страниц
|
||||||
|
uint32_t alloc_size = pages_needed * 0x1000; // Сколько байт выделяем
|
||||||
|
|
||||||
|
void* new_page = (void*)((uint32_t)kernel_heap.start + kernel_heap.size);
|
||||||
|
for (uint32_t i = 0; i < pages_needed; i++) {
|
||||||
|
map_kernel_page((void*)((uint32_t)new_page + i * 0x1000), 0x2);
|
||||||
|
}
|
||||||
|
kernel_heap.size += alloc_size;
|
||||||
|
|
||||||
|
Block* new_block = (Block*)new_page;
|
||||||
|
new_block->size = alloc_size - sizeof(Block);
|
||||||
|
new_block->is_free = 1;
|
||||||
|
new_block->next = (Block*)0;
|
||||||
|
|
||||||
|
if (prev) prev->next = new_block;
|
||||||
|
if (!kernel_heap.first) kernel_heap.first = new_block;
|
||||||
|
|
||||||
|
// Теперь current указывает на новый блок, продолжаем цикл
|
||||||
|
current = new_block;
|
||||||
|
if (current->size >= size) {
|
||||||
|
if (current->size >= size + sizeof(Block) + 4) {
|
||||||
|
Block* split_block = (Block*)((uint32_t)current + sizeof(Block) + size);
|
||||||
|
split_block->size = current->size - size - sizeof(Block);
|
||||||
|
split_block->is_free = 1;
|
||||||
|
split_block->next = current->next;
|
||||||
|
|
||||||
|
current->size = size;
|
||||||
|
current->next = split_block;
|
||||||
|
}
|
||||||
|
current->is_free = 0;
|
||||||
|
return (void*)((uint32_t)current + sizeof(Block));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если что-то пошло не так, возвращаем NULL
|
||||||
|
return (void*)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Освобождение памяти
|
||||||
|
//Помечает блок как свободный и пытается объединить его с соседними свободными блоками,
|
||||||
|
//чтобы уменьшить фрагментацию.
|
||||||
|
void heap_free(void* ptr) {
|
||||||
|
if (!ptr || (uint32_t)ptr < (uint32_t)kernel_heap.start) return;
|
||||||
|
|
||||||
|
// Находим блок по указателю (указатель указывает после заголовка)
|
||||||
|
Block* block = (Block*)((uint32_t)ptr - sizeof(Block));
|
||||||
|
block->is_free = 1;
|
||||||
|
|
||||||
|
// Слияние с предыдущим блоком, если он свободен
|
||||||
|
Block* current = kernel_heap.first;
|
||||||
|
Block* prev = (Block*)0;
|
||||||
|
while (current && current != block) {
|
||||||
|
prev = current;
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
if (prev && prev->is_free) {
|
||||||
|
prev->size += sizeof(Block) + block->size;
|
||||||
|
prev->next = block->next;
|
||||||
|
block = prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Слияние с следующим блоком, если он свободен
|
||||||
|
if (block->next && block->next->is_free) {
|
||||||
|
block->size += sizeof(Block) + block->next->size;
|
||||||
|
block->next = block->next->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Функция проверки страниц
|
||||||
|
void test_pages() {
|
||||||
|
// Тест 1: Проверка ядра
|
||||||
|
void* kernel_addr = (void*)0xC0100000;
|
||||||
|
map_kernel_page(kernel_addr, 0x2); // writable, supervisor
|
||||||
|
|
||||||
|
// Записываем значение в страницу
|
||||||
|
uint32_t* kernel_ptr = (uint32_t*)kernel_addr;
|
||||||
|
*kernel_ptr = 0xDEADBEEF;
|
||||||
|
|
||||||
|
// Проверяем, что значение записалось
|
||||||
|
if (*kernel_ptr == 0xDEADBEEF) {
|
||||||
|
// Успех! (в реальной ОС тут можно вывести сообщение через UART или VGA)
|
||||||
|
} else {
|
||||||
|
while (1); // Ошибка
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Тест 2: Проверка пользовательского процесса
|
||||||
|
Process* proc = create_process(1); // Создаём процесс
|
||||||
|
void* user_addr = (void*)0x1000;
|
||||||
|
map_user_page(proc, user_addr, 0x6); // writable, user
|
||||||
|
|
||||||
|
// Переключаемся на процесс
|
||||||
|
switch_to_process(proc);
|
||||||
|
|
||||||
|
// Записываем значение
|
||||||
|
uint32_t* user_ptr = (uint32_t*)user_addr;
|
||||||
|
*user_ptr = 0xCAFEBABE;
|
||||||
|
|
||||||
|
// Проверяем
|
||||||
|
if (*user_ptr == 0xCAFEBABE) {
|
||||||
|
// Успех!
|
||||||
|
} else {
|
||||||
|
while (1); // Ошибка
|
||||||
|
}
|
||||||
|
|
||||||
|
// Возвращаемся в ядро
|
||||||
|
switch_to_process((Process*)0); // Предполагаем, что 0 вернёт нас к kernel_page_directory
|
||||||
|
*/
|
||||||
|
// Тест 3: Проверка кучи
|
||||||
|
heap_init();
|
||||||
|
void* heap_ptr1 = heap_alloc(16);
|
||||||
|
uint32_t* heap_data1 = (uint32_t*)heap_ptr1;
|
||||||
|
*heap_data1 = 0x12345678;
|
||||||
|
|
||||||
|
void* heap_ptr2 = heap_alloc(200);
|
||||||
|
uint32_t* heap_data2 = (uint32_t*)heap_ptr2;
|
||||||
|
*heap_data2 = 0x87654321;
|
||||||
|
|
||||||
|
heap_free(heap_ptr1);
|
||||||
|
void* heap_ptr3 = heap_alloc(12); // Должно взять место ptr1
|
||||||
|
|
||||||
|
if (*heap_data1 == 0x12345678 && *heap_data2 == 0x87654321 &&
|
||||||
|
heap_ptr3 == heap_ptr1) {
|
||||||
|
// Успех!
|
||||||
|
} else {
|
||||||
|
while (1); // Ошибка
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
0xD0000000: Произвольный адрес в ядре (выше 0xC0000000), выбран для кучи, чтобы не пересекаться с другими данными.
|
||||||
|
0xFFFFF000: Виртуальный адрес каталога страниц благодаря рекурсивному отображению (последняя запись указывает на себя).
|
||||||
|
0xFFC00000: Начало области, где лежат все таблицы страниц, доступные через рекурсию.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
Dalshe idyot to chto napisal grok dlya processov v buduschem
|
||||||
|
**/
|
||||||
|
/*
|
||||||
|
// Структура для процесса
|
||||||
|
typedef struct {
|
||||||
|
uint32_t page_directory[1024] __attribute__((aligned(4096))); // Каталог страниц процесса
|
||||||
|
uint32_t pid; // ID процесса (для примера)
|
||||||
|
} Process;
|
||||||
|
|
||||||
|
// Создание нового процесса
|
||||||
|
//Создаёт новый каталог страниц для процесса, копируя ядро (768–1023 записи) и оставляя нижние 3 ГБ пустыми.
|
||||||
|
Process* create_process(uint32_t pid) {
|
||||||
|
Process* proc = (Process*)alloc_page(); // Выделяем страницу под структуру
|
||||||
|
if (!proc) return (Process*)0; // Нет памяти
|
||||||
|
|
||||||
|
int i;
|
||||||
|
// Копируем каталог ядра в каталог процесса
|
||||||
|
for (i = 0; i < 1024; i++) {
|
||||||
|
proc->page_directory[i] = kernel_page_directory[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нижние 3 ГБ (0-767) изначально пустые
|
||||||
|
for (i = 0; i < 768; i++) {
|
||||||
|
proc->page_directory[i] = 0x00000002; // not present
|
||||||
|
}
|
||||||
|
|
||||||
|
proc->pid = pid;
|
||||||
|
return proc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Переключение на процесс
|
||||||
|
//Переключает процессор на каталог страниц процесса.
|
||||||
|
void switch_to_process(Process* proc) {
|
||||||
|
loadPageDirectory(proc->page_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Выделяет страницу в пользовательском пространстве (ниже 0xC0000000) с флагами user-level (бит 2 = 1).
|
||||||
|
void map_user_page(Process* proc, void* virtualaddr, unsigned int flags) {
|
||||||
|
uint32_t virt = (uint32_t)virtualaddr & ~0xFFF;
|
||||||
|
if (virt >= 0xC0000000) while(1); // Ошибка: пользователь не может трогать ядро!
|
||||||
|
|
||||||
|
uint32_t pdindex = virt >> 22;
|
||||||
|
uint32_t ptindex = (virt >> 12) & 0x3FF;
|
||||||
|
|
||||||
|
// Проверяем, есть ли таблица страниц
|
||||||
|
if (!(proc->page_directory[pdindex] & 0x1)) {
|
||||||
|
void* new_pt = alloc_page();
|
||||||
|
if (!new_pt) while(1); // Нет памяти
|
||||||
|
|
||||||
|
// Новая таблица: user, writable, present (0x7 = 111b)
|
||||||
|
proc->page_directory[pdindex] = ((uint32_t)new_pt) | 0x7;
|
||||||
|
|
||||||
|
// Очищаем таблицу
|
||||||
|
uint32_t* pt = (uint32_t*)new_pt;
|
||||||
|
for (int i = 0; i < 1024; i++) {
|
||||||
|
pt[i] = 0; // Все страницы "не присутствуют"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем таблицу страниц
|
||||||
|
uint32_t* pt = (uint32_t*)(proc->page_directory[pdindex] & ~0xFFF);
|
||||||
|
void* phys = alloc_page();
|
||||||
|
if (!phys) while(1); // Нет памяти
|
||||||
|
|
||||||
|
// Отображаем страницу: user, flags, present
|
||||||
|
pt[ptindex] = (uint32_t)phys | (flags & 0xFFF) | 0x5; // 0x5 = 101b (present, user)
|
||||||
|
|
||||||
|
// Сбрасываем TLB
|
||||||
|
asm volatile("invlpg (%0)" : : "r" (virtualaddr) : "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пример использования
|
||||||
|
void test_process() {
|
||||||
|
Process* proc = create_process(1); // Создаём процесс с PID 1
|
||||||
|
map_user_page(proc, (void*)0x1000, 0x6); // Отображаем страницу на 0x1000 (writable, user)
|
||||||
|
switch_to_process(proc); // Переключаемся на процесс
|
||||||
|
}
|
||||||
|
*/
|
||||||
Reference in New Issue
Block a user