1 /*
2 * Copyright (c) 2022 Winner Microelectronics Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 /*****************************************************************************
17 *
18 * File Name : wm_mem.c
19 *
20 * Description: memory manager Module
21 *
22 * Copyright (c) 2014 Winner Micro Electronic Design Co., Ltd.
23 * All rights reserved.
24 *
25 * Author : dave
26 *
27 * Date : 2014-6-12
28 *****************************************************************************/
29
30 #include <string.h>
31 #include "wm_osal.h"
32 #include "list.h"
33 #include "wm_mem.h"
34 #if TLS_OS_LITEOS
35 #include "los_memory.h"
36 #endif
37 extern u8 tls_get_isr_count(void);
38 /**
39 * This variable is set if the memory mananger has been initialized.
40 * This is available only for debug version of the driver
41 */
42 bool memory_manager_initialized = false;
43 /**
44 * This mutex is used to synchronize the list of allocated
45 * memory blocks. This is a debug version only feature
46 */
47 tls_os_sem_t *mem_sem;
48 #if WM_MEM_DEBUG
49
50 struct dl_list memory_used_list;
51 struct dl_list memory_free_list;
52 #define MEM_BLOCK_SIZE 800
53 MEMORY_BLOCK mem_blocks[MEM_BLOCK_SIZE];
54
55 u32 alloc_heap_mem_bytes = 0;
56 u32 alloc_heap_mem_blk_cnt = 0;
57 u32 alloc_heap_mem_max_size = 0;
58
59 #define PRE_OVERSIZE 0
60 #define OVERSIZE 0
61
62 /**
63 * This is a debug only function that performs memory management operations for us.
64 * Memory allocated using this function is tracked, flagged when leaked, and caught for
65 * overflows and underflows.
66 *
67 * \param size The size in bytes of memory to
68 * allocate
69 *
70 * \param file The full path of file where this
71 * function is invoked from
72 * \param line The line number in the file where this
73 * method was called from
74 * \return Pointer to the allocated memory or NULL in case of a failure
75 */
mem_alloc_debug(u32 size,char * file,int line)76 void *mem_alloc_debug(u32 size, char* file, int line)
77 {
78 void *buf = NULL;
79 u32 pad_len;
80 u32 cpu_sr;
81 //
82 // If the memory manager has not been initialized, do so now
83 //
84 cpu_sr = tls_os_set_critical();
85 if (!memory_manager_initialized) {
86 tls_os_status_t os_status;
87 memory_manager_initialized = true;
88 //
89 // NOTE: If two thread allocate the very first allocation simultaneously
90 // it could cause double initialization of the memory manager. This is a
91 // highly unlikely scenario and will occur in debug versions only.
92 //
93 os_status = tls_os_sem_create(&mem_sem, 1);
94 if (os_status != TLS_OS_SUCCESS)
95 printf("mem_alloc_debug: tls_os_sem_create mem_sem error\r\n");
96 dl_list_init(&memory_used_list);
97 dl_list_init(&memory_free_list);
98 for (int i = 0; i < MEM_BLOCK_SIZE; i++) {
99 dl_list_add_tail(&memory_free_list, &mem_blocks[i].list);
100 }
101 }
102 tls_os_release_critical(cpu_sr);
103
104 tls_os_sem_acquire(mem_sem, 0);
105 cpu_sr = tls_os_set_critical();
106 //
107 // Allocate the required memory chunk plus header and trailer bytes
108 //
109 pad_len = sizeof(u32) - (size & 0x3);
110 buf = malloc(sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + size + pad_len + OVERSIZE + sizeof(MEMORY_PATTERN));
111 if (buf) {
112 //
113 // Memory allocation succeeded. Add information about the allocated
114 // block in the list that tracks all allocations.
115 //
116 PMEMORY_PATTERN mem_ptn_hd;
117 PMEMORY_PATTERN mem_ptn_tl;
118 PMEMORY_BLOCK mem_blk_hd1;
119
120 if (dl_list_empty(&memory_free_list)) {
121 printf("Memory blocks empty!\r\n");
122 free(buf);
123 tls_os_release_critical(cpu_sr);
124 tls_os_sem_release(mem_sem);
125 tls_mem_alloc_info();
126 return NULL;
127 }
128 mem_blk_hd1 = dl_list_first(&memory_free_list, MEMORY_BLOCK, list);
129 dl_list_del(&mem_blk_hd1->list);
130 dl_list_add_tail(&memory_used_list, &mem_blk_hd1->list);
131 alloc_heap_mem_bytes += size+sizeof(MEMORY_PATTERN) + sizeof(MEMORY_PATTERN) + pad_len +\
132 PRE_OVERSIZE + OVERSIZE;
133 alloc_heap_mem_blk_cnt++;
134 if (alloc_heap_mem_bytes > alloc_heap_mem_max_size) {
135 alloc_heap_mem_max_size = alloc_heap_mem_bytes;
136 }
137
138 mem_blk_hd1->pad = pad_len;
139 mem_blk_hd1->file = file;
140 mem_blk_hd1->line = line;
141 mem_blk_hd1->length = size;
142 mem_blk_hd1->header_pattern = (u32)buf;
143
144 // Fill in the memory header and trailer
145 mem_ptn_hd = (PMEMORY_PATTERN)buf;
146 mem_ptn_hd->pattern0= MEM_HEADER_PATTERN;
147
148 mem_ptn_tl = (PMEMORY_PATTERN)(((u8 *)(buf))+size + sizeof(MEMORY_PATTERN)+pad_len + \
149 PRE_OVERSIZE + OVERSIZE);
150 mem_ptn_tl->pattern0= MEM_TAILER_PATTERN;
151
152 // Jump ahead by memory header so pointer returned to caller points at the right place
153 buf = ((u8 *)buf) + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE;
154 } else {
155 printf("==>Memory was allocated from %s at line %d with length %d, allocated size %d, count %d\r\n",
156 file,
157 line,
158 size, alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt);
159 tls_os_release_critical(cpu_sr);
160 tls_os_sem_release(mem_sem);
161 tls_mem_alloc_info();
162 return buf;
163 }
164 tls_os_release_critical(cpu_sr);
165 tls_os_sem_release(mem_sem);
166 return buf;
167 }
168
mem_calloc_debug(u32 n,u32 size,char * file,int line)169 void *mem_calloc_debug(u32 n, u32 size, char* file, int line)
170 {
171 void *buf = NULL;
172 u32 pad_len;
173 u32 cpu_sr;
174 //
175 // If the memory manager has not been initialized, do so now
176 //
177 cpu_sr = tls_os_set_critical();
178 if (!memory_manager_initialized) {
179 tls_os_status_t os_status;
180 memory_manager_initialized = true;
181 //
182 // NOTE: If two thread allocate the very first allocation simultaneously
183 // it could cause double initialization of the memory manager. This is a
184 // highly unlikely scenario and will occur in debug versions only.
185 //
186 os_status = tls_os_sem_create(&mem_sem, 1);
187 if (os_status != TLS_OS_SUCCESS)
188 printf("mem_alloc_debug: tls_os_sem_create mem_sem error\r\n");
189 dl_list_init(&memory_used_list);
190 dl_list_init(&memory_free_list);
191 for (int i = 0; i < MEM_BLOCK_SIZE; i++) {
192 dl_list_add_tail(&memory_free_list, &mem_blocks[i].list);
193 }
194 }
195 tls_os_release_critical(cpu_sr);
196
197 tls_os_sem_acquire(mem_sem, 0);
198 cpu_sr = tls_os_set_critical();
199 //
200 // Allocate the required memory chunk plus header and trailer bytes
201 //
202 pad_len = sizeof(u32) - ((n*size) & 0x3);
203 buf = malloc(sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + n*size + pad_len + OVERSIZE + sizeof(MEMORY_PATTERN));
204 if (buf) {
205 //
206 // Memory allocation succeeded. Add information about the allocated
207 // block in the list that tracks all allocations.
208 //
209 PMEMORY_PATTERN mem_ptn_hd;
210 PMEMORY_PATTERN mem_ptn_tl;
211 PMEMORY_BLOCK mem_blk_hd1;
212
213 if (dl_list_empty(&memory_free_list)) {
214 printf("Memory blocks empty!\r\n");
215 free(buf);
216 tls_os_release_critical(cpu_sr);
217 tls_os_sem_release(mem_sem);
218 tls_mem_alloc_info();
219 return NULL;
220 }
221 mem_blk_hd1 = dl_list_first(&memory_free_list, MEMORY_BLOCK, list);
222 dl_list_del(&mem_blk_hd1->list);
223 dl_list_add_tail(&memory_used_list, &mem_blk_hd1->list);
224 alloc_heap_mem_bytes += n*size+sizeof(MEMORY_PATTERN)+sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE;
225 alloc_heap_mem_blk_cnt++;
226 if (alloc_heap_mem_bytes > alloc_heap_mem_max_size) {
227 alloc_heap_mem_max_size = alloc_heap_mem_bytes;
228 }
229
230 mem_blk_hd1->pad = pad_len;
231 mem_blk_hd1->file = file;
232 mem_blk_hd1->line = line;
233 mem_blk_hd1->length = n*size;
234 mem_blk_hd1->header_pattern = (u32)buf;
235
236 // Fill in the memory header and trailer
237 mem_ptn_hd = (PMEMORY_PATTERN)buf;
238 mem_ptn_hd->pattern0= MEM_HEADER_PATTERN;
239
240 mem_ptn_tl = (PMEMORY_PATTERN)(((u8 *)(buf))+n*size + sizeof(MEMORY_PATTERN)+pad_len + PRE_OVERSIZE + OVERSIZE);
241 mem_ptn_tl->pattern0= MEM_TAILER_PATTERN;
242
243 // Jump ahead by memory header so pointer returned to caller points at the right place
244 buf = ((u8 *)buf) + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE;
245 } else {
246 printf("==>Memory was allocated from %s at line %d with length %d, allocated size %d, count %d\r\n",
247 file,
248 line,
249 n*size, alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt);
250
251 tls_os_release_critical(cpu_sr);
252 tls_os_sem_release(mem_sem);
253 tls_mem_alloc_info();
254 return buf;
255 }
256 tls_os_release_critical(cpu_sr);
257 tls_os_sem_release(mem_sem);
258 return buf;
259 }
260 /**
261 * This routine is called to free memory which was previously allocated using MpAllocateMemory function.
262 * Before freeing the memory, this function checks and makes sure that no overflow or underflows have
263 * happened and will also try to detect multiple frees of the same memory chunk.
264 *
265 * \param p Pointer to allocated memory
266 */
mem_free_debug(void * p,char * file,int line)267 void mem_free_debug(void *p, char* file, int line)
268 {
269 PMEMORY_PATTERN mem_ptn_hd;
270 PMEMORY_PATTERN mem_ptn_tl;
271 PMEMORY_BLOCK mem_blk_hd1;
272 u8 needfree = 0;
273 u8 haserr = 0;
274 u32 cpu_sr;
275
276 // Jump back by memory header size so we can get to the header
277 mem_ptn_hd = (PMEMORY_PATTERN) (((u8 *)p) - sizeof(MEMORY_PATTERN) - PRE_OVERSIZE);
278 tls_os_sem_acquire(mem_sem, 0);
279 cpu_sr = tls_os_set_critical();
280 dl_list_for_each(mem_blk_hd1, &memory_used_list, MEMORY_BLOCK, list) {
281 if (mem_blk_hd1->header_pattern == (u32)mem_ptn_hd) {
282 needfree = 1;
283 break;
284 }
285 }
286 if (needfree) {
287 dl_list_del(&mem_blk_hd1->list);
288 dl_list_add_tail(&memory_free_list, &mem_blk_hd1->list);
289 alloc_heap_mem_bytes -= mem_blk_hd1->length + sizeof(MEMORY_PATTERN) + sizeof(MEMORY_PATTERN) +
290 PRE_OVERSIZE + OVERSIZE + mem_blk_hd1->pad;
291 alloc_heap_mem_blk_cnt--;
292 }
293 if (needfree == 0) {
294 printf("Memory Block %p was deallocated from %s at line %d \r\n", mem_ptn_hd, file, line);
295 printf("Memory %p has been deallocated!\r\n", p);
296 dl_list_for_each_reverse(mem_blk_hd1, &memory_free_list, MEMORY_BLOCK, list) {
297 if (mem_blk_hd1->header_pattern == (u32)mem_ptn_hd) {
298 printf("Memory Block %p has been put free list!\r\n", mem_ptn_hd);
299 break;
300 }
301 }
302 tls_os_release_critical(cpu_sr);
303 tls_os_sem_release(mem_sem);
304 tls_mem_alloc_info();
305 return;
306 }
307 mem_ptn_tl = (PMEMORY_PATTERN) ((u8 *)p + mem_blk_hd1->length + mem_blk_hd1->pad + OVERSIZE);
308 //
309 // Check that header was not corrupted
310 //
311 if (mem_ptn_hd->pattern0 != MEM_HEADER_PATTERN) {
312 printf("Memory %p was deallocated from %s at line %d \r\n", p, file, line);
313 printf("Memory header corruption due to underflow detected at memory block %p\r\n",
314 mem_ptn_hd);
315 printf("Header pattern 0(0x%x)\r\n", mem_ptn_hd->pattern0);
316 printf("Memory was allocated from %s at line %d with length %d\r\n",
317 mem_blk_hd1->file,
318 mem_blk_hd1->line,
319 mem_blk_hd1->length);
320 haserr = 1;
321 }
322
323 //
324 // Check that trailer was not corrupted
325 //
326 if (mem_ptn_tl->pattern0 != MEM_TAILER_PATTERN) {
327 printf("Memory %p was deallocated from %s at line %d \r\n", p, file, line);
328 printf("Memory tailer corruption due to overflow detected at %p\r\n", mem_ptn_hd);
329 printf("Tailer pattern 0(0x%x)\r\n", mem_ptn_tl->pattern0);
330 printf("Memory was allocated from %s at line %d with length %d\r\n",
331 mem_blk_hd1->file, mem_blk_hd1->line, mem_blk_hd1->length);
332 haserr = 1;
333 }
334 if (needfree) {
335 free(mem_ptn_hd);
336 }
337
338 tls_os_release_critical(cpu_sr);
339 tls_os_sem_release(mem_sem);
340
341 if (haserr)
342 tls_mem_alloc_info();
343 }
344
mem_realloc_debug(void * mem_address,u32 size,char * file,int line)345 void *mem_realloc_debug(void *mem_address, u32 size, char* file, int line)
346 {
347 void *mem_re_addr;
348 u32 cpu_sr;
349
350 if ((mem_re_addr = mem_alloc_debug(size, file, line)) == NULL) {
351 printf("mem_realloc_debug failed(size=%d).\r\n", size);
352 return NULL;
353 }
354 if (mem_address != NULL) {
355 cpu_sr = tls_os_set_critical();
356 memcpy_s(mem_re_addr, sizeof(mem_re_addr), mem_address, size);
357 tls_os_release_critical(cpu_sr);
358 mem_free_debug(mem_address, file, line);
359 }
360 return mem_re_addr;
361 }
362
tls_mem_alloc_info(void)363 void tls_mem_alloc_info(void)
364 {
365 int i;
366 MEMORY_BLOCK *pos;
367 u32 cpu_sr;
368
369 tls_os_sem_acquire(mem_sem, 0);
370 cpu_sr = tls_os_set_critical();
371 printf("==>Memory was allocated size %d, count %d\r\n",
372 alloc_heap_mem_bytes, alloc_heap_mem_blk_cnt);
373 i = 1;
374 dl_list_for_each(pos, &memory_used_list, MEMORY_BLOCK, list) {
375 printf("Block(%2d): addr<%p>, file<%s>, line<%d>, length<%d>\r\n",
376 i, pos->header_pattern, pos->file, pos->line, pos->length);
377 i++;
378 }
379 tls_os_release_critical(cpu_sr);
380 tls_os_sem_release(mem_sem);
381 }
382
is_safe_addr_debug(void * p,u32 len,char * file,int line)383 int is_safe_addr_debug(void* p, u32 len, char* file, int line)
384 {
385 int i;
386 MEMORY_BLOCK *pos;
387 u32 cpu_sr;
388
389 if (((u32)p) >= (u32)0x64ae8 || ((u32)p) < (u32)0x54ae8) {
390 return 1;
391 }
392 tls_os_sem_acquire(mem_sem, 0);
393 cpu_sr = tls_os_set_critical();
394 i = 1;
395 dl_list_for_each(pos, &memory_used_list, MEMORY_BLOCK, list) {
396 if ((pos->header_pattern + sizeof (MEMORY_PATTERN) + PRE_OVERSIZE) <= ((u32)p) && ((u32)p) <= \
397 ((u32)(pos->header_pattern + sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + pos->length))) {
398 if (((u32)p) + len > ((u32)(pos->header_pattern + sizeof(MEMORY_PATTERN) + PRE_OVERSIZE + pos->length))) {
399 printf("==>Memory oversize. Block(%2d): addr<%p>, file<%s>, line<%d>, length<%d>\r\n",
400 i, pos->header_pattern, pos->file, pos->line, pos->length);
401 break;
402 } else {
403 tls_os_release_critical(cpu_sr);
404 tls_os_sem_release(mem_sem);
405 return 1;
406 }
407 }
408 i++;
409 }
410 tls_os_release_critical(cpu_sr);
411 tls_os_sem_release(mem_sem);
412 printf("==>Memory is not safe addr<%p>, file<%s>, line<%d>.\r\n", p, file, line);
413 return 0;
414 }
415
416 #else /* WM_MEM_DEBUG */
mem_alloc_debug(u32 size)417 void *mem_alloc_debug(u32 size)
418 {
419 u32 *buffer = (u32 *)LOS_MemAlloc(OS_SYS_MEM_ADDR, size);
420
421 if (buffer == NULL)
422 printf("malloc error \n");
423
424 return buffer;
425 }
426
mem_free_debug(void * p)427 void mem_free_debug(void *p)
428 {
429 int ret = LOS_MemFree(OS_SYS_MEM_ADDR, p);
430 if (ret) {
431 printf("mem free error\n");
432 }
433 }
434
mem_realloc_debug(void * mem_address,u32 size)435 void *mem_realloc_debug(void *mem_address, u32 size)
436 {
437 u32 *mem_re_addr = (u32 *)LOS_MemRealloc(OS_SYS_MEM_ADDR, mem_address, size);
438
439 return mem_re_addr;
440 }
441
mem_calloc_debug(u32 n,u32 size)442 void *mem_calloc_debug(u32 n, u32 size)
443 {
444 u32 *buffer = (u32 *)LOS_MemAlloc(OS_SYS_MEM_ADDR, n*size);
445 if (buffer) {
446 memset_s(buffer, sizeof(buffer), 0, n*size);
447 }
448
449 return buffer;
450 }
451 #endif /* WM_MEM_DEBUG */
452
tls_mem_get_avail_heapsize(void)453 u32 tls_mem_get_avail_heapsize(void)
454 {
455 LOS_MEM_POOL_STATUS status = {0};
456 if (LOS_MemInfoGet(OS_SYS_MEM_ADDR, &status) == LOS_NOK) {
457 return 0;
458 }
459 return status.totalFreeSize;
460 }