334 lines
8.6 KiB
C
334 lines
8.6 KiB
C
// vmm.c
|
|
// Author: Josh Holtrop
|
|
// Date: 09/30/03
|
|
// Rewritten from scratch: 12/23/03
|
|
// Modified: 07/29/04
|
|
|
|
#include "hos_defines.h"
|
|
#include "kernel.h"
|
|
#include "mm/vmm.h"
|
|
#include "asmfuncs.h"
|
|
#include "mm/mm.h"
|
|
|
|
int vmm_map(void *virt);
|
|
int vmm_map1(unsigned int virt, unsigned int physical);
|
|
int vmm_mapn(unsigned int virt, unsigned int physical, unsigned int n);
|
|
void vmm_unmap1(unsigned int virt);
|
|
void vmm_unmapn(unsigned int virt, unsigned int n);
|
|
int vmm_map_range(void *virt_start, void *virt_end, u32_t phys_start);
|
|
void vmm_heb_init(HeapEntryBlock_t *heb);
|
|
void vmm_addToQueue(u32_t queue, HeapEntry_t *preceeding, HeapEntry_t *he);
|
|
int vmm_countHeapEntries(HeapEntry_t *he);
|
|
HeapEntry_t *vmm_followChain(HeapEntry_t *he);
|
|
HeapEntry_t *vmm_getUnusedEntry();
|
|
HeapEntry_t *vmm_stripUnusedEntry();
|
|
|
|
//void *calloc(unsigned int number, unsigned int size);
|
|
|
|
extern mb_info_t mb_info_block;
|
|
extern mb_module_t mb_modules[MAX_MODULES];
|
|
extern u32_t mm_freepages;
|
|
|
|
HeapEntryQueue_t heapEntryQueues[VMM_HE_TYPES]; // linked queue of HeapEntry objects
|
|
HeapEntry_t heapEntryHeadNodes[VMM_HE_TYPES]; // head nodes for linked queues
|
|
HeapEntry_t heapEntryTailNodes[VMM_HE_TYPES]; // tail nodes for linked queues
|
|
HeapEntryBlock_t initialHEB; // block for initial 256 HeapEntry objects
|
|
|
|
|
|
// This is the initialization procedure for the Virtual Memory Manager
|
|
// It sets up the heap for dynamic memory allocation and virtual page allocation
|
|
void vmm_init()
|
|
{
|
|
int i;
|
|
for (i = 0; i < mb_info_block.mods_count; i++) //page in the kernel modules
|
|
vmm_map_range((void*)mb_modules[i].mod_start, (void*)mb_modules[i].mod_end - 1, mb_modules[i].mod_start - VIRT_OFFSET);
|
|
for (i = 0; i < VMM_HE_TYPES; i++)
|
|
{
|
|
heapEntryQueues[i].head = &heapEntryHeadNodes[i];
|
|
heapEntryHeadNodes[i].next = &heapEntryTailNodes[i];
|
|
heapEntryTailNodes[i].prev = &heapEntryHeadNodes[i];
|
|
}
|
|
vmm_heb_init(&initialHEB);
|
|
vmm_addToQueue(VMM_HE_UNUSED, &heapEntryHeadNodes[VMM_HE_UNUSED], &initialHEB.entry[0]);
|
|
HeapEntry_t *wilderness = vmm_stripUnusedEntry();
|
|
wilderness->base = (void *) HEAP_START;
|
|
wilderness->length = HEAP_LENGTH;
|
|
vmm_addToQueue(VMM_HE_HOLE, &heapEntryHeadNodes[VMM_HE_HOLE], wilderness);
|
|
}
|
|
|
|
|
|
/* Allocate a physical page and map the virtual address to it, return physical address allocated or NULL */
|
|
int vmm_map(void *virt)
|
|
{
|
|
if (mm_freepages < 10);
|
|
return -1;
|
|
vmm_map1((u32_t)virt, mm_palloc());
|
|
return 0;
|
|
}
|
|
|
|
|
|
// This function maps a virtual address to a physical address using the page directory / page table
|
|
int vmm_map1(u32_t virt, u32_t physical)
|
|
{
|
|
u32_t pde = virt >> 22;
|
|
u32_t pte = (virt & 0x003FF000) >> 12;
|
|
u32_t *pageTables = (u32_t *)0xFFFFF000; //this is the location of the page directory
|
|
if (!(pageTables[pde] & 0x01)) //the page directory entry is not present, we must allocate a page table
|
|
{
|
|
u32_t newpagetable;
|
|
if (!(newpagetable = mm_palloc()))
|
|
return 1; //out of physical memory
|
|
pageTables[pde] = newpagetable | 0x03;
|
|
invlpg_(virt); //in case it was cached, so we can fill page table safely
|
|
memsetd((void*)(0xFFC00000 | (pde << 12)), 0, 1024); //zero out new page table
|
|
}
|
|
*(u32_t *)(0xFFC00000 | (pde << 12) | (pte << 2)) = (physical & 0xFFFFF000) | 0x03;
|
|
invlpg_(virt);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// This function maps a variable number of pages in a row
|
|
int vmm_mapn(u32_t virt, u32_t physical, u32_t n)
|
|
{
|
|
while (n > 0)
|
|
{
|
|
if (vmm_map1(virt, physical))
|
|
return 1; // error mapping page
|
|
virt += 4096;
|
|
physical += 4096;
|
|
n--;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
// This function removes the virtual address's entry in the page directory / page table
|
|
void vmm_unmap1(u32_t virt)
|
|
{
|
|
*(u32_t *)(0xFFC00000 | ((virt & 0xFFC00000) >> 10) | ((virt & 0x003FF000) >> 10)) = 0;
|
|
invlpg_(virt);
|
|
}
|
|
|
|
|
|
// This function removes multiple pages' entries
|
|
void vmm_unmapn(u32_t virt, u32_t n)
|
|
{
|
|
while (n > 0)
|
|
{
|
|
vmm_unmap1(virt);
|
|
virt += 4096;
|
|
n--;
|
|
}
|
|
}
|
|
|
|
|
|
// This function maps an entire address range into memory
|
|
int vmm_map_range(void *virt_start, void *virt_end, u32_t phys_start)
|
|
{
|
|
if (virt_end < virt_start)
|
|
return -1; // invalid region
|
|
while (virt_start < virt_end)
|
|
{
|
|
if (vmm_map1((u32_t)virt_start, phys_start))
|
|
return -2; // out of memory
|
|
virt_start += 4096;
|
|
phys_start += 4096;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
// kernel virtual memory allocator
|
|
void *kmalloc(u32_t size)
|
|
{
|
|
k_enter_critical();
|
|
|
|
k_leave_critical();
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// kernel virtual memory de-allocator
|
|
int kfree(void *addr)
|
|
{
|
|
k_enter_critical();
|
|
|
|
k_leave_critical();
|
|
return 0;
|
|
}
|
|
|
|
|
|
// This function allocates a virtual page and maps it to a physical page
|
|
void *vmm_palloc()
|
|
{
|
|
k_enter_critical();
|
|
HeapEntry_t *he = heapEntryQueues[VMM_HE_HOLE].head->next;
|
|
HeapEntry_t *wilderness = he;
|
|
while (he)
|
|
{
|
|
if (he->length == 4096)
|
|
{
|
|
((HeapEntry_t *)he->prev)->next = he->next;
|
|
((HeapEntry_t *)he->next)->prev = he->prev;
|
|
heapEntryQueues[VMM_HE_HOLE].count--;
|
|
he->next = NULL;
|
|
he->prev = NULL;
|
|
vmm_addToQueue(VMM_HE_USED, &heapEntryHeadNodes[VMM_HE_USED], he);
|
|
vmm_map(he->base);
|
|
k_leave_critical();
|
|
return he->base;
|
|
}
|
|
if (he->length > wilderness->length)
|
|
wilderness = he;
|
|
he = (HeapEntry_t *)he->next;
|
|
}
|
|
if (wilderness->length < 0x00010000) //leave 16 pages free
|
|
{
|
|
k_leave_critical();
|
|
return NULL;
|
|
}
|
|
wilderness->length -= 4096; //strip 4k from the top
|
|
he = vmm_getUnusedEntry();
|
|
he->base = wilderness->base + wilderness->length;
|
|
he->length = 4096;
|
|
vmm_addToQueue(VMM_HE_USED, &heapEntryHeadNodes[VMM_HE_USED], he);
|
|
vmm_map(he->base);
|
|
k_leave_critical();
|
|
return he->base;
|
|
}
|
|
|
|
|
|
// This function frees a previously-allocated virtual page
|
|
int vmm_pfree(void *addr)
|
|
{
|
|
k_enter_critical();
|
|
HeapEntry_t *he = heapEntryQueues[VMM_HE_USED].head->next;
|
|
while (he)
|
|
{
|
|
if (he->base == addr) //found the page to free
|
|
{
|
|
((HeapEntry_t *)he->prev)->next = he->next;
|
|
((HeapEntry_t *)he->next)->prev = he->prev;
|
|
heapEntryQueues[VMM_HE_USED].count--;
|
|
he->next = NULL;
|
|
he->prev = NULL;
|
|
vmm_unmap1((u32_t)he->base);
|
|
vmm_addToQueue(VMM_HE_HOLE, &heapEntryHeadNodes[VMM_HE_HOLE], he);
|
|
k_leave_critical();
|
|
return 0;
|
|
}
|
|
he = he->next;
|
|
}
|
|
k_leave_critical();
|
|
return -1; // page not found
|
|
}
|
|
|
|
|
|
// This function allocates and zeros memory for the given number of objects,
|
|
// given the size of each object
|
|
/*
|
|
void *calloc(u32_t number, u32_t size)
|
|
{
|
|
void *mem = malloc(number * size);
|
|
if (!mem)
|
|
return NULL; //could not get memory
|
|
memset(mem, 0, number * size);
|
|
return mem;
|
|
}*/
|
|
|
|
|
|
|
|
// This function initialzes a Heap Entry Block to entries linked together
|
|
void vmm_heb_init(HeapEntryBlock_t *heb)
|
|
{
|
|
int a;
|
|
for (a = 0; a < 255; a++)
|
|
{
|
|
heb->entry[a].next = &heb->entry[a+1];
|
|
heb->entry[a+1].prev = &heb->entry[a];
|
|
}
|
|
heb->entry[0].prev = NULL;
|
|
heb->entry[255].next = NULL;
|
|
}
|
|
|
|
|
|
// This function adds a HeapEntry structure to the queue following 'preceeding' the queue
|
|
void vmm_addToQueue(u32_t queue, HeapEntry_t *preceeding, HeapEntry_t *he)
|
|
{
|
|
heapEntryQueues[queue].count += vmm_countHeapEntries(he);
|
|
HeapEntry_t *last = vmm_followChain(he);
|
|
last->next = preceeding->next;
|
|
he->prev = preceeding;
|
|
((HeapEntry_t *)last->next)->prev = last;
|
|
preceeding->next = he;
|
|
}
|
|
|
|
|
|
// This function returns how many HeapEntry objects are in a queue starting/including from the object given
|
|
int vmm_countHeapEntries(HeapEntry_t *he)
|
|
{
|
|
int count = 0;
|
|
while (he)
|
|
{
|
|
count++;
|
|
he = (HeapEntry_t *)he->next;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
// This function follows a chain of HeapEntry objects and returns a pointer to the last one
|
|
HeapEntry_t *vmm_followChain(HeapEntry_t *he)
|
|
{
|
|
while (he->next)
|
|
he = (HeapEntry_t *)he->next;
|
|
return he;
|
|
}
|
|
|
|
|
|
// This function breaks an unused chunk from its queue and returns a pointer to it
|
|
HeapEntry_t *vmm_getUnusedEntry()
|
|
{
|
|
if (heapEntryQueues[VMM_HE_UNUSED].count < 5)
|
|
{
|
|
HeapEntry_t *he = heapEntryQueues[VMM_HE_HOLE].head->next;
|
|
HeapEntry_t *wilderness = he;
|
|
while (he)
|
|
{
|
|
if ((he->length) > (wilderness->length))
|
|
wilderness = he;
|
|
he = (HeapEntry_t *)he->next;
|
|
}
|
|
wilderness->length -= 4096; //strip 4k from the top
|
|
HeapEntryBlock_t *newHEB = wilderness->base + wilderness->length;
|
|
vmm_map(newHEB);
|
|
vmm_heb_init(newHEB);
|
|
HeapEntry_t *newDesc = vmm_stripUnusedEntry(); //descriptor for the new HEB
|
|
newDesc->base = newHEB;
|
|
newDesc->length = 4096;
|
|
vmm_addToQueue(VMM_HE_USED, heapEntryTailNodes[VMM_HE_USED].prev, newDesc);
|
|
}
|
|
return vmm_stripUnusedEntry();
|
|
}
|
|
|
|
|
|
// Return pointer to an unused HeapEntry object, ASSUMES THERE IS ONE PRESENT IN QUEUE
|
|
HeapEntry_t *vmm_stripUnusedEntry()
|
|
{
|
|
HeapEntry_t *he = heapEntryQueues[VMM_HE_UNUSED].head->next;
|
|
heapEntryQueues[VMM_HE_UNUSED].head->next = he->next;
|
|
((HeapEntry_t *)he->next)->prev = he->prev;
|
|
heapEntryQueues[VMM_HE_UNUSED].count--;
|
|
he->next = 0;
|
|
he->prev = 0;
|
|
return he;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|