KERNEL: добавлен физический менеджер памяти

This commit is contained in:
msh356 2026-07-03 13:59:54 +03:00
parent a66b0416d5
commit ea0a395cc5
8 changed files with 293 additions and 4 deletions

View file

@ -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 \

View file

@ -19,7 +19,7 @@
## 2. Процессы, память, user mode, еще драйвера
- [ ] Физ. менеджер памяти
- [x] Физ. менеджер памяти
- [ ] Вирт. менеджер памяти
- [ ] Куча ядра
- [ ] Потоки ядра

View file

@ -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 $ ; Если Си-функция вернется — виснем в вечном цикле

View file

@ -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);
}

47
kernel/multiboot.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef MULTIBOOT_H
#define MULTIBOOT_H
#include <stdint.h>
#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

119
kernel/pmm.c Normal file
View file

@ -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;
}

30
kernel/pmm.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef PMM_H
#define PMM_H
#include <stdint.h>
#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

View file

@ -30,4 +30,5 @@ SECTIONS
*(COMMON)
*(.bss)
}
end = .;
}