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 void nsi_exit(int exit_code)
48 {
49 	exit(nsi_exit_inner(exit_code));
50 }
51 
52 /**
53  * Run all early native_posix initialization steps, including command
54  * line parsing and CPU start, until we are ready to let the HW models
55  * run via hwm_one_event()
56  */
nsi_init(int argc,char * argv[])57 static void nsi_init(int argc, char *argv[])
58 {
59 	/*
60 	 * Let's ensure that even if we are redirecting to a file, we get stdout
61 	 * and stderr line buffered (default for console)
62 	 * Note that glibc ignores size. But just in case we set a reasonable
63 	 * number in case somebody tries to compile against a different library
64 	 */
65 	setvbuf(stdout, NULL, _IOLBF, 512);
66 	setvbuf(stderr, NULL, _IOLBF, 512);
67 
68 	nsi_run_tasks(NSITASK_PRE_BOOT_1_LEVEL);
69 	for (int i = 0; i < NSI_N_CPUS; i++) {
70 		nsif_cpun_pre_cmdline_hooks(i);
71 	}
72 
73 	nsi_handle_cmd_line(argc, argv);
74 
75 	nsi_run_tasks(NSITASK_PRE_BOOT_2_LEVEL);
76 	for (int i = 0; i < NSI_N_CPUS; i++) {
77 		nsif_cpun_pre_hw_init_hooks(i);
78 	}
79 
80 	nsi_run_tasks(NSITASK_HW_INIT_LEVEL);
81 	nsi_hws_init();
82 
83 	nsi_run_tasks(NSITASK_PRE_BOOT_3_LEVEL);
84 
85 	nsi_cpu_auto_boot();
86 
87 	nsi_run_tasks(NSITASK_FIRST_SLEEP_LEVEL);
88 }
89 
90 /**
91  * Execute the simulator for at least the specified timeout, then
92  * return.  Note that this does not affect event timing, so the "next
93  * event" may be significantly after the request if the hardware has
94  * not been configured to e.g. send an interrupt when expected.
95  */
nsi_exec_for(uint64_t us)96 void nsi_exec_for(uint64_t us)
97 {
98 	uint64_t start = nsi_hws_get_time();
99 
100 	do {
101 		nsi_hws_one_event();
102 	} while (nsi_hws_get_time() < (start + us));
103 }
104 
105 #ifndef NSI_LIBFUZZER
106 
107 /**
108  *
109  * Note that this main() is not used when building fuzz cases,
110  * as libfuzzer has its own main(),
111  * and calls the "OS" through a per-case fuzz test entry point.
112  */
main(int argc,char * argv[])113 int main(int argc, char *argv[])
114 {
115 	nsi_init(argc, argv);
116 	while (true) {
117 		nsi_hws_one_event();
118 	}
119 
120 	/* This line should be unreachable */
121 	return 1; /* LCOV_EXCL_LINE */
122 }
123 
124 #else /* NSI_LIBFUZZER */
125 
126 /**
127  * Entry point for fuzzing (when enabled). Works by placing the data
128  * into two known symbols, triggering an app-visible interrupt, and
129  * then letting the simulator run for a fixed amount of time (intended to be
130  * "long enough" to handle the event and reach a quiescent state
131  * again)
132  */
133 uint8_t *nsi_fuzz_buf, nsi_fuzz_sz;
134 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t sz)135 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz)
136 {
137 	static bool nsi_initialized;
138 
139 	if (!nsi_initialized) {
140 		nsi_init(0, NULL);
141 		nsi_initialized = true;
142 	}
143 
144 	/* Provide the fuzz data to the embedded OS as an interrupt, with
145 	 * "DMA-like" data placed into nsi_fuzz_buf/sz
146 	 */
147 	nsi_fuzz_buf = (void *)data;
148 	nsi_fuzz_sz = sz;
149 	hw_irq_ctrl_set_irq(NSI_FUZZ_IRQ);
150 
151 	/* Give the OS time to process whatever happened in that
152 	 * interrupt and reach an idle state.
153 	 */
154 	nsi_exec_for(NSI_FUZZ_TIME);
155 
156 	return 0;
157 }
158 
159 #endif /* NSI_LIBFUZZER */
160