/* * Copyright (c) 2017 Oticon A/S * Copyright (c) 2023 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ /* * Native simulator entry point (main) * * Documentation can be found starting in docs/README.md */ #include #include #include #include "nsi_cpun_if.h" #include "nsi_tasks.h" #include "nsi_cmdline_main_if.h" #include "nsi_utils.h" #include "nsi_hw_scheduler.h" #include "nsi_config.h" #include "nsi_cpu_ctrl.h" int nsi_exit_inner(int exit_code) { static int max_exit_code; int cpu_ret; max_exit_code = NSI_MAX(exit_code, max_exit_code); /* * nsif_cpun_cleanup may not return if this is called from a SW thread, * but instead it would get nsi_exit() recalled again * ASAP from the HW thread */ for (int i = 0; i < NSI_N_CPUS; i++) { cpu_ret = nsif_cpun_cleanup(i); max_exit_code = NSI_MAX(cpu_ret, max_exit_code); } nsi_run_tasks(NSITASK_ON_EXIT_PRE_LEVEL); nsi_hws_cleanup(); nsi_run_tasks(NSITASK_ON_EXIT_POST_LEVEL); return max_exit_code; } NSI_FUNC_NORETURN void nsi_exit(int exit_code) { exit(nsi_exit_inner(exit_code)); } /** * Run all early native simulator initialization steps, including command * line parsing and CPU start, until we are ready to let the HW models * run via hwm_one_event() */ static void nsi_init(int argc, char *argv[]) { /* * Let's ensure that even if we are redirecting to a file, we get stdout * and stderr line buffered (default for console) * Note that glibc ignores size. But just in case we set a reasonable * number in case somebody tries to compile against a different library */ setvbuf(stdout, NULL, _IOLBF, 512); setvbuf(stderr, NULL, _IOLBF, 512); nsi_run_tasks(NSITASK_PRE_BOOT_1_LEVEL); for (int i = 0; i < NSI_N_CPUS; i++) { nsif_cpun_pre_cmdline_hooks(i); } nsi_handle_cmd_line(argc, argv); nsi_run_tasks(NSITASK_PRE_BOOT_2_LEVEL); for (int i = 0; i < NSI_N_CPUS; i++) { nsif_cpun_pre_hw_init_hooks(i); } nsi_run_tasks(NSITASK_HW_INIT_LEVEL); nsi_hws_init(); nsi_run_tasks(NSITASK_PRE_BOOT_3_LEVEL); nsi_cpu_auto_boot(); nsi_run_tasks(NSITASK_FIRST_SLEEP_LEVEL); } /** * Execute the simulator for at least the specified timeout, then * return. Note that this does not affect event timing, so the "next * event" may be significantly after the request if the hardware has * not been configured to e.g. send an interrupt when expected. */ void nsi_exec_for(uint64_t us) { uint64_t start = nsi_hws_get_time(); do { nsi_hws_one_event(); } while (nsi_hws_get_time() < (start + us)); } #ifndef NSI_LIBFUZZER /** * * Note that this main() is not used when building fuzz cases, * as libfuzzer has its own main(), * and calls the "OS" through a per-case fuzz test entry point. */ int main(int argc, char *argv[]) { nsi_init(argc, argv); while (true) { nsi_hws_one_event(); } /* This line should be unreachable */ return 1; /* LCOV_EXCL_LINE */ } #else /* NSI_LIBFUZZER */ /** * Entry point for fuzzing (when enabled). Works by placing the data * into two known symbols, triggering an app-visible interrupt, and * then letting the simulator run for a fixed amount of time (intended to be * "long enough" to handle the event and reach a quiescent state * again) */ uint8_t *nsi_fuzz_buf, nsi_fuzz_sz; int LLVMFuzzerTestOneInput(const uint8_t *data, size_t sz) { static bool nsi_initialized; if (!nsi_initialized) { nsi_init(0, NULL); nsi_initialized = true; } /* Provide the fuzz data to the embedded OS as an interrupt, with * "DMA-like" data placed into nsi_fuzz_buf/sz */ nsi_fuzz_buf = (void *)data; nsi_fuzz_sz = sz; hw_irq_ctrl_set_irq(NSI_FUZZ_IRQ); /* Give the OS time to process whatever happened in that * interrupt and reach an idle state. */ nsi_exec_for(NSI_FUZZ_TIME); return 0; } #endif /* NSI_LIBFUZZER */