00001 /*! \file mm/umalloc.c 00002 * \brief Virtual Memory Allocator for the User Space. 00003 * \author Andrea Righi <drizzt@inwind.it> 00004 * \date Last update: 2004-01-01 00005 * \note Copyright (©) 2003 Andrea Righi 00006 */ 00007 00008 #include <const.h> 00009 00010 #include <arch/paging.h> 00011 00012 #include <kernel/task.h> 00013 #include <kernel/video.h> 00014 00015 #include <kernel/umalloc.h> 00016 00017 //! \brief Initialize the user virtual memory area. 00018 //! \param heap_start The start of the user heap. 00019 //! \param heap_size The size of the user heap. 00020 void umalloc_init(size_t heap_start, size_t heap_size) 00021 { 00022 task_t *t; 00023 umem_block_t *p; 00024 00025 t = get_curr_task(); 00026 if( t==NULL ) 00027 return; 00028 00029 // Setup the heap in the task structure. 00030 t->heap_start = heap_start; 00031 t->heap_size = heap_size; 00032 00033 // Create only one hudge block. 00034 p = (umem_block_t *)heap_start; 00035 p->magic = UM_MAGIC; 00036 p->flags = UM_FREE; 00037 p->size = heap_size - sizeof(umem_block_t); 00038 p->owner = NULL; 00039 } 00040 00041 //! \brief 00042 //! Allocate a memory block from the user memory area. 00043 //! This is a best-fit like allocator. 00044 //! \param size The size of the block you want to allocate. 00045 //! \exception NULL Out-of-memory. 00046 //! \return A pointer to a memory area. 00047 //! \note The memory area is not initialized when it's just allocated. 00048 void *umalloc(size_t size) 00049 { 00050 task_t *t; 00051 umem_block_t *p, *p_best=NULL; 00052 00053 if ( !size ) return( NULL ); // Is the process crazy?! :-) 00054 00055 // Get the current task structure. 00056 t = get_curr_task(); 00057 00058 // Round the size up to sizeof(umem_block_t); this is the unit 00059 // of allocation. 00060 size = ( size & -(sizeof(umem_block_t)) ) + sizeof(umem_block_t); 00061 00062 // Explore the heap for the best-fit hole. 00063 p=(umem_block_t *)(t->heap_start); 00064 if( !p ) return( NULL ); 00065 while(p < (umem_block_t *)(t->heap_start + t->heap_size)) 00066 { 00067 // Find a free block. 00068 if (p->flags == UM_FREE) 00069 { 00070 // This block is free... let's proceed! 00071 if (p->size == size) 00072 { 00073 // Best fit block found! You're lucky! 00074 p_best = p; 00075 break; 00076 } 00077 if (p->size > size) 00078 { 00079 // The hole is bigger than size... 00080 // ...check if it's the best-fit hole! 00081 if (p_best==NULL) 00082 p_best = p; 00083 else 00084 if (p_best->size > p->size) 00085 p_best = p; 00086 } 00087 } 00088 // Examine the next memory block. 00089 p = (void *)p+p->size+sizeof(umem_block_t); 00090 } 00091 00092 // Have you found a free block?! 00093 if (p_best == NULL) 00094 { 00095 // No free memory :( 00096 return( NULL ); 00097 } 00098 00099 // Found, now allocate it! 00100 p_best->magic = UM_MAGIC; 00101 p_best->flags = UM_ALLOC; 00102 p_best->owner = get_pid(); 00103 00104 if (p_best->size == size) 00105 { 00106 p_best = (void *)p_best + sizeof(umem_block_t); 00107 // The block cover the size perfectly. 00108 return( (void *)p_best ); 00109 } 00110 00111 // Otherwise split the hole in two blocks. 00112 p = (void *)p_best + sizeof(umem_block_t) + size; 00113 p->magic = UM_MAGIC; 00114 p->flags = UM_FREE; 00115 p->size = p_best->size - size - sizeof(umem_block_t); 00116 p->owner = NULL; 00117 00118 p_best->size = size; 00119 p_best = (void *)p_best + sizeof(umem_block_t); 00120 00121 // Return the best-fit pointer. 00122 return ( (void *)p_best ); 00123 } 00124 00125 //! \brief Try to release physical frames occupied by the freed block. 00126 //! \param ptr The pointer to deallocate. 00127 static void deallocate(umem_block_t *ptr) 00128 { 00129 size_t start = 00130 PAGE_ALIGN_UP((uint32_t)((void *)ptr + sizeof(umem_block_t))); 00131 size_t end = 00132 PAGE_ALIGN((uint32_t)((void *)ptr + sizeof(umem_block_t) + ptr->size)); 00133 00134 while( start<end ) 00135 { 00136 if (*ADDR_TO_PDE(start) != NULL) 00137 { 00138 delete_page( start ); 00139 start += PAGE_SIZE; 00140 } 00141 else 00142 start = PAGE_DIR_ALIGN_UP( start ); 00143 } 00144 } 00145 00146 //! \brief 00147 //! Frees the memory space pointed by ptr, which must have 00148 //! been returned by a previous call to umalloc(size_t size). 00149 //! \param ptr The pointer you want to free. 00150 void ufree(void *ptr) 00151 { 00152 task_t *t; 00153 umem_block_t *p, *p2; 00154 00155 if (ptr == NULL) return; 00156 00157 // Point to the header. 00158 p = (void *)ptr - sizeof(umem_block_t); 00159 00160 if (p->magic != UM_MAGIC) return; // Is the process crazy?! :) 00161 if (p->flags != UM_ALLOC) return; // Crazy again?! :) 00162 00163 // Get the current task structure. 00164 t = get_curr_task(); 00165 00166 // Free the block. 00167 p->flags = UM_FREE; 00168 p->owner = NULL; 00169 00170 // Try to combine the block wih the next one... 00171 p2 = (void *)p + p->size + sizeof(umem_block_t); 00172 00173 if (p2 < (umem_block_t *)(t->heap_start + t->heap_size)) 00174 if (p2->flags == UM_FREE) 00175 { 00176 // Let's merge the two blocks! 00177 p->size += p2->size + sizeof(umem_block_t); 00178 p2->magic = NULL; 00179 } 00180 00181 // Try to combine the block with the previous one... 00182 if (p != (umem_block_t *)(t->heap_start)) 00183 { 00184 // Find the previous block. 00185 p2 = (void *)(t->heap_start); 00186 for(;;) 00187 { 00188 if (((void *)p2 + p2->size + sizeof(umem_block_t)) == p) 00189 { 00190 // Block found! 00191 // Check if it's a free block. 00192 if (p2->flags == UM_FREE) 00193 { 00194 // Let's merge the two blocks! 00195 p2->size += (p->size + sizeof(umem_block_t)); 00196 p->magic = NULL; 00197 p = p2; 00198 } 00199 break; 00200 } 00201 p2 = (void *)p2 + p2->size + sizeof(umem_block_t); 00202 } 00203 } 00204 00205 // Release the physical space occupied by the block. 00206 deallocate( p ); 00207 }