1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2006-03-16     Bernard      the first version
9  * 2006-05-25     Bernard      rewrite vsprintf
10  * 2006-08-10     Bernard      add rt_show_version
11  * 2010-03-17     Bernard      remove rt_strlcpy function
12  *                             fix gcc compiling issue.
13  * 2010-04-15     Bernard      remove weak definition on ICCM16C compiler
14  * 2012-07-18     Arda         add the alignment display for signed integer
15  * 2012-11-23     Bernard      fix IAR compiler error.
16  * 2012-12-22     Bernard      fix rt_kprintf issue, which found by Grissiom.
17  * 2013-06-24     Bernard      remove rt_kprintf if RT_USING_CONSOLE is not defined.
18  * 2013-09-24     aozima       make sure the device is in STREAM mode when used by rt_kprintf.
19  * 2015-07-06     Bernard      Add rt_assert_handler routine.
20  * 2021-02-28     Meco Man     add RT_KSERVICE_USING_STDLIB
21  * 2021-12-20     Meco Man     implement rt_strcpy()
22  * 2022-01-07     Gabriel      add __on_rt_assert_hook
23  * 2022-06-04     Meco Man     remove strnlen
24  * 2022-08-24     Yunjie       make rt_memset word-independent to adapt to ti c28x (16bit word)
25  * 2022-08-30     Yunjie       make rt_vsnprintf adapt to ti c28x (16bit int)
26  * 2023-02-02     Bernard      add Smart ID for logo version show
27  * 2023-10-16     Shell        Add hook point for rt_malloc services
28  * 2023-10-21     Shell        support the common backtrace API which is arch-independent
29  * 2023-12-10     xqyjlj       perf rt_hw_interrupt_disable/enable, fix memheap lock
30  * 2024-03-10     Meco Man     move std libc related functions to rtklibc
31  */
32 
33 #include <rtthread.h>
34 
35 /* include rt_hw_backtrace macro defined in cpuport.h */
36 #define RT_HW_INCLUDE_CPUPORT
37 #include <rthw.h>
38 
39 #define DBG_TAG           "kernel.service"
40 #ifdef RT_DEBUG_DEVICE
41 #define DBG_LVL           DBG_LOG
42 #else
43 #define DBG_LVL           DBG_WARNING
44 #endif /* defined (RT_DEBUG_DEVICE) */
45 #include <rtdbg.h>
46 
47 #ifdef RT_USING_MODULE
48 #include <dlmodule.h>
49 #endif /* RT_USING_MODULE */
50 
51 #ifdef RT_USING_SMART
52 #include <lwp.h>
53 #include <lwp_user_mm.h>
54 #endif
55 
56 /**
57  * @addtogroup group_kernel_service
58  * @{
59  */
60 
61 #if defined(RT_USING_DEVICE) && defined(RT_USING_CONSOLE)
62 static rt_device_t _console_device = RT_NULL;
63 #endif
64 
rt_hw_us_delay(rt_uint32_t us)65 rt_weak void rt_hw_us_delay(rt_uint32_t us)
66 {
67     (void) us;
68     LOG_W("rt_hw_us_delay() doesn't support for this board."
69         "Please consider implementing rt_hw_us_delay() in another file.");
70 }
71 
rt_hw_cpu_reset(void)72 rt_weak void rt_hw_cpu_reset(void)
73 {
74     LOG_W("rt_hw_cpu_reset() doesn't support for this board."
75         "Please consider implementing rt_hw_cpu_reset() in another file.");
76     return;
77 }
78 
rt_hw_cpu_shutdown(void)79 rt_weak void rt_hw_cpu_shutdown(void)
80 {
81     LOG_I("CPU shutdown...");
82     LOG_W("Using default rt_hw_cpu_shutdown()."
83         "Please consider implementing rt_hw_cpu_shutdown() in another file.");
84     rt_hw_interrupt_disable();
85     RT_ASSERT(0);
86     return;
87 }
88 
89 /**
90  * @note can be overridden by cpuport.h which is defined by a specific arch
91  */
92 #ifndef RT_HW_BACKTRACE_FRAME_GET_SELF
93 
94 #ifdef __GNUC__
95     #define RT_HW_BACKTRACE_FRAME_GET_SELF(frame) do {          \
96         (frame)->fp = (rt_uintptr_t)__builtin_frame_address(0U);   \
97         (frame)->pc = ({__label__ pc; pc: (rt_uintptr_t)&&pc;});   \
98     } while (0)
99 
100 #else
101     #define RT_HW_BACKTRACE_FRAME_GET_SELF(frame) do {  \
102         (frame)->fp = 0;                                \
103         (frame)->pc = 0;                                \
104     } while (0)
105 
106 #endif /* __GNUC__ */
107 
108 #endif /* RT_HW_BACKTRACE_FRAME_GET_SELF */
109 
110 /**
111  * @brief Get the inner most frame of target thread
112  *
113  * @param thread the thread which frame belongs to
114  * @param frame the specified frame to be unwound
115  * @return rt_err_t 0 is succeed, otherwise a failure
116  */
rt_hw_backtrace_frame_get(rt_thread_t thread,struct rt_hw_backtrace_frame * frame)117 rt_weak rt_err_t rt_hw_backtrace_frame_get(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
118 {
119     RT_UNUSED(thread);
120     RT_UNUSED(frame);
121 
122     LOG_W("%s is not implemented", __func__);
123     return -RT_ENOSYS;
124 }
125 
126 /**
127  * @brief Unwind the target frame
128  *
129  * @param thread the thread which frame belongs to
130  * @param frame the specified frame to be unwound
131  * @return rt_err_t 0 is succeed, otherwise a failure
132  */
rt_hw_backtrace_frame_unwind(rt_thread_t thread,struct rt_hw_backtrace_frame * frame)133 rt_weak rt_err_t rt_hw_backtrace_frame_unwind(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
134 {
135     RT_UNUSED(thread);
136     RT_UNUSED(frame);
137 
138     LOG_W("%s is not implemented", __func__);
139     return -RT_ENOSYS;
140 }
141 
rt_hw_cpu_arch(void)142 rt_weak const char *rt_hw_cpu_arch(void)
143 {
144     return "unknown";
145 }
146 
147 /**
148  * @brief This function will show the version of rt-thread rtos
149  */
rt_show_version(void)150 void rt_show_version(void)
151 {
152     rt_kprintf("\n \\ | /\n");
153 #if defined(RT_USING_SMART)
154     rt_kprintf("- RT -     Thread Smart Operating System\n");
155 #elif defined(RT_USING_NANO)
156     rt_kprintf("- RT -     Thread Nano Operating System\n");
157 #else
158     rt_kprintf("- RT -     Thread Operating System\n");
159 #endif
160     rt_kprintf(" / | \\     %d.%d.%d build %s %s\n",
161                (rt_int32_t)RT_VERSION_MAJOR, (rt_int32_t)RT_VERSION_MINOR, (rt_int32_t)RT_VERSION_PATCH, __DATE__, __TIME__);
162     rt_kprintf(" 2006 - 2024 Copyright by RT-Thread team\n");
163 }
164 RTM_EXPORT(rt_show_version);
165 
166 #ifdef RT_USING_CONSOLE
167 #ifdef RT_USING_DEVICE
168 /**
169  * @brief  This function returns the device using in console.
170  *
171  * @return Returns the console device pointer or RT_NULL.
172  */
rt_console_get_device(void)173 rt_device_t rt_console_get_device(void)
174 {
175     return _console_device;
176 }
177 RTM_EXPORT(rt_console_get_device);
178 
179 /**
180  * @brief  This function will set a device as console device.
181  * After set a device to console, all output of rt_kprintf will be
182  * redirected to this new device.
183  *
184  * @param  name is the name of new console device.
185  *
186  * @return the old console device handler on successful, or RT_NULL on failure.
187  */
rt_console_set_device(const char * name)188 rt_device_t rt_console_set_device(const char *name)
189 {
190     rt_device_t old_device = _console_device;
191     rt_device_t new_device = rt_device_find(name);
192 
193     if (new_device != RT_NULL && new_device != old_device)
194     {
195         if (old_device != RT_NULL)
196         {
197             /* close old console device */
198             rt_device_close(old_device);
199         }
200 
201         /* set new console device */
202         rt_device_open(new_device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM);
203         _console_device = new_device;
204     }
205 
206     return old_device;
207 }
208 RTM_EXPORT(rt_console_set_device);
209 #endif /* RT_USING_DEVICE */
210 
rt_hw_console_output(const char * str)211 rt_weak void rt_hw_console_output(const char *str)
212 {
213     /* empty console output */
214     RT_UNUSED(str);
215 }
216 RTM_EXPORT(rt_hw_console_output);
217 
218 #ifdef RT_USING_THREADSAFE_PRINTF
219 
220 /* system console lock */
221 static struct rt_spinlock _syscon_lock = RT_SPINLOCK_INIT;
222 /* lock of kprintf buffer */
223 static struct rt_spinlock _prbuf_lock = RT_SPINLOCK_INIT;
224 /* current user of system console */
225 static rt_thread_t _pr_curr_user;
226 
227 #ifdef RT_USING_DEBUG
228 static rt_base_t _pr_critical_level;
229 #endif /* RT_USING_DEBUG */
230 
231 /* nested level of current user */
232 static volatile int _pr_curr_user_nested;
233 
rt_console_current_user(void)234 rt_thread_t rt_console_current_user(void)
235 {
236     return _pr_curr_user;
237 }
238 
_console_take(void)239 static void _console_take(void)
240 {
241     rt_ubase_t level = rt_spin_lock_irqsave(&_syscon_lock);
242     rt_thread_t self_thread = rt_thread_self();
243     rt_base_t critical_level;
244     RT_UNUSED(critical_level);
245 
246     while (_pr_curr_user != self_thread)
247     {
248         if (_pr_curr_user == RT_NULL)
249         {
250             /* no preemption is allowed to avoid dead lock */
251             critical_level = rt_enter_critical();
252 #ifdef RT_USING_DEBUG
253             _pr_critical_level = _syscon_lock.critical_level;
254             _syscon_lock.critical_level = critical_level;
255 #endif
256             _pr_curr_user = self_thread;
257             break;
258         }
259         else
260         {
261             rt_spin_unlock_irqrestore(&_syscon_lock, level);
262             rt_thread_yield();
263             level = rt_spin_lock_irqsave(&_syscon_lock);
264         }
265     }
266 
267     _pr_curr_user_nested++;
268 
269     rt_spin_unlock_irqrestore(&_syscon_lock, level);
270 }
271 
_console_release(void)272 static void _console_release(void)
273 {
274     rt_ubase_t level = rt_spin_lock_irqsave(&_syscon_lock);
275     rt_thread_t self_thread = rt_thread_self();
276     RT_UNUSED(self_thread);
277 
278     RT_ASSERT(_pr_curr_user == self_thread);
279 
280     _pr_curr_user_nested--;
281     if (!_pr_curr_user_nested)
282     {
283         _pr_curr_user = RT_NULL;
284 
285 #ifdef RT_USING_DEBUG
286         rt_exit_critical_safe(_syscon_lock.critical_level);
287         _syscon_lock.critical_level = _pr_critical_level;
288 #else
289         rt_exit_critical();
290 #endif
291     }
292     rt_spin_unlock_irqrestore(&_syscon_lock, level);
293 }
294 
295 #define CONSOLE_TAKE          _console_take()
296 #define CONSOLE_RELEASE       _console_release()
297 #define PRINTF_BUFFER_TAKE    rt_ubase_t level = rt_spin_lock_irqsave(&_prbuf_lock)
298 #define PRINTF_BUFFER_RELEASE rt_spin_unlock_irqrestore(&_prbuf_lock, level)
299 #else
300 
301 #define CONSOLE_TAKE
302 #define CONSOLE_RELEASE
303 #define PRINTF_BUFFER_TAKE
304 #define PRINTF_BUFFER_RELEASE
305 #endif /* RT_USING_THREADSAFE_PRINTF */
306 
307 /**
308  * @brief This function will put string to the console.
309  *
310  * @param str is the string output to the console.
311  */
_kputs(const char * str,long len)312 static void _kputs(const char *str, long len)
313 {
314 #ifdef RT_USING_DEVICE
315     rt_device_t console_device = rt_console_get_device();
316 #endif /* RT_USING_DEVICE */
317 
318     CONSOLE_TAKE;
319 
320 #ifdef RT_USING_DEVICE
321     if (console_device == RT_NULL)
322     {
323         rt_hw_console_output(str);
324     }
325     else
326     {
327         rt_device_write(console_device, 0, str, len);
328     }
329 #else
330     RT_UNUSED(len);
331     rt_hw_console_output(str);
332 #endif /* RT_USING_DEVICE */
333 
334     CONSOLE_RELEASE;
335 }
336 
337 /**
338  * @brief This function will put string to the console.
339  *
340  * @param str is the string output to the console.
341  */
rt_kputs(const char * str)342 void rt_kputs(const char *str)
343 {
344     if (!str)
345     {
346         return;
347     }
348 
349     _kputs(str, rt_strlen(str));
350 }
351 
352 /**
353  * @brief This function will print a formatted string on system console.
354  *
355  * @param fmt is the format parameters.
356  *
357  * @return The number of characters actually written to buffer.
358  */
rt_kprintf(const char * fmt,...)359 rt_weak int rt_kprintf(const char *fmt, ...)
360 {
361     va_list args;
362     rt_size_t length = 0;
363     static char rt_log_buf[RT_CONSOLEBUF_SIZE];
364 
365     va_start(args, fmt);
366     PRINTF_BUFFER_TAKE;
367 
368     /* the return value of vsnprintf is the number of bytes that would be
369      * written to buffer had if the size of the buffer been sufficiently
370      * large excluding the terminating null byte. If the output string
371      * would be larger than the rt_log_buf, we have to adjust the output
372      * length. */
373     length = rt_vsnprintf(rt_log_buf, sizeof(rt_log_buf) - 1, fmt, args);
374     if (length > RT_CONSOLEBUF_SIZE - 1)
375     {
376         length = RT_CONSOLEBUF_SIZE - 1;
377     }
378 
379     _kputs(rt_log_buf, length);
380 
381     PRINTF_BUFFER_RELEASE;
382     va_end(args);
383 
384     return length;
385 }
386 RTM_EXPORT(rt_kprintf);
387 #endif /* RT_USING_CONSOLE */
388 
389 /**
390  * @brief Print backtrace of current thread to system console device
391  *
392  * @return rt_err_t 0 is success, otherwise a failure
393  */
rt_backtrace(void)394 rt_weak rt_err_t rt_backtrace(void)
395 {
396     struct rt_hw_backtrace_frame frame;
397     rt_thread_t thread = rt_thread_self();
398 
399     RT_HW_BACKTRACE_FRAME_GET_SELF(&frame);
400     if (!frame.fp)
401         return -RT_EINVAL;
402 
403     /* we don't want this frame to be printed which is nearly garbage info */
404     rt_hw_backtrace_frame_unwind(thread, &frame);
405 
406     return rt_backtrace_frame(thread, &frame);
407 }
408 
409 /**
410  * @brief Print backtrace from frame to system console device
411  *
412  * @param thread the thread which frame belongs to
413  * @param frame where backtrace starts from
414  * @return rt_err_t 0 is success, otherwise a failure
415  */
rt_backtrace_frame(rt_thread_t thread,struct rt_hw_backtrace_frame * frame)416 rt_weak rt_err_t rt_backtrace_frame(rt_thread_t thread, struct rt_hw_backtrace_frame *frame)
417 {
418     long nesting = 0;
419 
420     rt_kprintf("please use: addr2line -e rtthread.elf -a -f\n");
421 
422     while (nesting < RT_BACKTRACE_LEVEL_MAX_NR)
423     {
424         rt_kprintf(" 0x%lx", (rt_ubase_t)frame->pc);
425         if (rt_hw_backtrace_frame_unwind(thread, frame))
426         {
427             break;
428         }
429         nesting++;
430     }
431     rt_kprintf("\n");
432     return RT_EOK;
433 }
434 
435 /**
436  * @brief Print backtrace from buffer to system console
437  *
438  * @param buffer where traced frames saved
439  * @param buflen number of items in buffer
440  * @return rt_err_t 0 is success, otherwise a failure
441  */
rt_backtrace_formatted_print(rt_ubase_t * buffer,long buflen)442 rt_weak rt_err_t rt_backtrace_formatted_print(rt_ubase_t *buffer, long buflen)
443 {
444     rt_kprintf("please use: addr2line -e rtthread.elf -a -f\n");
445 
446     for (rt_size_t i = 0; i < buflen && buffer[i] != 0; i++)
447     {
448         rt_kprintf(" 0x%lx", (rt_ubase_t)buffer[i]);
449     }
450 
451     rt_kprintf("\n");
452     return RT_EOK;
453 }
454 
455 
456 /**
457  * @brief Print backtrace from frame to the given buffer
458  *
459  * @param thread the thread which frame belongs to
460  * @param frame where backtrace starts from. NULL if it's the current one
461  * @param skip the number of frames to discarded counted from calling function.
462  *             Noted that the inner most frame is always discarded and not counted,
463  *             which is obviously reasonable since that's this function itself.
464  * @param buffer where traced frames saved
465  * @param buflen max number of items can be saved in buffer. If there are no more
466  *               than buflen items to be saved, there will be a NULL after the
467  *               last saved item in the buffer.
468  * @return rt_err_t 0 is success, otherwise a failure
469  */
rt_backtrace_to_buffer(rt_thread_t thread,struct rt_hw_backtrace_frame * frame,long skip,rt_ubase_t * buffer,long buflen)470 rt_weak rt_err_t rt_backtrace_to_buffer(rt_thread_t thread,
471                                         struct rt_hw_backtrace_frame *frame,
472                                         long skip,
473                                         rt_ubase_t *buffer,
474                                         long buflen)
475 {
476     long nesting = 0;
477     struct rt_hw_backtrace_frame cur_frame;
478 
479     if (!thread)
480         return -RT_EINVAL;
481 
482     RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
483 
484     if (!frame)
485     {
486         frame = &cur_frame;
487         RT_HW_BACKTRACE_FRAME_GET_SELF(frame);
488         if (!frame->fp)
489             return -RT_EINVAL;
490     }
491 
492     /* discard frames as required. The inner most is always threw. */
493     do {
494         rt_hw_backtrace_frame_unwind(thread, frame);
495     } while (skip-- > 0);
496 
497     while (nesting < buflen)
498     {
499         *buffer++ = (rt_ubase_t)frame->pc;
500         if (rt_hw_backtrace_frame_unwind(thread, frame))
501         {
502             break;
503         }
504         nesting++;
505     }
506 
507     if (nesting < buflen)
508         *buffer = RT_NULL;
509 
510     return RT_EOK;
511 }
512 
513 /**
514  * @brief Print backtrace of a thread to system console device
515  *
516  * @param thread which call stack is traced
517  * @return rt_err_t 0 is success, otherwise a failure
518  */
rt_backtrace_thread(rt_thread_t thread)519 rt_err_t rt_backtrace_thread(rt_thread_t thread)
520 {
521     rt_err_t rc;
522     struct rt_hw_backtrace_frame frame;
523     if (thread)
524     {
525         rc = rt_hw_backtrace_frame_get(thread, &frame);
526         if (rc == RT_EOK)
527         {
528             rc = rt_backtrace_frame(thread, &frame);
529         }
530     }
531     else
532     {
533         rc = -RT_EINVAL;
534     }
535     return rc;
536 }
537 
538 #if defined(RT_USING_LIBC) && defined(RT_USING_FINSH)
539 #include <stdlib.h> /* for string service */
540 
cmd_backtrace(int argc,char ** argv)541 static void cmd_backtrace(int argc, char** argv)
542 {
543     rt_uintptr_t pid;
544     char *end_ptr;
545 
546     if (argc != 2)
547     {
548         if (argc == 1)
549         {
550             rt_kprintf("[INFO] No thread specified\n"
551                 "[HELP] You can use commands like: backtrace %p\n"
552                 "Printing backtrace of calling stack...\n",
553                 rt_thread_self());
554             rt_backtrace();
555             return ;
556         }
557         else
558         {
559             rt_kprintf("please use: backtrace [thread_address]\n");
560             return;
561         }
562     }
563 
564     pid = strtoul(argv[1], &end_ptr, 0);
565     if (end_ptr == argv[1])
566     {
567         rt_kprintf("Invalid input: %s\n", argv[1]);
568         return ;
569     }
570 
571     if (pid && rt_object_get_type((void *)pid) == RT_Object_Class_Thread)
572     {
573         rt_thread_t target = (rt_thread_t)pid;
574         rt_kprintf("backtrace %s(0x%lx), from %s\n", target->parent.name, pid, argv[1]);
575         rt_backtrace_thread(target);
576     }
577     else
578         rt_kprintf("Invalid pid: %ld\n", pid);
579 }
580 MSH_CMD_EXPORT_ALIAS(cmd_backtrace, backtrace, print backtrace of a thread);
581 
582 #endif /* RT_USING_LIBC */
583 
584 #if defined(RT_USING_HEAP) && !defined(RT_USING_USERHEAP)
585 #ifdef RT_USING_HOOK
586 static void (*rt_malloc_hook)(void **ptr, rt_size_t size);
587 static void (*rt_realloc_entry_hook)(void **ptr, rt_size_t size);
588 static void (*rt_realloc_exit_hook)(void **ptr, rt_size_t size);
589 static void (*rt_free_hook)(void **ptr);
590 
591 /**
592  * @ingroup group_hook
593  * @{
594  */
595 
596 /**
597  * @brief This function will set a hook function, which will be invoked when a memory
598  *        block is allocated from heap memory.
599  *
600  * @param hook the hook function.
601  */
rt_malloc_sethook(void (* hook)(void ** ptr,rt_size_t size))602 void rt_malloc_sethook(void (*hook)(void **ptr, rt_size_t size))
603 {
604     rt_malloc_hook = hook;
605 }
606 
607 /**
608  * @brief This function will set a hook function, which will be invoked when a memory
609  *        block is allocated from heap memory.
610  *
611  * @param hook the hook function.
612  */
rt_realloc_set_entry_hook(void (* hook)(void ** ptr,rt_size_t size))613 void rt_realloc_set_entry_hook(void (*hook)(void **ptr, rt_size_t size))
614 {
615     rt_realloc_entry_hook = hook;
616 }
617 
618 /**
619  * @brief This function will set a hook function, which will be invoked when a memory
620  *        block is allocated from heap memory.
621  *
622  * @param hook the hook function.
623  */
rt_realloc_set_exit_hook(void (* hook)(void ** ptr,rt_size_t size))624 void rt_realloc_set_exit_hook(void (*hook)(void **ptr, rt_size_t size))
625 {
626     rt_realloc_exit_hook = hook;
627 }
628 
629 /**
630  * @brief This function will set a hook function, which will be invoked when a memory
631  *        block is released to heap memory.
632  *
633  * @param hook the hook function
634  */
rt_free_sethook(void (* hook)(void ** ptr))635 void rt_free_sethook(void (*hook)(void **ptr))
636 {
637     rt_free_hook = hook;
638 }
639 
640 /**@}*/
641 
642 #endif /* RT_USING_HOOK */
643 
644 #if defined(RT_USING_HEAP_ISR)
645 static struct rt_spinlock _heap_spinlock;
646 #elif defined(RT_USING_MUTEX)
647 static struct rt_mutex _lock;
648 #endif
649 
_heap_lock_init(void)650 rt_inline void _heap_lock_init(void)
651 {
652 #if defined(RT_USING_HEAP_ISR)
653     rt_spin_lock_init(&_heap_spinlock);
654 #elif defined(RT_USING_MUTEX)
655     rt_mutex_init(&_lock, "heap", RT_IPC_FLAG_PRIO);
656 #endif
657 }
658 
_heap_lock(void)659 rt_inline rt_base_t _heap_lock(void)
660 {
661 #if defined(RT_USING_HEAP_ISR)
662     return rt_spin_lock_irqsave(&_heap_spinlock);
663 #elif defined(RT_USING_MUTEX)
664     if (rt_thread_self())
665         return rt_mutex_take(&_lock, RT_WAITING_FOREVER);
666     else
667         return RT_EOK;
668 #else
669     rt_enter_critical();
670     return RT_EOK;
671 #endif
672 }
673 
_heap_unlock(rt_base_t level)674 rt_inline void _heap_unlock(rt_base_t level)
675 {
676 #if defined(RT_USING_HEAP_ISR)
677     rt_spin_unlock_irqrestore(&_heap_spinlock, level);
678 #elif defined(RT_USING_MUTEX)
679     RT_ASSERT(level == RT_EOK);
680     if (rt_thread_self())
681         rt_mutex_release(&_lock);
682 #else
683     rt_exit_critical();
684 #endif
685 }
686 
687 #ifdef RT_USING_UTESTCASES
688 /* export to utest to observe the inner statements */
689 #ifdef _MSC_VER
690 #define rt_heap_lock() _heap_lock()
691 #define rt_heap_unlock() _heap_unlock()
692 #else
693 rt_base_t rt_heap_lock(void) __attribute__((alias("_heap_lock")));
694 void rt_heap_unlock(rt_base_t level) __attribute__((alias("_heap_unlock")));
695 #endif /* _MSC_VER */
696 #endif
697 
698 #if defined(RT_USING_SMALL_MEM_AS_HEAP)
699 static rt_smem_t system_heap;
_smem_info(rt_size_t * total,rt_size_t * used,rt_size_t * max_used)700 rt_inline void _smem_info(rt_size_t *total,
701     rt_size_t *used, rt_size_t *max_used)
702 {
703     if (total)
704         *total = system_heap->total;
705     if (used)
706         *used = system_heap->used;
707     if (max_used)
708         *max_used = system_heap->max;
709 }
710 #define _MEM_INIT(_name, _start, _size) \
711     system_heap = rt_smem_init(_name, _start, _size)
712 #define _MEM_MALLOC(_size)  \
713     rt_smem_alloc(system_heap, _size)
714 #define _MEM_REALLOC(_ptr, _newsize)\
715     rt_smem_realloc(system_heap, _ptr, _newsize)
716 #define _MEM_FREE(_ptr) \
717     rt_smem_free(_ptr)
718 #define _MEM_INFO(_total, _used, _max)  \
719     _smem_info(_total, _used, _max)
720 #elif defined(RT_USING_MEMHEAP_AS_HEAP)
721 static struct rt_memheap system_heap;
722 void *_memheap_alloc(struct rt_memheap *heap, rt_size_t size);
723 void _memheap_free(void *rmem);
724 void *_memheap_realloc(struct rt_memheap *heap, void *rmem, rt_size_t newsize);
725 #define _MEM_INIT(_name, _start, _size) \
726     do {\
727         rt_memheap_init(&system_heap, _name, _start, _size); \
728         system_heap.locked = RT_TRUE; \
729     } while(0)
730 #define _MEM_MALLOC(_size)  \
731     _memheap_alloc(&system_heap, _size)
732 #define _MEM_REALLOC(_ptr, _newsize)    \
733     _memheap_realloc(&system_heap, _ptr, _newsize)
734 #define _MEM_FREE(_ptr)   \
735     _memheap_free(_ptr)
736 #define _MEM_INFO(_total, _used, _max)   \
737     rt_memheap_info(&system_heap, _total, _used, _max)
738 #elif defined(RT_USING_SLAB_AS_HEAP)
739 static rt_slab_t system_heap;
_slab_info(rt_size_t * total,rt_size_t * used,rt_size_t * max_used)740 rt_inline void _slab_info(rt_size_t *total,
741     rt_size_t *used, rt_size_t *max_used)
742 {
743     if (total)
744         *total = system_heap->total;
745     if (used)
746         *used = system_heap->used;
747     if (max_used)
748         *max_used = system_heap->max;
749 }
750 #define _MEM_INIT(_name, _start, _size) \
751     system_heap = rt_slab_init(_name, _start, _size)
752 #define _MEM_MALLOC(_size)  \
753     rt_slab_alloc(system_heap, _size)
754 #define _MEM_REALLOC(_ptr, _newsize)    \
755     rt_slab_realloc(system_heap, _ptr, _newsize)
756 #define _MEM_FREE(_ptr) \
757     rt_slab_free(system_heap, _ptr)
758 #define _MEM_INFO       _slab_info
759 #else
760 #define _MEM_INIT(...)
761 #define _MEM_MALLOC(...)     RT_NULL
762 #define _MEM_REALLOC(...)    RT_NULL
763 #define _MEM_FREE(...)
764 #define _MEM_INFO(...)
765 #endif
766 
767 /**
768  * @brief This function will do the generic system heap initialization.
769  *
770  * @param begin_addr the beginning address of system page.
771  *
772  * @param end_addr the end address of system page.
773  */
rt_system_heap_init_generic(void * begin_addr,void * end_addr)774 void rt_system_heap_init_generic(void *begin_addr, void *end_addr)
775 {
776     rt_uintptr_t begin_align = RT_ALIGN((rt_uintptr_t)begin_addr, RT_ALIGN_SIZE);
777     rt_uintptr_t end_align   = RT_ALIGN_DOWN((rt_uintptr_t)end_addr, RT_ALIGN_SIZE);
778 
779     RT_ASSERT(end_align > begin_align);
780 
781     /* Initialize system memory heap */
782     _MEM_INIT("heap", (void *)begin_align, end_align - begin_align);
783     /* Initialize multi thread contention lock */
784     _heap_lock_init();
785 }
786 
787 /**
788  * @brief This function will init system heap. User can override this API to
789  *        complete other works, like heap sanitizer initialization.
790  *
791  * @param begin_addr the beginning address of system page.
792  *
793  * @param end_addr the end address of system page.
794  */
rt_system_heap_init(void * begin_addr,void * end_addr)795 rt_weak void rt_system_heap_init(void *begin_addr, void *end_addr)
796 {
797     rt_system_heap_init_generic(begin_addr, end_addr);
798 }
799 
800 /**
801  * @brief Allocate a block of memory with a minimum of 'size' bytes.
802  *
803  * @param size is the minimum size of the requested block in bytes.
804  *
805  * @return the pointer to allocated memory or NULL if no free memory was found.
806  */
rt_malloc(rt_size_t size)807 rt_weak void *rt_malloc(rt_size_t size)
808 {
809     rt_base_t level;
810     void *ptr;
811 
812     /* Enter critical zone */
813     level = _heap_lock();
814     /* allocate memory block from system heap */
815     ptr = _MEM_MALLOC(size);
816     /* Exit critical zone */
817     _heap_unlock(level);
818     /* call 'rt_malloc' hook */
819     RT_OBJECT_HOOK_CALL(rt_malloc_hook, (&ptr, size));
820     return ptr;
821 }
822 RTM_EXPORT(rt_malloc);
823 
824 /**
825  * @brief This function will change the size of previously allocated memory block.
826  *
827  * @param ptr is the pointer to memory allocated by rt_malloc.
828  *
829  * @param newsize is the required new size.
830  *
831  * @return the changed memory block address.
832  */
rt_realloc(void * ptr,rt_size_t newsize)833 rt_weak void *rt_realloc(void *ptr, rt_size_t newsize)
834 {
835     rt_base_t level;
836     void *nptr;
837 
838     /* Entry hook */
839     RT_OBJECT_HOOK_CALL(rt_realloc_entry_hook, (&ptr, newsize));
840     /* Enter critical zone */
841     level = _heap_lock();
842     /* Change the size of previously allocated memory block */
843     nptr = _MEM_REALLOC(ptr, newsize);
844     /* Exit critical zone */
845     _heap_unlock(level);
846     /* Exit hook */
847     RT_OBJECT_HOOK_CALL(rt_realloc_exit_hook, (&nptr, newsize));
848     return nptr;
849 }
850 RTM_EXPORT(rt_realloc);
851 
852 /**
853  * @brief  This function will contiguously allocate enough space for count objects
854  *         that are size bytes of memory each and returns a pointer to the allocated
855  *         memory.
856  *
857  * @note   The allocated memory is filled with bytes of value zero.
858  *
859  * @param  count is the number of objects to allocate.
860  *
861  * @param  size is the size of one object to allocate.
862  *
863  * @return pointer to allocated memory / NULL pointer if there is an error.
864  */
rt_calloc(rt_size_t count,rt_size_t size)865 rt_weak void *rt_calloc(rt_size_t count, rt_size_t size)
866 {
867     void *p;
868 
869     /* allocate 'count' objects of size 'size' */
870     p = rt_malloc(count * size);
871     /* zero the memory */
872     if (p)
873     {
874         rt_memset(p, 0, count * size);
875     }
876     return p;
877 }
878 RTM_EXPORT(rt_calloc);
879 
880 /**
881  * @brief This function will release the previously allocated memory block by
882  *        rt_malloc. The released memory block is taken back to system heap.
883  *
884  * @param ptr the address of memory which will be released.
885  */
rt_free(void * ptr)886 rt_weak void rt_free(void *ptr)
887 {
888     rt_base_t level;
889 
890     /* call 'rt_free' hook */
891     RT_OBJECT_HOOK_CALL(rt_free_hook, (&ptr));
892     /* NULL check */
893     if (ptr == RT_NULL) return;
894     /* Enter critical zone */
895     level = _heap_lock();
896     _MEM_FREE(ptr);
897     /* Exit critical zone */
898     _heap_unlock(level);
899 }
900 RTM_EXPORT(rt_free);
901 
902 /**
903 * @brief This function will caculate the total memory, the used memory, and
904 *        the max used memory.
905 *
906 * @param total is a pointer to get the total size of the memory.
907 *
908 * @param used is a pointer to get the size of memory used.
909 *
910 * @param max_used is a pointer to get the maximum memory used.
911 */
rt_memory_info(rt_size_t * total,rt_size_t * used,rt_size_t * max_used)912 rt_weak void rt_memory_info(rt_size_t *total,
913                             rt_size_t *used,
914                             rt_size_t *max_used)
915 {
916     rt_base_t level;
917 
918     /* Enter critical zone */
919     level = _heap_lock();
920     _MEM_INFO(total, used, max_used);
921     /* Exit critical zone */
922     _heap_unlock(level);
923 }
924 RTM_EXPORT(rt_memory_info);
925 
926 #if defined(RT_USING_SLAB) && defined(RT_USING_SLAB_AS_HEAP)
rt_page_alloc(rt_size_t npages)927 void *rt_page_alloc(rt_size_t npages)
928 {
929     rt_base_t level;
930     void *ptr;
931 
932     /* Enter critical zone */
933     level = _heap_lock();
934     /* alloc page */
935     ptr = rt_slab_page_alloc(system_heap, npages);
936     /* Exit critical zone */
937     _heap_unlock(level);
938     return ptr;
939 }
940 
rt_page_free(void * addr,rt_size_t npages)941 void rt_page_free(void *addr, rt_size_t npages)
942 {
943     rt_base_t level;
944 
945     /* Enter critical zone */
946     level = _heap_lock();
947     /* free page */
948     rt_slab_page_free(system_heap, addr, npages);
949     /* Exit critical zone */
950     _heap_unlock(level);
951 }
952 #endif
953 
954 /**
955  * @brief  This function allocates a memory block, which address is aligned to the
956  * specified alignment size.
957  *
958  * @param  size is the allocated memory block size.
959  *
960  * @param  align is the alignment size.
961  *
962  * @return The memory block address was returned successfully, otherwise it was
963  *         returned empty RT_NULL.
964  */
rt_malloc_align(rt_size_t size,rt_size_t align)965 rt_weak void *rt_malloc_align(rt_size_t size, rt_size_t align)
966 {
967     void *ptr = RT_NULL;
968     void *align_ptr = RT_NULL;
969     int uintptr_size = 0;
970     rt_size_t align_size = 0;
971 
972     /* sizeof pointer */
973     uintptr_size = sizeof(void*);
974     uintptr_size -= 1;
975 
976     /* align the alignment size to uintptr size byte */
977     align = ((align + uintptr_size) & ~uintptr_size);
978 
979     /* get total aligned size */
980     align_size = ((size + uintptr_size) & ~uintptr_size) + align;
981     /* allocate memory block from heap */
982     ptr = rt_malloc(align_size);
983     if (ptr != RT_NULL)
984     {
985         /* the allocated memory block is aligned */
986         if (((rt_uintptr_t)ptr & (align - 1)) == 0)
987         {
988             align_ptr = (void *)((rt_uintptr_t)ptr + align);
989         }
990         else
991         {
992             align_ptr = (void *)(((rt_uintptr_t)ptr + (align - 1)) & ~(align - 1));
993         }
994 
995         /* set the pointer before alignment pointer to the real pointer */
996         *((rt_uintptr_t *)((rt_uintptr_t)align_ptr - sizeof(void *))) = (rt_uintptr_t)ptr;
997 
998         ptr = align_ptr;
999     }
1000 
1001     return ptr;
1002 }
1003 RTM_EXPORT(rt_malloc_align);
1004 
1005 /**
1006  * @brief This function release the memory block, which is allocated by
1007  * rt_malloc_align function and address is aligned.
1008  *
1009  * @param ptr is the memory block pointer.
1010  */
rt_free_align(void * ptr)1011 rt_weak void rt_free_align(void *ptr)
1012 {
1013     void *real_ptr = RT_NULL;
1014 
1015     /* NULL check */
1016     if (ptr == RT_NULL) return;
1017     real_ptr = (void *) * (rt_uintptr_t *)((rt_uintptr_t)ptr - sizeof(void *));
1018     rt_free(real_ptr);
1019 }
1020 RTM_EXPORT(rt_free_align);
1021 #endif /* RT_USING_HEAP */
1022 
1023 #ifndef RT_USING_CPU_FFS
1024 #ifdef RT_USING_TINY_FFS
1025 const rt_uint8_t __lowest_bit_bitmap[] =
1026 {
1027     /*  0 - 7  */  0,  1,  2, 27,  3, 24, 28, 32,
1028     /*  8 - 15 */  4, 17, 25, 31, 29, 12, 32, 14,
1029     /* 16 - 23 */  5,  8, 18, 32, 26, 23, 32, 16,
1030     /* 24 - 31 */ 30, 11, 13,  7, 32, 22, 15, 10,
1031     /* 32 - 36 */  6, 21,  9, 20, 19
1032 };
1033 
1034 /**
1035  * @brief This function finds the first bit set (beginning with the least significant bit)
1036  * in value and return the index of that bit.
1037  *
1038  * Bits are numbered starting at 1 (the least significant bit).  A return value of
1039  * zero from any of these functions means that the argument was zero.
1040  *
1041  * @param value is the value to find the first bit set in.
1042  *
1043  * @return return the index of the first bit set. If value is 0, then this function
1044  * shall return 0.
1045  */
__rt_ffs(int value)1046 int __rt_ffs(int value)
1047 {
1048     return __lowest_bit_bitmap[(rt_uint32_t)(value & (value - 1) ^ value) % 37];
1049 }
1050 #else
1051 const rt_uint8_t __lowest_bit_bitmap[] =
1052 {
1053     /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1054     /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1055     /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1056     /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1057     /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1058     /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1059     /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1060     /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1061     /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1062     /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1063     /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1064     /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1065     /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1066     /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1067     /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
1068     /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
1069 };
1070 
1071 /**
1072  * @brief This function finds the first bit set (beginning with the least significant bit)
1073  * in value and return the index of that bit.
1074  *
1075  * Bits are numbered starting at 1 (the least significant bit).  A return value of
1076  * zero from any of these functions means that the argument was zero.
1077  *
1078  * @param value is the value to find the first bit set in.
1079  *
1080  * @return Return the index of the first bit set. If value is 0, then this function
1081  *         shall return 0.
1082  */
__rt_ffs(int value)1083 int __rt_ffs(int value)
1084 {
1085     if (value == 0)
1086     {
1087         return 0;
1088     }
1089 
1090     if (value & 0xff)
1091     {
1092         return __lowest_bit_bitmap[value & 0xff] + 1;
1093     }
1094 
1095     if (value & 0xff00)
1096     {
1097         return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9;
1098     }
1099 
1100     if (value & 0xff0000)
1101     {
1102         return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17;
1103     }
1104 
1105     return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25;
1106 }
1107 #endif /* RT_USING_TINY_FFS */
1108 #endif /* RT_USING_CPU_FFS */
1109 
1110 #ifdef RT_DEBUGING_ASSERT
1111 /* RT_ASSERT(EX)'s hook */
1112 
1113 void (*rt_assert_hook)(const char *ex, const char *func, rt_size_t line);
1114 
1115 /**
1116  * This function will set a hook function to RT_ASSERT(EX). It will run when the expression is false.
1117  *
1118  * @param hook is the hook function.
1119  */
rt_assert_set_hook(void (* hook)(const char * ex,const char * func,rt_size_t line))1120 void rt_assert_set_hook(void (*hook)(const char *ex, const char *func, rt_size_t line))
1121 {
1122     rt_assert_hook = hook;
1123 }
1124 
1125 /**
1126  * The RT_ASSERT function.
1127  *
1128  * @param ex_string is the assertion condition string.
1129  *
1130  * @param func is the function name when assertion.
1131  *
1132  * @param line is the file line number when assertion.
1133  */
rt_assert_handler(const char * ex_string,const char * func,rt_size_t line)1134 void rt_assert_handler(const char *ex_string, const char *func, rt_size_t line)
1135 {
1136     volatile char dummy = 0;
1137 
1138     if (rt_assert_hook == RT_NULL)
1139     {
1140 #ifdef RT_USING_MODULE
1141         if (dlmodule_self())
1142         {
1143             /* close assertion module */
1144             dlmodule_exit(-1);
1145         }
1146         else
1147 #endif /*RT_USING_MODULE*/
1148         {
1149             rt_kprintf("(%s) assertion failed at function:%s, line number:%d \n", ex_string, func, line);
1150             rt_backtrace();
1151             while (dummy == 0);
1152         }
1153     }
1154     else
1155     {
1156         rt_assert_hook(ex_string, func, line);
1157     }
1158 }
1159 RTM_EXPORT(rt_assert_handler);
1160 #endif /* RT_DEBUGING_ASSERT */
1161 
1162 /**@}*/
1163