Coverage Report

Created: 2026-10-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/barebox/common/startup.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * (C) Copyright 2002-2006
4
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5
 *
6
 * (C) Copyright 2002
7
 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
8
 * Marius Groeger <mgroeger@sysgo.de>
9
 */
10
11
#ifdef CONFIG_DEBUG_INITCALLS
12
#define DEBUG
13
#endif
14
15
/**
16
 * @file
17
 * @brief Main entry into the C part of barebox
18
 */
19
#include <common.h>
20
#include <shell.h>
21
#include <init.h>
22
#include <command.h>
23
#include <poweroff.h>
24
#include <malloc.h>
25
#include <debug_ll.h>
26
#include <fs.h>
27
#include <errno.h>
28
#include <slice.h>
29
#include <linux/stat.h>
30
#include <envfs.h>
31
#include <magicvar.h>
32
#include <readkey.h>
33
#include <linux/reboot-mode.h>
34
#include <asm/sections.h>
35
#include <libfile.h>
36
#include <uncompress.h>
37
#include <globalvar.h>
38
#include <console_countdown.h>
39
#include <environment.h>
40
#include <linux/ctype.h>
41
#include <watchdog.h>
42
#include <glob.h>
43
#include <net.h>
44
#include <efi/mode.h>
45
#include <bselftest.h>
46
#include <pbl/handoff-data.h>
47
#include <libfile.h>
48
#include <fuzz.h>
49
#include <of.h>
50
51
extern initcall_t __barebox_initcalls_start[], __barebox_early_initcalls_end[],
52
      __barebox_initcalls_end[];
53
54
extern exitcall_t __barebox_exitcalls_start[], __barebox_exitcalls_end[];
55
56
enum system_states barebox_system_state;
57
58
#if defined CONFIG_FS_RAMFS && defined CONFIG_FS_DEVFS
59
static int mount_root(void)
60
26
{
61
26
  mount("none", "ramfs", "/", NULL);
62
26
  mkdir("/dev", 0);
63
26
  mkdir("/tmp", 0);
64
26
  mkdir("/mnt", 0);
65
26
  devfs_init();
66
67
26
  if (IS_ENABLED(CONFIG_FS_EFIVARFS)) {
68
0
    mkdir("/efivars", 0);
69
0
    automount_add("/efivars", "mount -t efivarfs none /efivars");
70
0
  }
71
72
26
  if (IS_ENABLED(CONFIG_FS_PSTORE)) {
73
26
    mkdir("/pstore", 0);
74
26
    mount("none", "pstore", "/pstore", NULL);
75
26
  }
76
77
26
  if (IS_ENABLED(CONFIG_9P_FS))
78
0
    mkdir("/mnt/9p", 0);
79
80
26
  if (IS_ENABLED(CONFIG_FS_SMHFS)) {
81
0
    mkdir("/mnt/smhfs", 0);
82
0
    automount_add("/mnt/smhfs", "mount -t smhfs /dev/null /mnt/smhfs");
83
0
  }
84
85
26
  return 0;
86
26
}
87
fs_initcall(mount_root);
88
#endif
89
90
static bool may_autoload_external_env = IS_ENABLED(CONFIG_ENV_HANDLING);
91
92
#ifdef CONFIG_ENV_HANDLING
93
void autoload_external_env(bool endis)
94
0
{
95
0
  may_autoload_external_env = endis;
96
0
}
97
#endif
98
99
static void env_merge_of_compat_overlays(const char *dir)
100
26
{
101
26
  struct device_node *root;
102
26
  const char *compat;
103
26
  struct stat s;
104
26
  char *ofdir, *src;
105
26
  int count;
106
107
26
  root = of_get_root_node();
108
26
  if (!root)
109
0
    return;
110
111
26
  ofdir = xasprintf("%s/match.of_compatible", dir);
112
113
26
  if (stat(ofdir, &s) || !S_ISDIR(s.st_mode))
114
0
    goto out;
115
116
26
  count = of_property_count_strings(root, "compatible");
117
118
  /*
119
   * Apply overlays from most generic to most specific, so that
120
   * more specific entries take precedence.
121
   */
122
52
  for (int i = count - 1; i >= 0; i--) {
123
26
    if (of_property_read_string_index(root, "compatible",
124
26
              i, &compat))
125
0
      continue;
126
127
26
    src = xasprintf("%s/%s", ofdir, compat);
128
129
26
    if (stat(src, &s) == 0 && S_ISDIR(s.st_mode)) {
130
26
      pr_debug("Applying '%s'-compatible environment overlay\n",
131
26
         compat);
132
26
      copy_recursive(src, dir, 0);
133
26
    }
134
135
26
    free(src);
136
26
  }
137
138
26
  unlink_recursive(ofdir, NULL);
139
26
out:
140
26
  free(ofdir);
141
26
}
142
143
static int load_environment(void)
144
26
{
145
26
  const char *default_environment_path;
146
26
  int __maybe_unused ret = 0;
147
148
26
  default_environment_path = default_environment_path_get();
149
150
26
  if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT)) {
151
26
    ret = defaultenv_load("/env", 0);
152
26
    if (ret)
153
26
      pr_warn("Failed loading (some) defaultenv overlays: %pe\n",
154
26
        ERR_PTR(ret));
155
26
  }
156
157
26
  if (may_autoload_external_env)
158
26
    envfs_load(default_environment_path, "/env", 0);
159
0
  else if (IS_ENABLED(CONFIG_DEFAULT_ENVIRONMENT))
160
0
    pr_info("external environment support %s. Using default environment\n",
161
26
      IS_ENABLED(CONFIG_ENV_HANDLING) ? "disallowed" : "disabled");
162
163
26
  env_merge_of_compat_overlays("/env");
164
165
26
  nvvar_load();
166
167
26
  return 0;
168
26
}
169
environment_initcall(load_environment);
170
171
static char *global_autoboot_abort_key;
172
static int global_autoboot_timeout = 3;
173
174
static const char * const global_autoboot_states[] = {
175
  [AUTOBOOT_COUNTDOWN] = "countdown",
176
  [AUTOBOOT_ABORT] = "abort",
177
  [AUTOBOOT_HALT] = "halt",
178
  [AUTOBOOT_MENU] = "menu",
179
  [AUTOBOOT_BOOT] = "boot",
180
};
181
static int global_autoboot_state = AUTOBOOT_COUNTDOWN;
182
183
static bool test_abort(void)
184
0
{
185
0
  bool do_abort = false;
186
0
  int c, ret;
187
0
  char key;
188
189
0
  while (tstc()) {
190
0
    c = getchar();
191
0
    if (tolower(c) == 'q' || c == 3)
192
0
      do_abort = true;
193
0
  }
194
195
0
  if (!do_abort)
196
0
    return false;
197
198
0
  printf("Abort init sequence? (y/n)\n"
199
0
         "Will continue with init sequence in:");
200
201
0
  ret = console_countdown(5, CONSOLE_COUNTDOWN_EXTERN, "yYnN", &key);
202
0
  if (!ret)
203
0
    return false;
204
205
0
  if (tolower(key) == 'y')
206
0
    return true;
207
208
0
  return false;
209
0
}
210
211
0
#define INITFILE "/env/bin/init"
212
0
#define MENUFILE "/env/menu/mainmenu"
213
214
/**
215
 * set_autoboot_state - set the autoboot state
216
 * @autoboot: the state to set
217
 *
218
 * This functions sets the autoboot state to the given state. This can be called
219
 * by boards which have its own idea when and how the autoboot shall be aborted.
220
 */
221
void set_autoboot_state(enum autoboot_state autoboot)
222
0
{
223
0
  global_autoboot_state = autoboot;
224
0
}
225
226
/**
227
 * do_autoboot_countdown - print autoboot countdown to console
228
 *
229
 * This prints the autoboot countdown to the console and waits for input. This
230
 * evaluates the global.autoboot_abort_key to determine which keys are allowed
231
 * to interrupt booting and also global.autoboot_timeout to determine the timeout
232
 * for the counter. This function can be called multiple times, it is executed
233
 * only the first time.
234
 *
235
 * Returns an enum autoboot_state value containing the user input.
236
 */
237
enum autoboot_state do_autoboot_countdown(void)
238
0
{
239
0
  static enum autoboot_state autoboot_state = AUTOBOOT_UNKNOWN;
240
0
  unsigned flags = CONSOLE_COUNTDOWN_EXTERN;
241
0
  int ret;
242
0
  struct stat s;
243
0
  bool menu_exists;
244
0
  char abortkeys[3] = {};
245
0
  unsigned char outkey;
246
247
0
  if (autoboot_state != AUTOBOOT_UNKNOWN)
248
0
    return autoboot_state;
249
250
0
  if (!console_get_first_interactive() &&
251
0
      global_autoboot_state != AUTOBOOT_ABORT &&
252
0
      global_autoboot_state != AUTOBOOT_HALT) {
253
0
    printf("\nNon-interactive console, booting system\n");
254
0
    return autoboot_state = AUTOBOOT_BOOT;
255
0
  }
256
257
0
  if (global_autoboot_state != AUTOBOOT_COUNTDOWN)
258
0
    return global_autoboot_state;
259
260
0
  menu_exists = stat(MENUFILE, &s) == 0;
261
262
0
  if (menu_exists)
263
0
    strcat(abortkeys, "m");
264
265
0
  if (!global_autoboot_abort_key ||
266
0
      !strcmp(global_autoboot_abort_key, "any"))
267
0
    flags |= CONSOLE_COUNTDOWN_ANYKEY;
268
0
  else if (!strcmp(global_autoboot_abort_key, "ctrl-c"))
269
0
    flags |= CONSOLE_COUNTDOWN_CTRLC;
270
0
  else
271
0
    strcat(abortkeys, global_autoboot_abort_key);
272
273
0
  printf("\nHit %s%s to stop autoboot: ",
274
0
         menu_exists ? "m for menu or " : "",
275
0
         global_autoboot_abort_key ? global_autoboot_abort_key : "any");
276
277
0
  command_slice_release();
278
0
  ret = console_countdown(global_autoboot_timeout, flags, abortkeys,
279
0
        &outkey);
280
0
  command_slice_acquire();
281
282
0
  if (ret == 0)
283
0
    autoboot_state = AUTOBOOT_BOOT;
284
0
  else if (menu_exists && outkey == 'm')
285
0
    autoboot_state = AUTOBOOT_MENU;
286
0
  else if (outkey == CTL_CH('d'))
287
0
    autoboot_state = AUTOBOOT_HALT;
288
0
  else
289
0
    autoboot_state = AUTOBOOT_ABORT;
290
291
0
  return autoboot_state;
292
0
}
293
294
static int autoboot_abort_key_set(struct param_d *p, void *priv)
295
0
{
296
0
  if (!strcmp(global_autoboot_abort_key, "any"))
297
0
    return 0;
298
0
  if (!strcmp(global_autoboot_abort_key, "ctrl-c"))
299
0
    return 0;
300
301
0
  if (strlen(global_autoboot_abort_key) != 1)
302
0
    return -EINVAL;
303
304
0
  return 0;
305
0
}
306
307
static int register_autoboot_vars(void)
308
26
{
309
26
  globalvar_add_param_string("autoboot_abort_key",
310
26
           autoboot_abort_key_set,
311
26
           NULL,
312
26
           &global_autoboot_abort_key,
313
26
           NULL);
314
26
  globalvar_add_simple_int("autoboot_timeout",
315
26
         &global_autoboot_timeout, "%u");
316
26
  globalvar_add_simple_enum("autoboot",
317
26
          &global_autoboot_state,
318
26
          global_autoboot_states,
319
26
          ARRAY_SIZE(global_autoboot_states));
320
321
26
  return 0;
322
26
}
323
postcore_initcall(register_autoboot_vars);
324
325
static enum autoboot_state current_autoboot = AUTOBOOT_UNKNOWN;
326
327
/**
328
 * get_autoboot_state - get the autoboot state
329
 *
330
 * This functions returns the autoboot state last used.
331
 */
332
enum autoboot_state get_autoboot_state(void)
333
0
{
334
0
  return current_autoboot;
335
0
}
336
337
static int run_init(void)
338
0
{
339
0
  const char *bmode, *cmdline;
340
0
  bool env_bin_init_exists;
341
0
  struct stat s;
342
0
  glob_t g;
343
0
  int i, ret;
344
0
  size_t size;
345
0
  void *ext_dtb;
346
347
0
  setenv("PATH", "/env/bin");
348
0
  export("PATH");
349
350
  /* Run legacy /env/bin/init if it exists */
351
0
  env_bin_init_exists = stat(INITFILE, &s) == 0;
352
0
  if (env_bin_init_exists) {
353
0
    pr_info("running legacy %s...\n", INITFILE);
354
0
    run_command("source " INITFILE);
355
0
    return 0;
356
0
  }
357
358
  /* Unblank console cursor */
359
0
  printf("\e[?25h");
360
361
0
  if (test_abort()) {
362
0
    pr_info("Init sequence aborted\n");
363
0
    return -EINTR;
364
0
  }
365
366
  /* Run scripts in /env/init/ */
367
0
  ret = glob("/env/init/*", 0, NULL, &g);
368
0
  if (!ret) {
369
0
    for (i = 0; i < g.gl_pathc; i++) {
370
0
      const char *path = g.gl_pathv[i];
371
372
0
      ret = stat(path, &s);
373
0
      if (ret)
374
0
        continue;
375
376
0
      if (!S_ISREG(s.st_mode))
377
0
        continue;
378
379
0
      pr_debug("Executing '%s'...\n", path);
380
0
      run_command("source %s", path);
381
0
    }
382
383
0
    globfree(&g);
384
0
  }
385
386
0
  cmdline = barebox_cmdline_get();
387
0
  if (cmdline) {
388
0
    ret = write_file("/cmdline", cmdline, strlen(cmdline));
389
0
    if (ret)
390
0
      return ret;
391
392
0
    console_ctrlc_allow();
393
0
    run_command("source /cmdline");
394
0
  }
395
396
0
  ext_dtb = handoff_data_get_entry(HANDOFF_DATA_EXTERNAL_DT, &size);
397
0
  if (ext_dtb)
398
0
    write_file("/external-devicetree", ext_dtb, size);
399
400
  /* source matching script in /env/bmode/ */
401
0
  bmode = reboot_mode_get();
402
0
  if (bmode) {
403
0
    char *scr, *path;
404
405
0
    scr = xasprintf("source /env/bmode/%s", bmode);
406
0
    path = &scr[strlen("source ")];
407
0
    if (stat(path, &s) == 0) {
408
0
      pr_info("Invoking '%s'...\n", path);
409
0
      run_command("%s", scr);
410
0
    }
411
0
    free(scr);
412
0
  }
413
414
0
  current_autoboot = do_autoboot_countdown();
415
416
0
  console_ctrlc_allow();
417
418
0
  if (current_autoboot == AUTOBOOT_BOOT)
419
0
    run_command("boot");
420
421
0
  if (IS_ENABLED(CONFIG_NET) && !IS_ENABLED(CONFIG_CONSOLE_DISABLE_INPUT) &&
422
0
      current_autoboot != AUTOBOOT_HALT)
423
0
    eth_open_all();
424
425
0
  if (current_autoboot != AUTOBOOT_MENU) {
426
0
    if (current_autoboot == AUTOBOOT_ABORT &&
427
0
        current_autoboot == global_autoboot_state)
428
0
      watchdog_inhibit_all();
429
430
0
    run_shell();
431
0
  }
432
433
0
  do {
434
    /*
435
     * Let's run the command once at least, so an error
436
     * message is printed if the file doesn't exist
437
     */
438
0
    run_command(MENUFILE);
439
0
  } while (stat(MENUFILE, &s) == 0);
440
441
0
  hang();
442
0
}
443
444
int (*barebox_main)(void)
445
  = !IS_ENABLED(CONFIG_SHELL_NONE) &&
446
           IS_ENABLED(CONFIG_COMMAND_SUPPORT) ? run_init : NULL;
447
448
void __noreturn start_barebox(void)
449
26
{
450
26
  initcall_t *initcall;
451
26
  int result;
452
453
26
  for (initcall = __barebox_initcalls_start;
454
3.58k
      initcall < __barebox_initcalls_end; initcall++) {
455
3.56k
    pr_debug("initcall-> %pS\n", *initcall);
456
3.56k
    result = (*initcall)();
457
3.56k
    if (result)
458
3.56k
      pr_err("initcall %pS failed: %pe\n", *initcall,
459
3.56k
          ERR_PTR(result));
460
3.56k
  }
461
462
26
  barebox_system_state = BAREBOX_RUNNING;
463
26
  pr_debug("initcalls done\n");
464
465
466
26
  if (IS_ENABLED(CONFIG_SELFTEST_AUTORUN))
467
0
    selftests_run();
468
469
26
  if (barebox_main)
470
26
    barebox_main();
471
472
26
  if (IS_ENABLED(CONFIG_SHELL_NONE)) {
473
0
    pr_crit("Nothing left to do\n");
474
0
    poweroff_machine(0);
475
26
  } else {
476
26
    while (1)
477
0
      run_shell();
478
26
  }
479
26
}
480
481
void __noreturn hang (void)
482
0
{
483
0
  puts ("### ERROR ### Please RESET the board ###\n");
484
0
  for (;;);
485
0
}
486
487
/* Everything needed to cleanly shutdown barebox.
488
 * Should be called before starting an OS to get
489
 * the devices into a clean state
490
 */
491
void shutdown_barebox(void)
492
0
{
493
0
  exitcall_t *exitcall;
494
495
0
  barebox_system_state = BAREBOX_EXITING;
496
497
0
  for (exitcall = __barebox_exitcalls_start;
498
0
      exitcall < __barebox_exitcalls_end; exitcall++) {
499
0
    pr_debug("exitcall-> %pS\n", *exitcall);
500
0
    (*exitcall)();
501
0
  }
502
503
0
  console_flush();
504
0
}
505
506
BAREBOX_MAGICVAR(global.autoboot,
507
                 "Autoboot state. Possible values: countdown (default), abort, halt, menu, boot");
508
BAREBOX_MAGICVAR(global.autoboot_abort_key,
509
                 "Which key allows to interrupt autoboot. Possible values: 'any', 'ctrl-c' or any other single ascii character.");
510
BAREBOX_MAGICVAR(global.autoboot_timeout,
511
                 "Timeout before autoboot starts in seconds");