119 lines
4.7 KiB
C
119 lines
4.7 KiB
C
#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;
|
||
}
|