BMOSP/kernel/mem.c
Aren Elchinyan 1c408d21d8 Небольшие исправления комментариев
В рамках ревью pull-а #15
2024-09-21 21:53:08 +03:00

373 lines
11 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* mem.c
* Функции управления памятью
*
* Основной функционал менеджера памяти
*
*/
#include <fb.h>
#include <limine.h>
#include <lock.h>
#include <log.h>
#include <mem.h>
#include <stdbool.h>
#include <tool.h>
// Запрос карты памяти от загрузчика
static volatile struct limine_memmap_request memmap_request = { .id = LIMINE_MEMMAP_REQUEST,
.revision = 0,
.response = (struct limine_memmap_response *)0 };
// Запрос к загрузчику Limine о виртуальном адресе Higher Half
static volatile struct limine_hhdm_request hhdm_request = { .id = LIMINE_HHDM_REQUEST,
.revision = 0,
.response = (struct limine_hhdm_response *)0 };
// Менеджер памяти выделяет память отдельными участками (блоками). Информация обо
// всех выделенных блоках хранится в виде односвязного списка структур mem_entry_t.
// Каждая mem_entry_t содержит информацию об отдельном выделенном блоке памяти
struct mem_entry {
// Ссылка на следующий элемент списка
struct mem_entry *next;
// Флаг, помечающий память как освобожденную
bool free;
// Поток, владеющий памятью
uint64_t task_id;
// Размер блока
uint64_t size;
// Адрес блока
uint8_t data[0];
};
typedef struct mem_entry mem_entry_t;
// Битовая карта для отслеживания занятых и свободных фреймов памяти
static uint8_t *bitmap;
// Объем доступных блоков
static uint64_t bitmap_available = 0;
// Объем блоков
static uint64_t bitmap_limit = 0;
// Верхняя граница доступной памяти
static uint64_t limit;
// Объем всего доступного физического адресного пространства
static uint64_t usable = 0;
// Объем доступной виртуальной памяти
static uint64_t available = 0;
// Наивысший адрес в available space
static uint64_t highest = 0;
// Количество записей в карте памяти
static uint64_t mmmap_count = 0;
extern task_t *current_task;
extern uint64_t full_init;
static const char memory_types[8][82] = { "Доступно", "Зарезервировано", "ACPI, можно освободить",
"ACPI NVS", "Плохая память", "Загрузчик, можно освободить",
"Ядро и модули", "Буфер кадра" };
// Ответ от загрузчика Limine о состоянии памяти
static struct limine_memmap_response *memmap_response;
// Описание перого выделенного блока памяти - начало списка с данными о памяти
static mem_entry_t *first_node;
void mem_dump_memory( ) {
mem_entry_t *curr = first_node;
// Проход по всему списку записей о выделенных блоках памяти
while (curr) {
if (curr->next) {
LOG("->0x%x | %u килобайт | %s | 0x%x | поток %u\n", &curr->data, (curr->size) / 1024,
curr->free ? memory_types[0] : memory_types[1], curr->next, curr->task_id);
} else {
LOG("->0x%x | %u килобайт | %s | поток %u | Это последний блок\n", &curr->data, (curr->size) / 1024,
curr->free ? memory_types[0] : memory_types[1], curr->task_id);
}
curr = curr->next;
}
}
void mem_get_stat( ) {
size_t free_mem = 0;
size_t used_mem = 0;
struct mem_entry *current_entry = first_node;
while (current_entry) {
if (current_entry->free) {
free_mem += current_entry->size;
} else {
used_mem += current_entry->size;
}
current_entry = current_entry->next;
}
LOG("Свободно: %u мегабайт\n", free_mem / 1024 / 1024);
LOG("Занято: %u мегабайт\n", used_mem / 1024 / 1024);
}
void mem_check_dynamic_memory( ) {
mem_entry_t *curr = first_node;
uint64_t free_mem = 0;
while (curr) {
if (curr->free) { free_mem += curr->size; }
curr = curr->next;
}
if (free_mem < 1024 * BLOCK_SIZE) {
void *ptr = mem_frame_alloc(1024);
if (ptr == NULL) {
LOG("Память кончилась!\n");
return;
}
mem_add_block(ptr, 1024 * BLOCK_SIZE);
mem_merge_all_blocks( );
}
}
void mem_frame_free(void *addr, uint64_t frames) {
// Проход по фреймам памяти и очистка битов в битовой карте
uint64_t frame = (uint64_t)addr / BLOCK_SIZE;
for (uint64_t i = frame; i < frames + frame; i++) { BIT_CLEAR(i); }
bitmap_available += frames;
}
// Функция выделения памяти
void *mem_frame_alloc(uint64_t wanted_frames) {
void *addr;
uint64_t available_frames = 0;
for (uint64_t frame = 1; frame < limit; frame++) {
if (!BIT_GET(frame)) {
available_frames++;
} else if (available_frames != wanted_frames) {
available_frames = 0;
continue;
}
if (available_frames == wanted_frames) {
uint64_t i;
for (i = 0; i < wanted_frames; i++) { BIT_SET(frame - i); }
frame -= i - 1;
addr = (void *)(BLOCK_SIZE * frame);
bitmap_available -= wanted_frames;
return addr;
}
}
return NULL;
}
void *mem_frame_calloc(uint64_t frames) {
void *addr = mem_frame_alloc(frames);
tool_memset(addr + HHDM_OFFSET, 0, frames * BLOCK_SIZE);
return addr;
}
static void merge_blocks(mem_entry_t *start) {
if (!start->free) return;
mem_entry_t *block = start;
while (block->next && block->next->free) {
block->size += block->next->size + sizeof(mem_entry_t);
if (block->next->next) {
block->next = block->next->next;
continue;
}
block->next = NULL;
}
}
void mem_merge_all_blocks( ) {
mem_entry_t *curr = first_node;
while (curr) {
merge_blocks(curr);
curr = curr->next;
}
}
void mem_add_block(void *addr, size_t size) {
mem_entry_t *new_entry = (mem_entry_t *)addr;
new_entry->size = size - sizeof(mem_entry_t);
new_entry->free = true;
if (first_node == NULL) {
first_node = new_entry;
new_entry->next = NULL;
} else {
mem_entry_t *curr = first_node;
while (curr->next != NULL) { curr = curr->next; }
curr->next = new_entry;
new_entry->next = NULL;
}
}
static void alloc_init(void *address, size_t length) {
first_node = (mem_entry_t *)address;
first_node->size = length - sizeof(mem_entry_t);
first_node->free = true;
first_node->next = NULL;
}
static void *alloc_align(size_t size, size_t alignment) {
mem_entry_t *curr = first_node;
while (curr) {
if (curr->free && curr->size >= (alignment + sizeof(mem_entry_t) + size)) {
void *addr = curr->data + alignment - 1;
addr -= (uintptr_t)addr % alignment + sizeof(mem_entry_t);
mem_entry_t *second = (mem_entry_t *)addr;
mem_entry_t *third = (mem_entry_t *)(second->data + size);
tool_memset(third, 0, sizeof(mem_entry_t));
third->size = curr->size - (third->data - curr->data);
third->next = curr->next;
third->free = 1;
second->size = size;
second->next = third;
second->free = 0;
second->task_id = 0;
if (task_f_init) { second->task_id = current_task->id; }
if (curr != second) {
curr->next = second;
curr->size = (uintptr_t)second - (uintptr_t)curr->data;
curr->free = 1;
}
return second->data;
}
curr = curr->next;
}
return NULL;
}
void *mem_alloc(size_t size) {
mem_check_dynamic_memory( );
void *data = alloc_align(size, 1);
return data;
}
void mem_free(void *addr) {
mem_entry_t *curr = first_node, *prev = NULL;
while (curr != NULL) {
if (curr->data == addr) {
curr->free = 1;
merge_blocks(prev ? prev : curr);
mem_merge_all_blocks( );
return;
}
prev = curr;
curr = curr->next;
}
}
void *mem_realloc(void *addr, size_t size) {
if (size == 0) {
mem_free(addr);
return NULL;
}
if (addr == NULL) { return mem_alloc(size); }
void *new_addr = mem_alloc(size);
if (new_addr == NULL) { return NULL; }
tool_memcpy(new_addr, addr, size);
mem_free(addr);
return new_addr;
}
// Инициализация менеджера памяти
void mem_init( ) {
// Получение информации о доступной памяти из Limine bootloader
memmap_response = memmap_request.response;
mmmap_count = memmap_response->entry_count;
struct limine_memmap_entry **mmaps = memmap_response->entries;
#ifdef DEBUG_MEM
LOG("Записей в карте памяти: %u\n", memmap_response->entry_count);
#endif
// Обработка каждой записи в карте памяти
for (uint64_t i = 0; i < mmmap_count; i++) {
available += mmaps[i]->length;
// LOG("\t%d: 0x%x\tдлина: 0x%x\tтип: %s\n", i + 1, mmaps[i]->base, mmaps[i]->length,
// memory_types[mmaps[i]->type]);
if (mmaps[i]->type == LIMINE_MEMMAP_FRAMEBUFFER) {
#ifdef DEBUG_MEM
LOG("На видеопамять BIOS/UEFI выделено: %u мегабайт + %u "
"килобайт\n",
mmaps[i]->length / 1024 / 1024, (mmaps[i]->length / 1024) % 1024);
#endif
}
if (!(mmaps[i]->type == LIMINE_MEMMAP_USABLE)) { continue; }
usable += mmaps[i]->length;
uint64_t top = mmaps[i]->base + mmaps[i]->length;
if (top > highest) highest = top;
}
limit = highest / BLOCK_SIZE;
uint64_t bitmap_size = ALIGN_UP(highest / BLOCK_SIZE / 8, BLOCK_SIZE);
// Находим доступное место для битовой карты и устанавливаем ее
for (uint64_t i = 0; i < mmmap_count; i++) {
if (!mmaps[i]->type == LIMINE_MEMMAP_USABLE) continue;
if (mmaps[i]->length >= bitmap_size) {
bitmap = (uint8_t *)mmaps[i]->base;
tool_memset(bitmap, 0xFF, bitmap_size);
mmaps[i]->length -= bitmap_size;
mmaps[i]->base += bitmap_size;
available -= bitmap_size;
break;
}
}
// Освобождаем все доступные фреймы памяти
for (uint64_t i = 0; i < mmmap_count; i++) {
for (uint64_t t = 0; t < mmaps[i]->length; t += BLOCK_SIZE) { bitmap_limit++; }
if (!(mmaps[i]->type == LIMINE_MEMMAP_USABLE)) { continue; }
for (uint64_t t = 0; t < mmaps[i]->length; t += BLOCK_SIZE) { mem_frame_free((void *)mmaps[i]->base + t, 1); }
}
#ifdef DEBUG_MEM
LOG("%u / %u блоков доступно\n", bitmap_available, bitmap_limit);
LOG("Размер битовой карты: %u\n", bitmap_size);
#endif
alloc_init(mem_frame_alloc(1024), 1024 * BLOCK_SIZE);
#ifdef DEBUG_MEM
LOG("%u мегабайт выделено в динамичную память\n", (256 * 16 * BLOCK_SIZE + BLOCK_SIZE) / 1024 / 1024);
#endif
// Выделяем по 4 мегабайта в аллокатор динамичной памяти
for (uint64_t i = 0; i < 32; i += 8) { mem_add_block(mem_frame_alloc(1024), 1024 * BLOCK_SIZE); }
mem_merge_all_blocks( );
mem_dump_memory( );
#ifdef DEBUG_MEM
LOG("%u МБ объем доступной памяти, %u МБ объем виртуальной памяти\n", (bitmap_available * BLOCK_SIZE) / 1024 / 1024,
available / 1024 / 1024);
LOG("%u / %u блоков доступно\n", bitmap_available, bitmap_limit);
#endif
}