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