Skip to content

Commit f3c0d76

Browse files
committed
cm: cm_malloc* functions for dynamic heap allocations
1 parent 98255ec commit f3c0d76

File tree

6 files changed

+268
-1
lines changed

6 files changed

+268
-1
lines changed

include/cm/cm.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,11 @@ char* cm_strncpy (char* d, const char* s, SIZE n);
9999
void* cm_memset (void* const s, U8 c, size_t n);
100100
INT cm_snprintf (CHAR* dest, size_t size, const CHAR* fmt, ...);
101101
INT cm_vsnprintf (CHAR* dest, size_t size, const CHAR* fmt, va_list l);
102+
103+
/***************************************************************************************************
104+
* Heap managemnt
105+
***************************************************************************************************/
106+
void cm_malloc_init();
107+
void* cm_malloc (size_t bytes);
108+
void* cm_calloc (size_t bytes);
109+
bool cm_free (void* addr);

include/cm/debug.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#pragma once
88

9+
#include <types.h>
10+
911
#if defined(DEBUG) && defined(PORT_E9_ENABLED)
1012
typedef enum CM_DebugLogType {
1113
CM_DEBUG_LOG_TYPE_INFO,

include/cm/err.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ typedef enum CMErrors {
2222
LIBRARY_ERRORS_START = 100,
2323
CM_ERR_INVALID_INPUT = 100,
2424
CM_ERR_EVENT_HANDLER_ALREADY_REGISTERED = 101,
25+
CM_ERR_OUT_OF_HEAP_MEM = 102,
2526
} CMErrors;
2627

2728
uint32_t cm_get_lib_error();

include/kcmlib.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55
* Types and function declarations defined here are private to the CM library and should not be
66
* exposed to the applications.
77
*/
8-
98
#include <intrusive_list.h>
109
#include <memloc.h>
1110

1211
#pragma once
1312

13+
typedef struct CM_MallocHeader
14+
{
15+
size_t netNodeSize; /// Size of a region together with the header size.
16+
bool isAllocated; /// Is the region allocated or free.
17+
ListNode adjnode; /// A node in the Adjacent list.
18+
ListNode freenode; /// A node in the Free list.
19+
ListNode allocnode; /// A node in the Allocation list
20+
} CM_MallocHeader;
21+
22+
#define CM_MALLOC_MEM_SIZE_BYTES (ARCH_MEM_LEN_BYTES_PROCESS_DATA / 2)
23+
1424
extern uint32_t cm_error_num;
1525

1626
/* Can be used to store an error code and return from a function */

src/cm/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ set(APPLIB_SOURCES
2727
${CMAKE_CURRENT_SOURCE_DIR}/debug.c
2828
${CMAKE_CURRENT_SOURCE_DIR}/cm.c
2929
${CMAKE_CURRENT_SOURCE_DIR}/string.c
30+
${CMAKE_CURRENT_SOURCE_DIR}/malloc.c
3031
)
3132

3233
if (MOS_GRAPHICS_ENABLED)

src/cm/malloc.c

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
/*
2+
* --------------------------------------------------------------------------------------------------
3+
* Megha Operating System V2 - App Library - malloc allocator
4+
* --------------------------------------------------------------------------------------------------
5+
*/
6+
#include <intrusive_list.h>
7+
#include <cm/err.h>
8+
#include <kcmlib.h>
9+
#include <types.h>
10+
#include <utils.h>
11+
#include <cm/debug.h>
12+
#include <cm/cm.h>
13+
#include <kcmlib.h>
14+
15+
typedef enum FindCriteria {
16+
FIND_CRIT_NODE_SIZE,
17+
FIND_CRIT_NODE_ADDRESS
18+
} FindCriteria;
19+
20+
static CM_MallocHeader* s_createNewNode (void* at, size_t netSize);
21+
static CM_MallocHeader* s_findFirst (ListNode* head, FindCriteria criteria, PTR value);
22+
static CM_MallocHeader* s_getMallocHeaderFromList (ListNode* head, ListNode* node);
23+
static void s_splitFreeNode (size_t bytes, CM_MallocHeader* freeNodeHdr);
24+
static void s_combineAdjFreeNodes (CM_MallocHeader* currentNode);
25+
26+
extern ListNode s_freeHead, s_allocHead, s_adjHead;
27+
static void* s_buffer;
28+
29+
#define NET_ALLOCATION_SIZE(sz_bytes) (sz_bytes + sizeof (CM_MallocHeader))
30+
31+
#ifndef UNITTEST
32+
ListNode s_freeHead, s_allocHead, s_adjHead;
33+
#else
34+
// kmalloc unit test must provide definitions for the list head variables
35+
#endif
36+
37+
/***************************************************************************************************
38+
* Initializes virtual & physical memory for kmalloc.
39+
*
40+
* @return None
41+
**************************************************************************************************/
42+
void cm_malloc_init()
43+
{
44+
CM_DBG_FUNC_ENTRY();
45+
46+
list_init (&s_freeHead);
47+
list_init (&s_allocHead);
48+
list_init (&s_adjHead);
49+
50+
s_buffer = cm_process_get_datamem_start();
51+
52+
CM_MallocHeader* newH = s_createNewNode (s_buffer, CM_MALLOC_MEM_SIZE_BYTES);
53+
list_add_before (&s_freeHead, &newH->freenode);
54+
list_add_before (&s_adjHead, &newH->adjnode);
55+
56+
CM_DBG_INFO ("Size of CM_MallocHeader: %lu bytes", sizeof (CM_MallocHeader));
57+
CM_DBG_INFO ("Malloc buffer is at: %px", s_buffer);
58+
}
59+
60+
/***************************************************************************************************
61+
* Allocates at least 'bytes' number of bytes from the kmalloc memory. If successful then zeros the
62+
* memory before returning.
63+
*
64+
* @Input bytes Number of bytes to allocate.
65+
* @return Poiter to the start of the allocated memory. Or NULL on failure.
66+
**************************************************************************************************/
67+
void* cm_calloc (size_t bytes)
68+
{
69+
CM_DBG_FUNC_ENTRY ("Bytes: %x", bytes);
70+
71+
void* addr = cm_malloc (bytes);
72+
if (addr != NULL) {
73+
cm_memset (addr, 0, bytes);
74+
}
75+
return addr;
76+
}
77+
78+
/***************************************************************************************************
79+
* Allocates at least 'bytes' number of bytes from the kmalloc memory.
80+
*
81+
* @Input bytes Number of bytes to allocate.
82+
* @return Poiter to the start of the allocated memory. Or NULL on failure.
83+
* @error ERR_OUT_OF_MEM - There is less memory than requested.
84+
**************************************************************************************************/
85+
void* cm_malloc (size_t bytes)
86+
{
87+
CM_DBG_FUNC_ENTRY ("Bytes: %x", bytes);
88+
89+
if (bytes == 0) {
90+
CM_RETURN_ERROR (CM_ERR_INVALID_INPUT, NULL);
91+
}
92+
93+
CM_DBG_INFO ("Requested net size of %lu bytes", NET_ALLOCATION_SIZE (bytes));
94+
95+
// Search for suitable node
96+
size_t searchAllocSize = NET_ALLOCATION_SIZE (bytes) + sizeof (CM_MallocHeader);
97+
CM_MallocHeader* node = s_findFirst (&s_freeHead, FIND_CRIT_NODE_SIZE, searchAllocSize);
98+
99+
if (node != NULL) {
100+
cm_assert (node->netNodeSize >= NET_ALLOCATION_SIZE (bytes)); // Found node too small
101+
102+
// Split the free node into two.
103+
s_splitFreeNode (bytes, node);
104+
return (void*)((PTR)node + sizeof (CM_MallocHeader));
105+
}
106+
107+
CM_RETURN_ERROR (CM_ERR_OUT_OF_HEAP_MEM, NULL);
108+
}
109+
110+
/***************************************************************************************************
111+
* Marks previously allocated memory starting at 'addr' as free.
112+
*
113+
* @Input addr Pointer to start of a kmalloc allocated memory.
114+
* @return True on success. False otherwise.
115+
* @error ERR_INVALID_ARGUMENT - If the input address was not found.
116+
**************************************************************************************************/
117+
bool cm_free (void* addr)
118+
{
119+
CM_DBG_FUNC_ENTRY ("Address: %px", addr);
120+
121+
void* headerAddress = (void*)((PTR)addr - sizeof (CM_MallocHeader));
122+
CM_MallocHeader* allocHdr = s_findFirst (&s_allocHead, FIND_CRIT_NODE_ADDRESS,
123+
(PTR)headerAddress);
124+
if (allocHdr != NULL) {
125+
CM_DBG_INFO ("Free at %px, Size = %lu", allocHdr, allocHdr->netNodeSize);
126+
list_remove (&allocHdr->allocnode);
127+
list_add_after (&s_freeHead, &allocHdr->freenode);
128+
allocHdr->isAllocated = false;
129+
130+
s_combineAdjFreeNodes (allocHdr);
131+
return true;
132+
}
133+
134+
// Could not find allocated node. Either double free or tatal error.
135+
cm_panic();
136+
return false;
137+
}
138+
139+
static void s_combineAdjFreeNodes (CM_MallocHeader* currentNode)
140+
{
141+
CM_MallocHeader* next = NULL;
142+
CM_MallocHeader* prev = NULL;
143+
144+
cm_assert (currentNode->isAllocated == false); // Cannot combine allocated node. Invalid input.
145+
146+
// Prev node could be the list head (which is not a CM_MallocHeader). That would mean beginning
147+
// of list and is not taken into account.
148+
if (currentNode->adjnode.prev != &s_adjHead) {
149+
prev = LIST_ITEM (currentNode->adjnode.prev, CM_MallocHeader, adjnode);
150+
}
151+
152+
// Next node could be the list head (which is not a CM_MallocHeader). That would mean end of
153+
// list and is not taken into account (Practically this is not possible as there will always be
154+
// one Free section at the end of kmalloc buffer).
155+
if (currentNode->adjnode.next != &s_adjHead) {
156+
next = LIST_ITEM (currentNode->adjnode.next, CM_MallocHeader, adjnode);
157+
}
158+
159+
if (next && !next->isAllocated) {
160+
CM_DBG_INFO ("Combining NEXT into CURRENT");
161+
currentNode->netNodeSize += next->netNodeSize;
162+
list_remove (&next->freenode);
163+
list_remove (&next->adjnode);
164+
}
165+
166+
if (prev && !prev->isAllocated) {
167+
CM_DBG_INFO ("Combining CURRENT into PREV");
168+
prev->netNodeSize += currentNode->netNodeSize;
169+
list_remove (&currentNode->freenode);
170+
list_remove (&currentNode->adjnode);
171+
}
172+
}
173+
174+
static CM_MallocHeader* s_createNewNode (void* at, size_t netSize)
175+
{
176+
// Node netSize too large. Not possible.
177+
cm_assert (((PTR)at + netSize - 1) < ((PTR)s_buffer + ARCH_MEM_LEN_BYTES_KMALLOC));
178+
179+
CM_MallocHeader* newH = at;
180+
newH->netNodeSize = netSize;
181+
newH->isAllocated = false;
182+
list_init (&newH->freenode);
183+
list_init (&newH->allocnode);
184+
list_init (&newH->adjnode);
185+
return newH;
186+
}
187+
188+
static CM_MallocHeader* s_getMallocHeaderFromList (ListNode* head, ListNode* node)
189+
{
190+
CM_MallocHeader* ret = NULL;
191+
if (head == &s_allocHead) {
192+
ret = LIST_ITEM (node, CM_MallocHeader, allocnode);
193+
} else if (head == &s_freeHead) {
194+
ret = LIST_ITEM (node, CM_MallocHeader, freenode);
195+
}
196+
197+
return ret;
198+
}
199+
200+
static CM_MallocHeader* s_findFirst (ListNode* head, FindCriteria criteria, PTR value)
201+
{
202+
bool found = false;
203+
ListNode* node = NULL;
204+
CM_MallocHeader* header = NULL;
205+
206+
CM_DBG_INFO ("Searching for value %lu (%lx)", value, value);
207+
208+
list_for_each (head, node)
209+
{
210+
header = s_getMallocHeaderFromList (head, node);
211+
CM_DBG_INFO (" (%px) Size = %lu...", header, header->netNodeSize);
212+
213+
switch (criteria) {
214+
case FIND_CRIT_NODE_ADDRESS:
215+
found = ((PTR)header == value);
216+
break;
217+
case FIND_CRIT_NODE_SIZE:
218+
found = (header->netNodeSize >= value);
219+
break;
220+
}
221+
222+
if (found) {
223+
CM_DBG_INFO ("Found.");
224+
break;
225+
}
226+
}
227+
return (found) ? header : NULL;
228+
}
229+
230+
static void s_splitFreeNode (size_t bytes, CM_MallocHeader* freeNodeHdr)
231+
{
232+
size_t netAllocSize = NET_ALLOCATION_SIZE (bytes);
233+
PTR splitAt = (PTR)freeNodeHdr + netAllocSize;
234+
size_t remainingSize = freeNodeHdr->netNodeSize - netAllocSize;
235+
cm_assert (remainingSize >= sizeof (CM_MallocHeader)); // Not enough space for kmalloc header
236+
237+
CM_MallocHeader* newFreeNodeHdr = s_createNewNode ((void*)splitAt, remainingSize);
238+
freeNodeHdr->netNodeSize = netAllocSize;
239+
freeNodeHdr->isAllocated = true;
240+
241+
list_add_after (&freeNodeHdr->freenode, &newFreeNodeHdr->freenode);
242+
list_add_after (&freeNodeHdr->adjnode, &newFreeNodeHdr->adjnode);
243+
list_remove (&freeNodeHdr->freenode);
244+
list_add_before (&s_allocHead, &freeNodeHdr->allocnode);
245+
}

0 commit comments

Comments
 (0)