1 /*
2  * Copyright (c) 2017 Oticon A/S
3  * Copyright (c) 2023 Nordic Semiconductor ASA
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /*
9  * Native simulator entry point (main)
10  *
11  * Documentation can be found starting in docs/README.md
12  */
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdbool.h>
17 #include "nsi_cpun_if.h"
18 #include "nsi_tasks.h"
19 #include "nsi_cmdline_main_if.h"
20 #include "nsi_utils.h"
21 #include "nsi_hw_scheduler.h"
22 #include "nsi_config.h"
23 #include "nsi_cpu_ctrl.h"
24 
nsi_exit_inner(int exit_code)25 int nsi_exit_inner(int exit_code)
26 {
27 	static int max_exit_code;
28 	int cpu_ret;
29 
30 	max_exit_code = NSI_MAX(exit_code, max_exit_code);
31 	/*
32 	 * nsif_cpun_cleanup may not return if this is called from a SW thread,
33 	 * but instead it would get nsi_exit() recalled again
34 	 * ASAP from the HW thread
35 	 */
36 	for (int i = 0; i < NSI_N_CPUS; i++) {
37 		cpu_ret = nsif_cpun_cleanup(i);
38 		max_exit_code = NSI_MAX(cpu_ret, max_exit_code);
39 	}
40 
41 	nsi_run_tasks(NSITASK_ON_EXIT_PRE_LEVEL);
42 	nsi_hws_cleanup();
43 	nsi_run_tasks(NSITASK_ON_EXIT_POST_LEVEL);
44 	return max_exit_code;
45 }
46 
nsi_exit(int exit_code)47 NSI_FUNC_NORETURN void nsi_exit(int exit_code)
48 {
49 	exit(nsi_exit_inner(exit_code));
50 }
51 
52 /**
53  * Run all early native simulator initialization steps, including command
54  * line parsing and CPU start, until we are ready to let the HW models
55  * run via nsi_hws_one_event()
56  *
57  * Note: This API should normally only be called by the native simulator main()
58  */
nsi_init(int argc,char * argv[])59 void nsi_init(int argc, char *argv[])
60 {
61 	/*
62 	 * Let's ensure that even if we are redirecting to a file, we get stdout
63 	 * and stderr line buffered (default for console)
64 	 * Note that glibc ignores size. But just in case we set a reasonable
65 	 * number in case somebody tries to compile against a different library
66 	 */
67 	setvbuf(stdout, NULL, _IOLBF, 512);
68 	setvbuf(stderr, NULL, _IOLBF, 512);
69 
70 	nsi_run_tasks(NSITASK_PRE_BOOT_1_LEVEL);
71 	for (int i = 0; i < NSI_N_CPUS; i++) {
72 		nsif_cpun_pre_cmdline_hooks(i);
73 	}
74 
75 	nsi_handle_cmd_line(argc, argv);
76 
77 	nsi_run_tasks(NSITASK_PRE_BOOT_2_LEVEL);
78 	for (int i = 0; i < NSI_N_CPUS; i++) {
79 		nsif_cpun_pre_hw_init_hooks(i);
80 	}
81 
82 	nsi_run_tasks(NSITASK_HW_INIT_LEVEL);
83 	nsi_hws_init();
84 
85 	nsi_run_tasks(NSITASK_PRE_BOOT_3_LEVEL);
86 
87 	nsi_cpu_auto_boot();
88 
89 	nsi_run_tasks(NSITASK_FIRST_SLEEP_LEVEL);
90 }
91 
92 /**
93  * Execute the simulator for at least the specified timeout, then
94  * return.  Note that this does not affect event timing, so the "next
95  * event" may be significantly after the request if the hardware has
96  * not been configured to e.g. send an interrupt when expected.
97  *
98  * Note: This API should normally only be called by the native simulator main()
99  */
nsi_exec_for(uint64_t us)100 void nsi_exec_for(uint64_t us)
101 {
102 	uint64_t start = nsi_hws_get_time();
103 
104 	do {
105 		nsi_hws_one_event();
106 	} while (nsi_hws_get_time() < (start + us));
107 }
108 
109 #ifndef NSI_NO_MAIN
110 
111 /**
112  *
113  * Note that this main() is not used when building fuzz cases,
114  * as libfuzzer has its own main(),
115  * and calls the "OS" through a per-case fuzz test entry point.
116  */
main(int argc,char * argv[])117 int main(int argc, char *argv[])
118 {
119 	nsi_init(argc, argv);
120 	while (true) {
121 		nsi_hws_one_event();
122 	}
123 
124 	/* This line should be unreachable */
125 	return 1; /* LCOV_EXCL_LINE */
126 }
127 
128 #endif /* NSI_NO_MAIN */
129