From ea0a395cc51a6b58cb8de0e6ae7f8a7b1981f464 Mon Sep 17 00:00:00 2001 From: msh356 Date: Fri, 3 Jul 2026 13:59:54 +0300 Subject: [PATCH] =?UTF-8?q?KERNEL:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=84=D0=B8=D0=B7=D0=B8=D1=87=D0=B5=D1=81=D0=BA?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=BC=D0=B5=D0=BD=D0=B5=D0=B4=D0=B6=D0=B5=D1=80?= =?UTF-8?q?=20=D0=BF=D0=B0=D0=BC=D1=8F=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 3 +- README.md | 2 +- boot/boot.asm | 3 ++ kernel/kmain.c | 92 ++++++++++++++++++++++++++++++++++- kernel/multiboot.h | 47 ++++++++++++++++++ kernel/pmm.c | 119 +++++++++++++++++++++++++++++++++++++++++++++ kernel/pmm.h | 30 ++++++++++++ linker.ld | 1 + 8 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 kernel/multiboot.h create mode 100644 kernel/pmm.c create mode 100644 kernel/pmm.h diff --git a/Makefile b/Makefile index 540ff0c..bb2a528 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ CC = gcc -CFLAGS = -m32 -ffreestanding -O2 -Wall -Wextra -Iinclude -fno-stack-protector +CFLAGS = -m32 -nostdlib -fno-builtin -fno-stack-protector -ffreestanding -mno-red-zone -mno-mmx -mno-sse -mno-sse2 AS = nasm ASFLAGS = -f elf32 @@ -21,6 +21,7 @@ OBJ = boot/boot.o \ kernel/io.o \ kernel/pic.o \ kernel/string.o \ + kernel/pmm.o \ drivers/serial.o \ drivers/vga.o \ drivers/speaker.o \ diff --git a/README.md b/README.md index c1204c7..699d429 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ ## 2. Процессы, память, user mode, еще драйвера -- [ ] Физ. менеджер памяти +- [x] Физ. менеджер памяти - [ ] Вирт. менеджер памяти - [ ] Куча ядра - [ ] Потоки ядра diff --git a/boot/boot.asm b/boot/boot.asm index 546d69f..f634ab0 100644 --- a/boot/boot.asm +++ b/boot/boot.asm @@ -15,6 +15,9 @@ extern kernel_main ; Наша функция из Си-файла _start: mov esp, stack_space ; Выделяем указатель на стек + ; Передаем параметры от GRUB в Си-код по стандарту cdecl + push ebx ; Аргумент 2: Адрес структуры Multiboot + push eax ; Аргумент 1: Магическое число Multiboot call kernel_main ; Прыгаем в Си! jmp $ ; Если Си-функция вернется — виснем в вечном цикле diff --git a/kernel/kmain.c b/kernel/kmain.c index 7734e30..2748ea6 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -5,12 +5,27 @@ #include "../drivers/speaker.h" #include "gdt.h" #include "idt.h" +#include "pmm.h" #include "pic.h" #include "logger.h" +#include "string.h" +#include "multiboot.h" -void kernel_main(void) { +extern uint32_t end; // Символ 'end' прописан в конце любого стандартного linker.ld +uint32_t kernel_end = (uint32_t)&end; + +void kernel_main(uint32_t magic, uint32_t mboot_addr) { log_info(LOG_NORMAL, "Initializing serial..."); serial_init(); + if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { + log_error(LOG_NORMAL, "fuck, i got invalid multiboot magic. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?"); + while(1); + } + struct multiboot_info* mbi = (struct multiboot_info*)mboot_addr; + if (!(mbi->flags & (1 << 6))) { + log_error(LOG_NORMAL, "fuck, i haven't got a memory map. IS THAT REALLY THE MULTIBOOT YOU TALKED ABOUT?"); + while(1); + } log_info(LOG_NORMAL, "Initializing VGA..."); vga_init(); log_info(LOG_NORMAL, "Setting color white on black"); @@ -31,8 +46,10 @@ void kernel_main(void) { pic_clear_mask(12); log_info(LOG_NORMAL, "Registering timer at 100Hz"); timer_init(100); - log_info(LOG_NORMAL, "Registering mouse"); + log_info(LOG_NORMAL, "Registering mouse"); 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); @@ -60,5 +77,76 @@ void kernel_main(void) { 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; + + while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) { + char addr_buf[32]; + char len_buf[32]; + + itoa((uint32_t)mmap->addr, addr_buf, 16); + itoa((uint32_t)mmap->len, len_buf, 10); + + kprint_serial("Region: Base=0x"); + kprint_serial(addr_buf); + kprint_serial(" Size="); + kprint_serial(len_buf); + + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + kprint_serial(" [FREE RAM]\n"); + } else { + kprint_serial(" [RESERVED]\n"); + } + + // Переходим к следующей записи (учитываем поле size) + mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size)); + } + kprint_serial("--- trying to allocate some fucking memory... ---\n"); + kprint_serial("page 1... "); + void* p1 = pmm_alloc_page(); + kprint_serial("page 2..."); + void* p2 = pmm_alloc_page(); + kprint_serial("ok!"); + char buf[32]; + kprint_serial("PMM TEST: Page 1 allocated at 0x"); + itoa((uint32_t)p1, buf, 16); + kprint_serial(buf); + kprint_serial("\n"); + + kprint_serial("PMM TEST: Page 2 allocated at 0x"); + itoa((uint32_t)p2, buf, 16); + kprint_serial(buf); + kprint_serial("\n"); + + kprint_serial("freeing the pages back... page 1... "); + pmm_free_page(p1); + kprint_serial("page 2... "); + pmm_free_page(p2); + kprint_serial("it don't died!\n"); + + kprint_serial("\n--- PMM MEMORY STATS ---\n"); + + pmm_get_stats(); // Просто запускаем сбор, она запишет всё в глобальную переменную + + // Берем указатель на глобальную переменную + pmm_stats_t* my_stats = pmm_get_stats_ptr(); + + kprint_serial("Total Memory: "); + itoa(my_stats->total_memory >> 20, buf, 10); + kprint_serial(buf); + kprint_serial(" MB\n"); + + kprint_serial("Used Memory: "); + itoa(my_stats->used_memory >> 10, buf, 10); + kprint_serial(buf); + kprint_serial(" KB\n"); + + kprint_serial("Free Memory: "); + itoa(my_stats->free_memory >> 20, buf, 10); + kprint_serial(buf); + kprint_serial(" MB\n"); + + kprint_serial("------------------------\n\n"); + while (1); } diff --git a/kernel/multiboot.h b/kernel/multiboot.h new file mode 100644 index 0000000..bc41b3a --- /dev/null +++ b/kernel/multiboot.h @@ -0,0 +1,47 @@ +#ifndef MULTIBOOT_H +#define MULTIBOOT_H + +#include + +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +struct multiboot_mmap_entry { + uint32_t size; + uint64_t addr; + uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + uint32_t type; +} __attribute__((packed)); + +struct multiboot_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + uint32_t num; + uint32_t size; + uint32_t addr; + uint32_t shndx; + uint32_t mmap_length; + uint32_t mmap_addr; // <-- Вот это нам нужно! Адрес карты памяти + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +} __attribute__((packed)); + +#endif diff --git a/kernel/pmm.c b/kernel/pmm.c new file mode 100644 index 0000000..9979bd6 --- /dev/null +++ b/kernel/pmm.c @@ -0,0 +1,119 @@ +#include "pmm.h" +#include "string.h" +#include "logger.h" +#include "../drivers/serial.h" + +// Указатель на начало нашей битовой карты в памяти +static uint32_t* pmm_bitmap = NULL; +static uint32_t pmm_bitmap_size = 0; +static uint32_t total_pages = 0; + +static pmm_stats_t global_stats; + +// Вспомогательные инлайны для быстрой работы с битами ядра +static inline void bitmap_set(uint32_t page_index) { + pmm_bitmap[page_index / 32] |= (1 << (page_index % 32)); +} + +static inline void bitmap_clear(uint32_t page_index) { + pmm_bitmap[page_index / 32] &= ~(1 << (page_index % 32)); +} + +static inline int bitmap_test(uint32_t page_index) { + return (pmm_bitmap[page_index / 32] & (1 << (page_index % 32))) != 0; +} + +void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end) { + // Рассчитываем менеджер на 128 МБ памяти + uint32_t mem_size = 128 * 1024 * 1024; + total_pages = mem_size / PAGE_SIZE; + + // Саму карту пихаем прямо за концом ядра (куда указывает линкер) + pmm_bitmap = (uint32_t*)kernel_end; + pmm_bitmap_size = total_pages / 8; // Размер карты в байтах (4096 байт) + + uint32_t bitmap_end = kernel_end + pmm_bitmap_size; + + // Шаг 1: Помечаем вообще всё как занятое (1). Безопасность превыше всего. + memset(pmm_bitmap, 0xFF, pmm_bitmap_size); + + // Шаг 2: Парсим карту памяти от GRUB и открываем доступные регионы + struct multiboot_mmap_entry* mmap = (struct multiboot_mmap_entry*)mbi->mmap_addr; + + while((uint32_t)mmap < mbi->mmap_addr + mbi->mmap_length) { + // Если GRUB говорит, что этот кусок RAM свободен для юзания + if (mmap->type == MULTIBOOT_MEMORY_AVAILABLE) { + uint32_t start_addr = (uint32_t)mmap->addr; + uint32_t end_addr = start_addr + (uint32_t)mmap->len; + + for (uint32_t addr = start_addr; addr < end_addr; addr += PAGE_SIZE) { + uint32_t page_idx = addr / PAGE_SIZE; + if (page_idx < total_pages) { + bitmap_clear(page_idx); // Ставим 0 (страница свободна) + } + } + } + // Шагаем к следующей записи в таблице Multiboot + mmap = (struct multiboot_mmap_entry*)((uint32_t)mmap + mmap->size + sizeof(mmap->size)); + } + + // Шаг 3: Жестко защищаем саму ОС! + // Всё, что лежит от физического адреса 0x0 до bitmap_end (код ядра, стек, сама битовая карта), + // мы принудительно забиваем единицами, чтобы аллокатор туда никогда не залез. + for (uint32_t addr = 0; addr < bitmap_end; addr += PAGE_SIZE) { + bitmap_set(addr / PAGE_SIZE); + } + + log_info(LOG_NORMAL, "pmm works."); +} + +void* pmm_alloc_page(void) { + // Тупо линейно ищем первый попавшийся бит, равный 0 + for (uint32_t i = 0; i < total_pages; i++) { + if (!bitmap_test(i)) { + bitmap_set(i); // Застолбили страницу (поставили 1) + return (void*)(i * PAGE_SIZE); // Считаем её реальный физический адрес + } + } + + log_error(LOG_NORMAL, "PMM: FUCK, WHERE IS ALL OUR FUCKING MEMORY?!"); + return NULL; +} + +void pmm_free_page(void* addr) { + uint32_t page_idx = ((uint32_t)addr) / PAGE_SIZE; + if (page_idx < total_pages) { + bitmap_clear(page_idx); // Сбросили в 0, страница снова чиста + } +} + +void pmm_get_stats(void) { + kprint_serial("Someone wants memory stats! Running the test!\n"); + uint32_t used = 0; + uint32_t free = 0; + + for (uint32_t i = 0; i < total_pages; i++) { + if (bitmap_test(i)) { + used++; + } else { + free++; + } + } + + kprint_serial("Okay, i am collected the stats. "); + + // Пишем в ГЛОБАЛЬНУЮ переменную + global_stats.total_pages = total_pages; + global_stats.used_pages = used; + global_stats.free_pages = free; + global_stats.total_memory = total_pages << 12; + global_stats.used_memory = used << 12; + global_stats.free_memory = free << 12; + + kprint_serial("Goodbye!\n"); +} + +// Добавим функцию-геттер, которая просто вернет указатель на эту глобальную память +pmm_stats_t* pmm_get_stats_ptr(void) { + return &global_stats; +} diff --git a/kernel/pmm.h b/kernel/pmm.h new file mode 100644 index 0000000..689176b --- /dev/null +++ b/kernel/pmm.h @@ -0,0 +1,30 @@ +#ifndef PMM_H +#define PMM_H + +#include +#include "multiboot.h" + +#define PAGE_SIZE 4096 + +// Инициализация: передаем инфу от GRUB и адрес конца ядра из линкера +void pmm_init(struct multiboot_info* mbi, uint32_t kernel_end); + +// Выделить одну страницу памяти (возвращает физический адрес) +void* pmm_alloc_page(void); + +// Освободить страницу памяти по адресу +void pmm_free_page(void* addr); + +typedef struct { + uint32_t total_memory; // Всего памяти в байтах + uint32_t used_memory; // Занято в байтах + uint32_t free_memory; // Свободно в байтах + uint32_t total_pages; // Всего страниц + uint32_t used_pages; // Занято страниц + uint32_t free_pages; // Свободно страниц +} pmm_stats_t; + +void pmm_get_stats(void); +pmm_stats_t* pmm_get_stats_ptr(void); + +#endif diff --git a/linker.ld b/linker.ld index 8c3c4d9..fba6f2a 100644 --- a/linker.ld +++ b/linker.ld @@ -30,4 +30,5 @@ SECTIONS *(COMMON) *(.bss) } + end = .; }