diff --git a/Makefile b/Makefile index bb2a528..d055018 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,8 @@ OBJ = boot/boot.o \ kernel/pic.o \ kernel/string.o \ kernel/pmm.o \ + kernel/vmm.o \ + kernel/vmm_asm.o \ drivers/serial.o \ drivers/vga.o \ drivers/speaker.o \ @@ -52,6 +54,12 @@ kernel/gdt_asm.o: kernel/gdt.asm kernel/interrupt.o: kernel/interrupt.asm @echo "[+] Компилируем interrupt.asm..." $(AS) $(ASFLAGS) kernel/interrupt.asm -o kernel/interrupt.o + +# Компиляция ассемблера VMM +kernel/vmm_asm.o: kernel/vmm_asm.asm + @echo "[+] Компилируем vmm_asm.asm..." + $(AS) $(ASFLAGS) kernel/vmm_asm.asm -o kernel/vmm_asm.o + # Компиляция драйверов drivers/%.o: drivers/%.c @echo "[+] Компилируем драйвер: $<" diff --git a/README.md b/README.md index 699d429..1d2aa42 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## 2. Процессы, память, user mode, еще драйвера - [x] Физ. менеджер памяти -- [ ] Вирт. менеджер памяти +- [x] Вирт. менеджер памяти - [ ] Куча ядра - [ ] Потоки ядра - [ ] **VFS** diff --git a/kernel/kmain.c b/kernel/kmain.c index 2748ea6..33321ae 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -6,6 +6,7 @@ #include "gdt.h" #include "idt.h" #include "pmm.h" +#include "vmm.h" #include "pic.h" #include "logger.h" #include "string.h" @@ -50,33 +51,6 @@ void kernel_main(uint32_t magic, uint32_t mboot_addr) { mouse_init(); log_info(LOG_NORMAL, "Enabling PMM..."); pmm_init(mbi, (uint32_t)&end); - log_info(LOG_NORMAL, "Enabling IRQ..."); - __asm__ __volatile__("sti"); - vga_set_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK); - vga_print("h"); - speaker_beep(100, 10); - vga_print("a"); - speaker_beep(125, 10); - vga_print("i"); - speaker_beep(150, 10); - vga_print("!"); - speaker_beep(175, 10); - vga_print("!"); - speaker_beep(200, 10); - vga_print("!"); - speaker_beep(225, 10); - vga_print("!"); - speaker_beep(250, 10); - vga_print("!"); - speaker_beep(275, 10); - vga_print("!"); - speaker_beep(300, 10); - vga_print("!"); - speaker_beep(325, 10); - vga_print("\n # ### #### \n #### ### # ## ### #### # # # \n # # ##### ## ##### # # # # ### \n # # # # # # # # # # \n #### ### # ### #### ### #### \n # \n"); - speaker_beep(350, 400); - vga_init(); - vga_set_color(VGA_COLOR_LIGHT_GRAY, VGA_COLOR_BLACK); kprint_serial("--- Memory Map From GRUB ---"); struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr; @@ -147,6 +121,35 @@ void kernel_main(uint32_t magic, uint32_t mboot_addr) { kprint_serial(" MB\n"); kprint_serial("------------------------\n\n"); + log_info(LOG_NORMAL, "Enabling VMM..."); + vmm_init(); + log_info(LOG_NORMAL, "Enabling IRQ..."); + __asm__ __volatile__("sti"); + vga_set_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK); + vga_print("h"); + speaker_beep(100, 10); + vga_print("a"); + speaker_beep(125, 10); + vga_print("i"); + speaker_beep(150, 10); + vga_print("!"); + speaker_beep(175, 10); + vga_print("!"); + speaker_beep(200, 10); + vga_print("!"); + speaker_beep(225, 10); + vga_print("!"); + speaker_beep(250, 10); + vga_print("!"); + speaker_beep(275, 10); + vga_print("!"); + speaker_beep(300, 10); + vga_print("!"); + speaker_beep(325, 10); + vga_print("\n # ### #### \n #### ### # ## ### #### # # # \n # # ##### ## ##### # # # # ### \n # # # # # # # # # # \n #### ### # ### #### ### #### \n # \n"); + speaker_beep(350, 400); + vga_init(); + vga_set_color(VGA_COLOR_LIGHT_GRAY, VGA_COLOR_BLACK); while (1); } diff --git a/kernel/vmm.c b/kernel/vmm.c new file mode 100644 index 0000000..567772a --- /dev/null +++ b/kernel/vmm.c @@ -0,0 +1,67 @@ +#include "vmm.h" +#include "../drivers/serial.h" +#include "string.h" + +// Каталог страниц (1024 элемента по 4 байта = 4КБ) +static uint32_t page_directory[1024] __attribute__((aligned(4096))); + +// Первая таблица страниц для маппинга первых 4 Мегабайт памяти +static uint32_t first_page_table[1024] __attribute__((aligned(4096))); + +extern void vmm_enable_paging(uint32_t* directory); + +void vmm_init(void) { + kprint_serial("VMM: Initializing paging...\n"); + + // 1. Очищаем каталог страниц (ставим не-present) + // Ставим флаг 0x2 (Read/Write, но Not Present) + for (int i = 0; i < 1024; i++) { + page_directory[i] = 0x00000002; + } + + // 2. Делаем Identity Mapping для первых 4 МБ (0x00000000 - 0x003FFFFF) + // Пробегаемся по всей первой таблице страниц + for (uint32_t i = 0; i < 1024; i++) { + // Физический адрес = i * 4096 + // Флаги: Present (0x1) + Read/Write (0x2) + first_page_table[i] = (i * 4096) | 3; + } + + // 3. Засовываем первую таблицу страниц в первый элемент каталога + // Адрес таблицы | флаги Present + Read/Write + page_directory[0] = ((uint32_t)first_page_table) | 3; + + kprint_serial("VMM: Paging structures configured. Enabling CR0 bits...\n"); + + // 4. Прыгаем в ассемблер, чтобы загрузить CR3 и выставить флаг в CR0 + vmm_enable_paging(page_directory); + + kprint_serial("VMM: HOLY SHIT IT WORKS! Paging is active!\n"); +} + +void vmm_map_page(uint32_t phys, uint32_t virt, uint32_t flags) { + // Выковыриваем индексы из виртуального адреса + uint32_t pd_idx = virt >> 22; // Старшие 10 бит — индекс каталога + uint32_t pt_idx = (virt >> 12) & 0x3FF; // Средние 10 бит — индекс таблицы + + // Проверяем, создана ли таблица страниц для этого региона + // Наше ядро пока создало только одну таблицу для первых 4 МБ (pd_idx == 0) + if (!(page_directory[pd_idx] & 1)) { + // Если таблицы нет, по-хорошему надо выделить 4КБ через pmm_alloc_page(), + // очистить её и засунуть в каталог. Но пока выкинем ошибку для отладки: + kprint_serial("VMM ERROR: Page table not allocated for this virtual address!\n"); + return; + } + + // Достаем адрес таблицы страниц из каталога (обнуляем нижние 12 бит флагов) + uint32_t* table = (uint32_t*)(page_directory[pd_idx] & 0xFFFFF000); + + // Запихиваем физический адрес и флаги в таблицу страниц + // PAGE_SIZE у нас 4096, поэтому phys должен быть выровнен (нижние 12 бит затрутся флагами) + table[pt_idx] = (phys & 0xFFFFF000) | flags; + + // ВАЖНО: Процессор кэширует таблицы в TLB (Translation Lookaside Buffer). + // Если мы изменили маппинг существующей страницы, процессор об этом не узнает. + // Нужно сбросить кэш для этого адреса инструкцией invlpg: + __asm__ __volatile__("invlpg (%0)" : : "r"(virt) : "memory"); +} diff --git a/kernel/vmm.h b/kernel/vmm.h new file mode 100644 index 0000000..9270791 --- /dev/null +++ b/kernel/vmm.h @@ -0,0 +1,17 @@ +#ifndef VMM_H +#define VMM_H + +#include + +// Элемент каталога/таблицы страниц +#define PAGE_PRESENT 0x1 // Страница есть в памяти +#define PAGE_WRITE 0x2 // Можно писать (Read/Write) +#define PAGE_USER 0x4 // Доступно из Ring 3 (User mode) + +// Инициализация страничного обмена +void vmm_init(void); + +// Отобразить виртуальный адрес на физический +void vmm_map_page(uint32_t phys, uint32_t virt, uint32_t flags); + +#endif diff --git a/kernel/vmm_asm.asm b/kernel/vmm_asm.asm new file mode 100644 index 0000000..97ddf2b --- /dev/null +++ b/kernel/vmm_asm.asm @@ -0,0 +1,20 @@ +global vmm_enable_paging + +section .text + +vmm_enable_paging: + push ebp + mov ebp, esp + + ; 1. Загружаем адрес каталога страниц в регистр CR3 + mov eax, [ebp + 8] + mov cr3, eax + + ; 2. Включаем пейджинг: выставляем 31-й бит (PG) в регистре CR0 + mov eax, cr0 + or eax, 0x80000000 + mov cr0, eax + + mov esp, ebp + pop ebp + ret