init.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. * Copyright (c) 2017 Richard Braun.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. *
  18. * The main purpose of the init module is to provide a convenient interface
  19. * to declare initialization operations and their dependency, infer an order
  20. * of execution based on the resulting graph (which must be acyclic), and
  21. * run the operations in that order. Topological sorting is achieved using
  22. * Kahn's algorithm.
  23. */
  24. #include <assert.h>
  25. #include <errno.h>
  26. #include <stdbool.h>
  27. #include <stddef.h>
  28. #include <stdint.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <kern/init.h>
  32. #include <kern/slist.h>
  33. #include <kern/macros.h>
  34. #include <machine/cpu.h>
  35. extern struct init_op _init_ops;
  36. extern struct init_op _init_ops_end;
  37. static_assert (sizeof (struct init_op) == INIT_OP_ALIGN,
  38. "invalid init_op size");
  39. /*
  40. * List of initialization operations.
  41. *
  42. * This type is used for the output of the topological sort.
  43. */
  44. struct init_ops_list
  45. {
  46. struct slist ops;
  47. size_t size;
  48. };
  49. static void __init
  50. init_ops_list_init (struct init_ops_list *list)
  51. {
  52. slist_init (&list->ops);
  53. list->size = 0;
  54. }
  55. static size_t __init
  56. init_ops_list_size (const struct init_ops_list *list)
  57. {
  58. return (list->size);
  59. }
  60. static void __init
  61. init_ops_list_push (struct init_ops_list *list, struct init_op *op)
  62. {
  63. slist_insert_head (&list->ops, &op->list_node);
  64. list->size++;
  65. }
  66. static struct init_op* __init
  67. init_ops_list_pop (struct init_ops_list *list)
  68. {
  69. if (! list->size)
  70. return (NULL);
  71. _Auto op = slist_first_entry (&list->ops, struct init_op, list_node);
  72. slist_remove (&list->ops, NULL);
  73. --list->size;
  74. return (op);
  75. }
  76. /*
  77. * Stack of initialization operations.
  78. *
  79. * This type is used internally by the topological sort algorithm.
  80. */
  81. struct init_ops_stack
  82. {
  83. struct slist ops;
  84. };
  85. static void __init
  86. init_ops_stack_init (struct init_ops_stack *stack)
  87. {
  88. slist_init (&stack->ops);
  89. }
  90. static void __init
  91. init_ops_stack_push (struct init_ops_stack *stack, struct init_op *op)
  92. {
  93. slist_insert_head (&stack->ops, &op->stack_node);
  94. }
  95. static struct init_op * __init
  96. init_ops_stack_pop (struct init_ops_stack *stack)
  97. {
  98. if (slist_empty (&stack->ops))
  99. return (NULL);
  100. _Auto op = slist_first_entry (&stack->ops, struct init_op, stack_node);
  101. slist_remove (&stack->ops, NULL);
  102. return (op);
  103. }
  104. static struct init_op_dep* __init
  105. init_op_get_dep (const struct init_op *op, size_t index)
  106. {
  107. assert (index < op->nr_deps);
  108. return (&op->deps[index]);
  109. }
  110. static void __init
  111. init_op_init (struct init_op *op)
  112. {
  113. for (size_t i = 0; i < op->nr_deps; i++)
  114. {
  115. _Auto dep = init_op_get_dep (op, i);
  116. ++dep->op->nr_parents;
  117. assert (dep->op->nr_parents);
  118. }
  119. }
  120. static bool __init
  121. init_op_orphan (const struct init_op *op)
  122. {
  123. return (op->nr_parents == 0);
  124. }
  125. static bool __init
  126. init_op_pending (const struct init_op *op)
  127. {
  128. return (op->state == INIT_OP_STATE_PENDING);
  129. }
  130. static void __init
  131. init_op_set_pending (struct init_op *op)
  132. {
  133. assert (op->state == INIT_OP_STATE_UNLINKED);
  134. op->state = INIT_OP_STATE_PENDING;
  135. }
  136. static bool __init
  137. init_op_complete (const struct init_op *op)
  138. {
  139. return (op->state == INIT_OP_STATE_COMPLETE);
  140. }
  141. static void __init
  142. init_op_set_complete (struct init_op *op)
  143. {
  144. assert (init_op_pending (op));
  145. op->state = INIT_OP_STATE_COMPLETE;
  146. }
  147. static bool __init
  148. init_op_ready (const struct init_op *op)
  149. {
  150. for (size_t i = 0; i < op->nr_deps; i++)
  151. {
  152. _Auto dep = init_op_get_dep (op, i);
  153. if (!init_op_complete (dep->op) ||
  154. (dep->required && dep->op->error))
  155. return (false);
  156. }
  157. return (true);
  158. }
  159. static void __init
  160. init_op_run (struct init_op *op)
  161. {
  162. if (init_op_ready (op))
  163. op->error = op->fn ();
  164. init_op_set_complete (op);
  165. }
  166. #ifdef CONFIG_INIT_DEBUG
  167. #define INIT_DEBUG_LOG_BUFFER_SIZE 8192
  168. struct init_debug_log
  169. {
  170. char buffer[INIT_DEBUG_LOG_BUFFER_SIZE];
  171. size_t index;
  172. };
  173. /*
  174. * Buffers used to store an easy-to-dump text representation of init operations.
  175. *
  176. * These buffers are meant to be retrieved through a debugging interface such
  177. * as JTAG.
  178. */
  179. struct init_debug_logs
  180. {
  181. struct init_debug_log roots; // graph roots.
  182. struct init_debug_log cycles; // operations with dependency cycles.
  183. struct init_debug_log pending; // operations successfully sorted.
  184. struct init_debug_log complete; // executed operations.
  185. };
  186. static struct init_debug_logs init_debug_logs;
  187. static void __init
  188. init_debug_log_append (struct init_debug_log *log, const char *name)
  189. {
  190. if (log->index == sizeof (log->buffer))
  191. return;
  192. size_t size = sizeof (log->buffer) - log->index;
  193. log->index += snprintf (log->buffer + log->index, size, "%s ", name);
  194. if (log->index >= sizeof (log->buffer))
  195. log->index = sizeof (log->buffer);
  196. }
  197. static void __init
  198. init_debug_append_root (const struct init_op *op)
  199. {
  200. init_debug_log_append (&init_debug_logs.roots, op->name);
  201. }
  202. static void __init
  203. init_debug_append_cycle (const struct init_op *op)
  204. {
  205. init_debug_log_append (&init_debug_logs.cycles, op->name);
  206. }
  207. static void __init
  208. init_debug_append_pending (const struct init_op *op)
  209. {
  210. init_debug_log_append (&init_debug_logs.pending, op->name);
  211. }
  212. static void __init
  213. init_debug_append_complete (const struct init_op *op)
  214. {
  215. init_debug_log_append (&init_debug_logs.complete, op->name);
  216. }
  217. static void __init
  218. init_debug_scan_not_pending (void)
  219. {
  220. for (_Auto op = &_init_ops; op < &_init_ops_end; ++op)
  221. if (!init_op_pending (op))
  222. init_debug_append_cycle (op);
  223. }
  224. #else
  225. #define init_debug_append_root(roots)
  226. #define init_debug_append_pending(op)
  227. #define init_debug_append_complete(op)
  228. #define init_debug_scan_not_pending()
  229. #endif
  230. static void __init
  231. init_add_pending_op (struct init_ops_list *pending_ops, struct init_op *op)
  232. {
  233. assert (!init_op_pending (op));
  234. init_op_set_pending (op);
  235. init_ops_list_push (pending_ops, op);
  236. init_debug_append_pending (op);
  237. }
  238. static void __init
  239. init_op_visit (struct init_op *op, struct init_ops_stack *stack)
  240. {
  241. for (size_t i = 0; i < op->nr_deps; i++)
  242. {
  243. _Auto dep = init_op_get_dep (op, i);
  244. assert (dep->op->nr_parents != 0);
  245. --dep->op->nr_parents;
  246. if (init_op_orphan (dep->op))
  247. init_ops_stack_push (stack, dep->op);
  248. }
  249. }
  250. static void __init
  251. init_check_ops_alignment (void)
  252. {
  253. uintptr_t start = (uintptr_t)&_init_ops,
  254. end = (uintptr_t)&_init_ops_end;
  255. if (((end - start) % INIT_OP_ALIGN) != 0)
  256. cpu_halt ();
  257. }
  258. static void __init
  259. init_bootstrap (void)
  260. {
  261. init_check_ops_alignment ();
  262. for (_Auto op = &_init_ops; op < &_init_ops_end; op++)
  263. init_op_init (op);
  264. }
  265. static void __init
  266. init_scan_roots (struct init_ops_stack *stack)
  267. {
  268. init_ops_stack_init (stack);
  269. for (_Auto op = &_init_ops; op < &_init_ops_end; op++)
  270. if (init_op_orphan (op))
  271. {
  272. init_ops_stack_push (stack, op);
  273. init_debug_append_root (op);
  274. }
  275. }
  276. static void __init
  277. init_scan_ops (struct init_ops_list *pending_ops)
  278. {
  279. struct init_ops_stack stack;
  280. init_scan_roots (&stack);
  281. while (1)
  282. {
  283. _Auto op = init_ops_stack_pop (&stack);
  284. if (! op)
  285. break;
  286. init_add_pending_op (pending_ops, op);
  287. init_op_visit (op, &stack);
  288. }
  289. init_debug_scan_not_pending ();
  290. size_t nr_ops = &_init_ops_end - &_init_ops;
  291. if (init_ops_list_size (pending_ops) != nr_ops)
  292. cpu_halt ();
  293. }
  294. static void __init
  295. init_run_ops (struct init_ops_list *pending_ops)
  296. {
  297. while (1)
  298. {
  299. _Auto op = init_ops_list_pop (pending_ops);
  300. if (! op)
  301. break;
  302. init_op_run (op);
  303. init_debug_append_complete (op);
  304. }
  305. }
  306. void __init
  307. init_setup (void)
  308. {
  309. struct init_ops_list pending_ops;
  310. init_ops_list_init (&pending_ops);
  311. init_bootstrap ();
  312. init_scan_ops (&pending_ops);
  313. init_run_ops (&pending_ops);
  314. }