bond/x64/src/kernel/k_pmm.c

168 lines
5.2 KiB
C

#include "k_alloc.h"
#include "k_bug_check.h"
#include "k_pmm.h"
typedef struct
{
linked_list_node_t free_list_node;
avl_tree_node_t avl_tree_node;
k_physical_addr_t base;
//k_physical_page_attr_t attr;
} k_physical_page_descriptor_t;
/*
* 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 _avl_compare(avl_tree_node_t *tree_node, avl_tree_node_t *my_node)
{
k_physical_addr_t tree_base = OBTAIN_STRUCT_ADDR(tree_node,
k_physical_page_descriptor_t,
avl_tree_node)->base;
k_physical_addr_t my_base = OBTAIN_STRUCT_ADDR(my_node,
k_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;
}
int32_t KAPI k_pmm_init(k_pmm_info_t *info, k_pmm_descriptor_t *desc)
{
if (info == NULL || desc == NULL || desc->initialized)
{
return PMM_STATUS_INVALID_ARGUMENTS;
}
linked_list_init(&desc->free_list);
avl_tree_init(&desc->active_tree, _avl_compare);
for (int i = 0; i < info->num_of_nodes; i++)
{
k_pmm_node_t *each_node = &info->nodes[i];
if (each_node->base % K_PAGE_SIZE != 0)
{
// if not aligned, bug check
return PMM_STATUS_INIT_UNALIGNED;
}
for (int 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 :)
k_physical_page_descriptor_t *page_info = k_alloc(sizeof(k_physical_page_descriptor_t));
if (page_info == NULL)
{
return PMM_STATUS_CANNOT_ALLOC_NODE;
}
page_info->base = each_node->base;
linked_list_push_back(&desc->free_list, &page_info->free_list_node);
}
}
desc->initialized = true;
return PMM_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
int32_t KAPI k_alloc_page(k_pmm_descriptor_t *desc, k_physical_addr_t *out)
{
if (desc == NULL || !desc->initialized)
return PMM_STATUS_INVALID_ARGUMENTS;
k_irql_t irql = k_spin_lock_irql_set(&desc->lock, K_IRQL_DISABLED_LEVEL);
int32_t result = PMM_STATUS_SUCCESS;
linked_list_node_t *node = NULL;
k_physical_page_descriptor_t *page_info = NULL;
node = linked_list_pop_front(&desc->free_list);
if (node != NULL)
{
page_info = OBTAIN_STRUCT_ADDR(node,
k_physical_page_descriptor_t,
free_list_node);
avl_tree_insert(&desc->active_tree, &page_info->avl_tree_node);
*out = page_info->base;
}
else
{
result = PMM_STATUS_NOT_ENOUGH_PAGE;
}
k_spin_unlock_irql_restore(&desc->lock, irql);
return result;
}
//int32_t KAPI k_query_page(k_pmm_descriptor_t *desc,
// k_physical_addr_t base,
// k_physical_page_attr_t *out)
//{
//
// if (desc == NULL || !desc->initialized)
// return PMM_STATUS_INVALID_ARGUMENTS;
//
// k_irql_t irql = k_spin_lock_irql_set(&desc->lock, K_IRQL_DISABLED_LEVEL);
// int32_t result = PMM_STATUS_SUCCESS;
// avl_tree_node_t *node = NULL;
// // search for dummy
// k_physical_page_descriptor_t dummy, *page_info = NULL;
// dummy.base = base;
//
// node = avl_tree_delete(&desc->pages_tree, &dummy.avl_tree_node);
//
// if (node != NULL)
// {
// page_info = OBTAIN_STRUCT_ADDR(node, k_physical_page_descriptor_t, avl_tree_node);
// *out = page_info->attr;
// }
// else
// {
// result = PMM_STATUS_PAGE_NOT_FOUND;
// }
//
// k_spin_unlock_irql_restore(&desc->lock, irql);
//
// return result;
//}
int32_t KAPI k_free_page(k_pmm_descriptor_t* desc, k_physical_addr_t base)
{
if (desc == NULL || !desc->initialized)
return PMM_STATUS_INVALID_ARGUMENTS;
// just lock since not sharing with anyone
k_irql_t irql = k_spin_lock_irql_set(&desc->lock, K_IRQL_DISABLED_LEVEL);
int32_t result = PMM_STATUS_SUCCESS;
avl_tree_node_t *node = NULL;
// search for dummy
k_physical_page_descriptor_t dummy, *page_info;
dummy.base = base;
node = avl_tree_delete(&desc->active_tree, &dummy.avl_tree_node);
if (node != NULL)
{
page_info = OBTAIN_STRUCT_ADDR(node, k_physical_page_descriptor_t, avl_tree_node);
linked_list_push_back(&desc->free_list, &page_info->free_list_node);
}
else
{
result = PMM_STATUS_PAGE_NOT_FOUND;
}
k_spin_unlock_irql_restore(&desc->lock, irql);
return result;
}