9256f5b575
- asm and ld files referencing c symbols are now generated by c preprocessor instead of duplicate definition. - Finished kernel memory layout. Unfinished init code.
188 lines
5.1 KiB
C
188 lines
5.1 KiB
C
#include "kernel/ke/assert.h"
|
|
#include "kernel/ke/rwwlock.h"
|
|
#include "status.h"
|
|
#include "kernel/ke/alloc.h"
|
|
#include "kernel/mm/pmm.h"
|
|
|
|
typedef struct
|
|
{
|
|
linked_list_node_t free_list_node;
|
|
avl_tree_node_t avl_tree_node;
|
|
uintptr_t base;
|
|
int32_t attr;
|
|
} physical_page_descriptor_t;
|
|
|
|
static avl_tree_t active_tree;
|
|
static linked_list_t free_list;
|
|
static k_rwwlock_t lock;
|
|
static _Bool initialized;
|
|
|
|
/*
|
|
* A comparison function between tree_node and your_node
|
|
* Returns:
|
|
* < 0 if tree_node < your_node
|
|
* = 0 if tree_node == your_node
|
|
* > 0 if tree_node > your_node
|
|
*/
|
|
static int32_t mmp_base_paddr_compare(void *tree_node, void *my_node)
|
|
{
|
|
uintptr_t tree_base = OBTAIN_STRUCT_ADDR(tree_node,
|
|
physical_page_descriptor_t,
|
|
avl_tree_node)->base;
|
|
uintptr_t my_base = OBTAIN_STRUCT_ADDR(my_node,
|
|
physical_page_descriptor_t,
|
|
avl_tree_node)->base;
|
|
if (tree_base > my_base)
|
|
return 1;
|
|
else if (tree_base < my_base)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
status_t KABI sx_pmm_init(pmm_info_t *info)
|
|
{
|
|
if (info == NULL)
|
|
{
|
|
return MM_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
if (initialized)
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ke_rwwlock_init(&lock);
|
|
lb_linked_list_init(&free_list);
|
|
lb_avl_tree_init(&active_tree, mmp_base_paddr_compare);
|
|
|
|
for (uint32_t i = 0; i < info->num_of_nodes; i++)
|
|
{
|
|
pmm_node_t *each_node = &info->nodes[i];
|
|
|
|
ke_assert (each_node->base % KERNEL_PAGE_SIZE != 0);
|
|
|
|
for (uint64_t j = 0; j <= each_node->size; j++)
|
|
{
|
|
// note that k_alloc function here might trigger page fault
|
|
// however it's fine as long as we don't touch linked list just yet
|
|
// it will use the pages that are already on file to enlarge the kernel heap
|
|
// don't worry, be happy :)
|
|
physical_page_descriptor_t *page_info = ke_alloc(sizeof(physical_page_descriptor_t));
|
|
|
|
if (page_info == NULL)
|
|
{
|
|
return MM_ALLOCATION_FAILED;
|
|
}
|
|
|
|
page_info->base = each_node->base;
|
|
lb_linked_list_push_back(&free_list, &page_info->free_list_node);
|
|
}
|
|
}
|
|
initialized = true;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
// free lists can only be updated at IRQL == DISABLED
|
|
// we need to guarantee that on the same CPU, these APIs are not preempted by
|
|
// potential callers of these, since timer/interrupts queue DPC, which might trigger
|
|
// page fault (kernel heap), therefore, it must set IRQL to DISABLED
|
|
|
|
status_t KABI mm_alloc_page(uintptr_t *out)
|
|
{
|
|
if (!initialized)
|
|
{
|
|
return MM_UNINITIALIZED;
|
|
}
|
|
|
|
if (out == NULL)
|
|
{
|
|
return MM_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
irql_t irql = ke_rwwlock_writer_lock_raise_irql(&lock, IRQL_DISABLED_LEVEL);
|
|
status_t result = STATUS_SUCCESS;
|
|
linked_list_node_t *node = NULL;
|
|
physical_page_descriptor_t *page_info = NULL;
|
|
node = lb_linked_list_pop_front(&free_list);
|
|
if (node != NULL)
|
|
{
|
|
page_info = OBTAIN_STRUCT_ADDR(node,
|
|
physical_page_descriptor_t,
|
|
free_list_node);
|
|
lb_avl_tree_insert(&active_tree, &page_info->avl_tree_node);
|
|
*out = page_info->base;
|
|
} else
|
|
{
|
|
result = MM_NOT_ENOUGH_PAGE;
|
|
}
|
|
|
|
ke_rwwlock_writer_unlock_lower_irql(&lock, irql);
|
|
return result;
|
|
}
|
|
|
|
status_t KABI mm_query_page_attr(uintptr_t base,
|
|
int32_t *out)
|
|
{
|
|
if (!initialized)
|
|
{
|
|
return MM_UNINITIALIZED;
|
|
}
|
|
|
|
if (out == NULL)
|
|
{
|
|
return MM_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
irql_t irql = ke_rwwlock_reader_lock_raise_irql(&lock, IRQL_DISABLED_LEVEL);
|
|
status_t result = STATUS_SUCCESS;
|
|
avl_tree_node_t *node = NULL;
|
|
// search for dummy
|
|
physical_page_descriptor_t dummy, *page_info = NULL;
|
|
dummy.base = base;
|
|
|
|
node = lb_avl_tree_delete(&active_tree, &dummy.avl_tree_node);
|
|
|
|
if (node != NULL)
|
|
{
|
|
page_info = OBTAIN_STRUCT_ADDR(node, physical_page_descriptor_t, avl_tree_node);
|
|
*out = page_info->attr;
|
|
} else
|
|
{
|
|
result = MM_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
ke_rwwlock_reader_unlock_lower_irql(&lock, irql);
|
|
|
|
return result;
|
|
}
|
|
|
|
status_t KABI mm_free_page(uintptr_t base)
|
|
{
|
|
if (!initialized)
|
|
{
|
|
return MM_UNINITIALIZED;
|
|
}
|
|
|
|
// just lock since not sharing with anyone
|
|
irql_t irql = ke_rwwlock_writer_lock_raise_irql(&lock, IRQL_DISABLED_LEVEL);
|
|
status_t result = STATUS_SUCCESS;
|
|
avl_tree_node_t *node = NULL;
|
|
// search for dummy
|
|
physical_page_descriptor_t dummy, *page_info;
|
|
dummy.base = base;
|
|
|
|
node = lb_avl_tree_delete(&active_tree, &dummy.avl_tree_node);
|
|
if (node != NULL)
|
|
{
|
|
page_info = OBTAIN_STRUCT_ADDR(node, physical_page_descriptor_t, avl_tree_node);
|
|
lb_linked_list_push_back(&free_list, &page_info->free_list_node);
|
|
} else
|
|
{
|
|
result = MM_INVALID_ARGUMENTS;
|
|
}
|
|
|
|
ke_rwwlock_writer_unlock_lower_irql(&lock, irql);
|
|
|
|
return result;
|
|
} |