Files
2020-09-18 14:23:20 +03:00

352 lines
10 KiB
C

#include "arch/amd64/mm/phys.h"
#include "arch/amd64/mm/pool.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/debug.h"
#include "sys/string.h"
#include "sys/spin.h"
#include "sys/mem/phys.h"
#include "sys/mm.h"
#define PHYS_MAX_PAGES ((1U << 30) / 0x1000)
// Reserve 1MiB at bottom
#define LOW_BOUND 0x100000
#define MMAP_KIND_RESERVED 0
#define MMAP_KIND_USABLE 1
#define MMAP_KIND_UNKNOWN 2
struct mmap_iter {
const struct mm_phys_memory_map *map;
size_t position, limit;
};
struct page *mm_pages = NULL;
static size_t _total_pages, _pages_free;
static size_t _alloc_pages[_PU_COUNT];
static spin_t phys_spin = 0;
static struct mm_phys_reserved phys_reserve_mm_pages,
phys_reserve_mmap;
static LIST_HEAD(reserved_regions);
static int is_reserved(uintptr_t addr) {
struct mm_phys_reserved *res;
list_for_each_entry(res, &reserved_regions, link) {
if (addr >= res->begin && addr < res->end) {
return 1;
}
}
return 0;
}
// EFI memory map
struct efi_mmap_entry {
uint32_t type;
uintptr_t physical_start;
uintptr_t virtual_start;
uint64_t number_of_pages;
uint64_t attribute;
};
enum efi_mmap_type {
efi_reserved_memory_type,
efi_loader_code,
efi_loader_data,
efi_boot_services_code,
efi_boot_services_data,
efi_runtime_services_code,
efi_runtime_services_data,
efi_conventional_memory,
efi_unusable_memory,
efi_acpi_reclaim_memory,
efi_acpi_memory_nvs,
efi_memory_mapped_io,
efi_memory_mapped_io_portspace,
efi_pal_code,
efi_persistent_memory,
efi_max_memory_type
};
static void mmap_iter_init(const struct mm_phys_memory_map *mmap,
struct mmap_iter *iter) {
_assert(mmap->format == MM_PHYS_MMAP_FMT_YBOOT ||
mmap->format == MM_PHYS_MMAP_FMT_MULTIBOOT2);
iter->map = mmap;
iter->limit = mmap->entry_count;
iter->position = 0;
}
static int mmap_iter_next(struct mmap_iter *iter,
int *kind,
uintptr_t *base,
size_t *size) {
const struct mm_phys_memory_map *map;
map = iter->map;
_assert(map);
if (iter->position == iter->limit) {
return 0;
}
switch (map->format) {
case MM_PHYS_MMAP_FMT_YBOOT:
{
struct efi_mmap_entry *ent;
ent = map->address + iter->position * map->entry_size;
*base = ent->physical_start;
*size = ent->number_of_pages * 0x1000;
switch (ent->type) {
case efi_loader_code:
case efi_loader_data:
case efi_boot_services_code:
case efi_boot_services_data:
case efi_runtime_services_code:
case efi_runtime_services_data:
case efi_conventional_memory:
*kind = MMAP_KIND_USABLE;
break;
case efi_pal_code:
case efi_persistent_memory:
case efi_reserved_memory_type:
case efi_unusable_memory:
case efi_acpi_reclaim_memory:
case efi_acpi_memory_nvs:
case efi_memory_mapped_io_portspace:
case efi_memory_mapped_io:
*kind = MMAP_KIND_RESERVED;
break;
default:
panic("Unknown ent type: %02x\n", ent->type);
}
}
break;
case MM_PHYS_MMAP_FMT_MULTIBOOT2:
{
struct multiboot_mmap_entry *ent;
ent = map->address + iter->position * map->entry_size;
*base = ent->addr;
*size = ent->len;
if (ent->type == MULTIBOOT_MEMORY_AVAILABLE) {
*kind = MMAP_KIND_USABLE;
} else {
*kind = MMAP_KIND_RESERVED;
}
}
break;
default:
panic("Impossible\n");
}
++iter->position;
return 1;
}
void mm_phys_reserve(const char *use, struct mm_phys_reserved *res) {
list_head_init(&res->link);
list_add(&res->link, &reserved_regions);
kdebug("#### Reserve region (%s): %p .. %p\n", use, res->begin, res->end);
}
void mm_phys_stat(struct mm_phys_stat *st) {
st->pages_total = _total_pages;
st->pages_free = _pages_free;
st->pages_used_kernel = _alloc_pages[PU_KERNEL];
st->pages_used_user = _alloc_pages[PU_PRIVATE];
st->pages_used_shared = _alloc_pages[PU_SHARED];
st->pages_used_paging = _alloc_pages[PU_PAGING];
st->pages_used_cache = _alloc_pages[PU_CACHE];
}
uint64_t *amd64_mm_pool_alloc(void) {
uint64_t *table;
uintptr_t ptr;
ptr = mm_phys_alloc_page(PU_PAGING);
_assert(ptr != MM_NADDR);
table = (uint64_t *) MM_VIRTUALIZE(ptr);
memset(table, 0, MM_PAGE_SIZE);
return table;
}
void amd64_mm_pool_free(uint64_t *p) {
memset(p, 0xFF, MM_PAGE_SIZE);
mm_phys_free_page(MM_PHYS(p));
}
uintptr_t mm_phys_alloc_page(enum page_usage pu) {
_assert(pu < _PU_COUNT && pu != PU_UNKNOWN);
uintptr_t irq;
spin_lock_irqsave(&phys_spin, &irq);
for (size_t i = LOW_BOUND >> 12; i < PHYS_MAX_PAGES; ++i) {
struct page *pg = &mm_pages[i];
if (!(pg->flags & PG_ALLOC)) {
_assert(pg->usage == PU_UNKNOWN);
_assert(pg->refcount == 0);
pg->usage = pu;
pg->flags |= PG_ALLOC;
++_alloc_pages[pu];
_assert(_pages_free);
--_pages_free;
spin_release_irqrestore(&phys_spin, &irq);
return i * MM_PAGE_SIZE;
}
}
spin_release_irqrestore(&phys_spin, &irq);
return MM_NADDR;
}
void mm_phys_free_page(uintptr_t addr) {
uintptr_t irq;
spin_lock_irqsave(&phys_spin, &irq);
struct page *pg = PHYS2PAGE(addr);
_assert(pg->refcount == 0);
_assert(pg->flags & PG_ALLOC);
_assert(_alloc_pages[pg->usage]);
--_alloc_pages[pg->usage];
++_pages_free;
pg->flags &= ~PG_ALLOC;
pg->usage = PU_UNKNOWN;
spin_release_irqrestore(&phys_spin, &irq);
}
uintptr_t mm_phys_alloc_contiguous(size_t count, enum page_usage pu) {
uintptr_t irq;
spin_lock_irqsave(&phys_spin, &irq);
for (size_t i = LOW_BOUND >> 12; i < PHYS_MAX_PAGES - count; ++i) {
for (size_t j = 0; j < count; ++j) {
if (mm_pages[i + j].flags & PG_ALLOC) {
goto fail;
}
}
for (size_t j = 0; j < count; ++j) {
_assert(!mm_pages[i + j].refcount);
mm_pages[i + j].flags |= PG_ALLOC;
mm_pages[i + j].usage = pu;
++_alloc_pages[pu];
_assert(_pages_free);
--_pages_free;
}
spin_release_irqrestore(&phys_spin, &irq);
return i * MM_PAGE_SIZE;
fail:
continue;
}
spin_release_irqrestore(&phys_spin, &irq);
return MM_NADDR;
}
static uintptr_t place_mm_pages(const struct mm_phys_memory_map *mmap, size_t req_count) {
struct mmap_iter iter;
size_t item_offset;
uintptr_t base;
size_t size;
int kind;
mmap_iter_init(mmap, &iter);
// TODO: merge two consecutive entries into a single address block
while (mmap_iter_next(&iter, &kind, &base, &size)) {
uintptr_t page_aligned_begin = (base + 0xFFF) & ~0xFFF;
uintptr_t page_aligned_end = (base + size) & ~0xFFF;
if (kind == MMAP_KIND_USABLE && page_aligned_end > page_aligned_begin) {
// Something like mm_phys_alloc_contiguous does, but
// we don't yet have it obviously
size_t collected = 0;
uintptr_t base_addr = MM_NADDR;
for (uintptr_t addr = page_aligned_begin; addr < page_aligned_end; addr += 0x1000) {
if (is_reserved(addr)) {
collected = 0;
base_addr = MM_NADDR;
continue;
}
if (base_addr == MM_NADDR) {
base_addr = addr;
}
++collected;
if (collected == req_count) {
return base_addr;
}
}
}
}
return MM_NADDR;
}
void amd64_phys_memory_map(const struct mm_phys_memory_map *mmap) {
struct mmap_iter iter;
uintptr_t base;
size_t size;
int kind;
phys_reserve_mmap.begin = (uintptr_t) MM_PHYS(mmap->address);
phys_reserve_mmap.end = phys_reserve_mmap.begin + mmap->entry_count * mmap->entry_size;
mm_phys_reserve("Memory map", &phys_reserve_mmap);
// Allocate space for mm_pages array
size_t mm_pages_req_count = (PHYS_MAX_PAGES * sizeof(struct page) + 0xFFF) >> 12;
uintptr_t mm_pages_addr = place_mm_pages(mmap, mm_pages_req_count);
_assert(mm_pages_addr != MM_NADDR);
kdebug("Placing mm_pages (%u) at %p\n", mm_pages_req_count, mm_pages_addr);
phys_reserve_mm_pages.begin = mm_pages_addr;
phys_reserve_mm_pages.end = mm_pages_addr + mm_pages_req_count * MM_PAGE_SIZE;
// TODO: also reserve memory map itself before screwing with it?
mm_phys_reserve("mm_pages", &phys_reserve_mm_pages);
mm_pages = (struct page *) MM_VIRTUALIZE(mm_pages_addr);
for (size_t i = 0; i < PHYS_MAX_PAGES; ++i) {
mm_pages[i].flags = PG_ALLOC;
mm_pages[i].refcount = (size_t) -1L;
}
_total_pages = 0;
mmap_iter_init(mmap, &iter);
// Collect usable physical memory information
while (mmap_iter_next(&iter, &kind, &base, &size)) {
uintptr_t page_aligned_begin = (base + 0xFFF) & ~0xFFF;
uintptr_t page_aligned_end = (base + size) & ~0xFFF;
if (kind == MMAP_KIND_USABLE && page_aligned_end > page_aligned_begin + 0x1000) {
//kdebug("+++ %S @ %p\n", page_aligned_end - page_aligned_begin, page_aligned_begin);
for (uintptr_t addr = page_aligned_begin; addr < page_aligned_end; addr += 0x1000) {
extern char _kernel_end;
if (!is_reserved(addr) && addr >= (MM_PHYS(&_kernel_end) + 0x1000)) {
struct page *pg = PHYS2PAGE(addr);
pg->flags &= ~PG_ALLOC;
pg->usage = PU_UNKNOWN;
pg->refcount = 0;
++_total_pages;
}
}
}
}
_pages_free = _total_pages;
kdebug("%S available\n", _total_pages << 12);
}