После включение paging'а, ядру нужен доступ к физической памяти. Текущая реализация HeLL OS маппит первые 4 мегабайта физической памяти в верхние адреса ядра (higher-half kernel), начиная с адреса 0x80000000
. Чтобы не усложнять вещи, мы будем работать в предположении, что вы можете выделить ядерные виртуальные адреса для всей доступной оперативной памяти. Однако в реальности это не так, поэтому в Linux используется следующий трюк: память разбивается на low и high, lowmem маппится полностью в пространство ядра — это адреса, к которым ядро часто обращается (например, DMA). Доступ к верхним адресам требует постоянных изменений page directory/page table, то есть вы сначала подмапливаете страницу, изменяете данные на ней, а затем отдаёте обратно ядру её виртуальный адрес для переиспользования. Почитать можно тут: раз, два, три, четыре и пять.
Цель: всё, что вам нужно сделать — это расширить диапазон адресов с 4 Мб до максимально доступного адреса оперативной памяти и добавить его в аллокатор. Его можно получить из инфорации, передаваемой Multiboot-загрузчиком. Парсинг этой информации у вас должен был быть реализован на прошлом шаге. Hint: используйте страницы размером 4 Мб — они позволят писать меньше кода, и будут работать быстрее, потому что будет меньше TLB misses.
В HeLL OS реализован простейший аллокатор фреймов, основнный на списке свободных страниц. kalloc
выделяет фрейм и возвращает его вирутальный адрес, а затем с помощью map_continuous
выделяется виртуальный адрес. Однако, аллокатор обладает рядом недостатков. Во-первых, он не умеет освобождать память. Во-вторых, ядру иногда нужно будет выделять не одну страницу, а сразу несколько (например, под стек ядра или для mmap
). Во-третьих, в следствии фрагментации физической памяти не всегда возможно выделить последовательные физические адреса заданного размера.
Цель: вам нужно будет чуть улучшить kalloc
так, чтобы он умел выделять (и освобождать) свободный диапазон виртуальных адресов заданного размера в текущем адресном пространстве и мапить его в нужное количество фреймов. Учтите, что в будущем потребуется выделять пользовательские и ядерные адреса в разных половинах адресного пространства.
Hint: как вариант, вы можете реализовать следующий интерфейс:
void* kalloc(size_t size, int flags)
, аflags
будет принимать флагиALLOC_USER | ALLOC_KERNEL | ALLOC_WRITABLE
.void kfree(void*, size_t size)
Hint: одна из оптимизаций, которую вы можете применить — не выделять виртуальный адрес для одиночных ядерных страниц, потому что они все уже будут иметь адрес после первого пункта.