blob: 72d33ceac7abcab57fda0b1a5df9417f0d6837aa [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2013 Travis Geiselbrecht
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24/*
25 * Global init hook mechanism. Allows code anywhere in the system to define
26 * a init hook that is called at increasing init levels as the system is
27 * initialized.
28 */
29#include <arch/ops.h>
30#include <lk/init.h>
31
32#include <assert.h>
33#include <compiler.h>
34#include <debug.h>
35#include <trace.h>
36
37#define LOCAL_TRACE 0
38#define TRACE_INIT (LK_DEBUGLEVEL >= 2)
39#ifndef EARLIEST_TRACE_LEVEL
40#define EARLIEST_TRACE_LEVEL LK_INIT_LEVEL_TARGET_EARLY
41#endif
42
43extern const struct lk_init_struct __lk_init[];
44extern const struct lk_init_struct __lk_init_end[];
45
46void lk_init_level(enum lk_init_flags required_flag, uint start_level, uint stop_level)
47{
48 LTRACEF("flags %#x, start_level %#x, stop_level %#x\n",
49 required_flag, start_level, stop_level);
50
51 ASSERT(start_level > 0);
52 uint last_called_level = start_level - 1;
53 const struct lk_init_struct *last = NULL;
54 for (;;) {
55 /* search for the lowest uncalled hook to call */
56 LTRACEF("last %p, last_called_level %#x\n", last, last_called_level);
57
58 const struct lk_init_struct *found = NULL;
59 bool seen_last = false;
60 for (const struct lk_init_struct *ptr = __lk_init; ptr != __lk_init_end; ptr++) {
61 LTRACEF("looking at %p (%s) level %#x, flags %#x, seen_last %d\n", ptr, ptr->name, ptr->level, ptr->flags, seen_last);
62
63 if (ptr == last)
64 seen_last = true;
65
66 /* reject the easy ones */
67 if (!(ptr->flags & required_flag))
68 continue;
69 if (ptr->level > stop_level)
70 continue;
71 if (ptr->level < last_called_level)
72 continue;
73 if (found && found->level <= ptr->level)
74 continue;
75
76 /* keep the lowest one we haven't called yet */
77 if (ptr->level >= start_level && ptr->level > last_called_level) {
78 found = ptr;
79 continue;
80 }
81
82 /* if we're at the same level as the last one we called and we've
83 * already passed over it this time around, we can mark this one
84 * and early terminate the loop.
85 */
86 if (ptr->level == last_called_level && ptr != last && seen_last) {
87 found = ptr;
88 break;
89 }
90 }
91
92 if (!found)
93 break;
94
95#if TRACE_INIT
96 if (found->level >= EARLIEST_TRACE_LEVEL) {
97 printf("INIT: cpu %d, calling hook %p (%s) at level %#x, flags %#x\n",
98 arch_curr_cpu_num(), found->hook, found->name, found->level, found->flags);
99 }
100#endif
101 found->hook(found->level);
102 last_called_level = found->level;
103 last = found;
104 }
105}
106
107#if 0
108void test_hook(uint level)
109{
110 LTRACEF("level %#x\n", level);
111}
112void test_hook1(uint level)
113{
114 LTRACEF("level %#x\n", level);
115}
116void test_hook1a(uint level)
117{
118 LTRACEF("level %#x\n", level);
119}
120void test_hook1b(uint level)
121{
122 LTRACEF("level %#x\n", level);
123}
124void test_hook2(uint level)
125{
126 LTRACEF("level %#x\n", level);
127}
128
129LK_INIT_HOOK(test, test_hook, 1);
130LK_INIT_HOOK(test1, test_hook1, 1);
131LK_INIT_HOOK(test2, test_hook2, 2);
132LK_INIT_HOOK(test1a, test_hook1a, 1);
133LK_INIT_HOOK(test1b, test_hook1b, 1);
134#endif