command: Remove the cmd_tbl_t typedef
[oweals/u-boot.git] / common / bootm_os.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000-2009
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  */
6
7 #include <common.h>
8 #include <bootm.h>
9 #include <bootstage.h>
10 #include <cpu_func.h>
11 #include <efi_loader.h>
12 #include <env.h>
13 #include <fdt_support.h>
14 #include <image.h>
15 #include <lmb.h>
16 #include <linux/libfdt.h>
17 #include <malloc.h>
18 #include <mapmem.h>
19 #include <vxworks.h>
20 #include <tee/optee.h>
21
22 DECLARE_GLOBAL_DATA_PTR;
23
24 static int do_bootm_standalone(int flag, int argc, char *const argv[],
25                                bootm_headers_t *images)
26 {
27         char *s;
28         int (*appl)(int, char *const[]);
29
30         /* Don't start if "autostart" is set to "no" */
31         s = env_get("autostart");
32         if ((s != NULL) && !strcmp(s, "no")) {
33                 env_set_hex("filesize", images->os.image_len);
34                 return 0;
35         }
36         appl = (int (*)(int, char * const []))images->ep;
37         appl(argc, argv);
38         return 0;
39 }
40
41 /*******************************************************************/
42 /* OS booting routines */
43 /*******************************************************************/
44
45 #if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9)
46 static void copy_args(char *dest, int argc, char *const argv[], char delim)
47 {
48         int i;
49
50         for (i = 0; i < argc; i++) {
51                 if (i > 0)
52                         *dest++ = delim;
53                 strcpy(dest, argv[i]);
54                 dest += strlen(argv[i]);
55         }
56 }
57 #endif
58
59 #ifdef CONFIG_BOOTM_NETBSD
60 static int do_bootm_netbsd(int flag, int argc, char *const argv[],
61                            bootm_headers_t *images)
62 {
63         void (*loader)(bd_t *, image_header_t *, char *, char *);
64         image_header_t *os_hdr, *hdr;
65         ulong kernel_data, kernel_len;
66         char *cmdline;
67
68         if (flag != BOOTM_STATE_OS_GO)
69                 return 0;
70
71 #if defined(CONFIG_FIT)
72         if (!images->legacy_hdr_valid) {
73                 fit_unsupported_reset("NetBSD");
74                 return 1;
75         }
76 #endif
77         hdr = images->legacy_hdr_os;
78
79         /*
80          * Booting a (NetBSD) kernel image
81          *
82          * This process is pretty similar to a standalone application:
83          * The (first part of an multi-) image must be a stage-2 loader,
84          * which in turn is responsible for loading & invoking the actual
85          * kernel.  The only differences are the parameters being passed:
86          * besides the board info strucure, the loader expects a command
87          * line, the name of the console device, and (optionally) the
88          * address of the original image header.
89          */
90         os_hdr = NULL;
91         if (image_check_type(&images->legacy_hdr_os_copy, IH_TYPE_MULTI)) {
92                 image_multi_getimg(hdr, 1, &kernel_data, &kernel_len);
93                 if (kernel_len)
94                         os_hdr = hdr;
95         }
96
97         if (argc > 0) {
98                 ulong len;
99                 int   i;
100
101                 for (i = 0, len = 0; i < argc; i += 1)
102                         len += strlen(argv[i]) + 1;
103                 cmdline = malloc(len);
104                 copy_args(cmdline, argc, argv, ' ');
105         } else {
106                 cmdline = env_get("bootargs");
107                 if (cmdline == NULL)
108                         cmdline = "";
109         }
110
111         loader = (void (*)(bd_t *, image_header_t *, char *, char *))images->ep;
112
113         printf("## Transferring control to NetBSD stage-2 loader (at address %08lx) ...\n",
114                (ulong)loader);
115
116         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
117
118         /*
119          * NetBSD Stage-2 Loader Parameters:
120          *   arg[0]: pointer to board info data
121          *   arg[1]: image load address
122          *   arg[2]: char pointer to the console device to use
123          *   arg[3]: char pointer to the boot arguments
124          */
125         (*loader)(gd->bd, os_hdr, "", cmdline);
126
127         return 1;
128 }
129 #endif /* CONFIG_BOOTM_NETBSD*/
130
131 #ifdef CONFIG_LYNXKDI
132 static int do_bootm_lynxkdi(int flag, int argc, char *const argv[],
133                             bootm_headers_t *images)
134 {
135         image_header_t *hdr = &images->legacy_hdr_os_copy;
136
137         if (flag != BOOTM_STATE_OS_GO)
138                 return 0;
139
140 #if defined(CONFIG_FIT)
141         if (!images->legacy_hdr_valid) {
142                 fit_unsupported_reset("Lynx");
143                 return 1;
144         }
145 #endif
146
147         lynxkdi_boot((image_header_t *)hdr);
148
149         return 1;
150 }
151 #endif /* CONFIG_LYNXKDI */
152
153 #ifdef CONFIG_BOOTM_RTEMS
154 static int do_bootm_rtems(int flag, int argc, char *const argv[],
155                           bootm_headers_t *images)
156 {
157         void (*entry_point)(bd_t *);
158
159         if (flag != BOOTM_STATE_OS_GO)
160                 return 0;
161
162 #if defined(CONFIG_FIT)
163         if (!images->legacy_hdr_valid) {
164                 fit_unsupported_reset("RTEMS");
165                 return 1;
166         }
167 #endif
168
169         entry_point = (void (*)(bd_t *))images->ep;
170
171         printf("## Transferring control to RTEMS (at address %08lx) ...\n",
172                (ulong)entry_point);
173
174         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
175
176         /*
177          * RTEMS Parameters:
178          *   r3: ptr to board info data
179          */
180         (*entry_point)(gd->bd);
181
182         return 1;
183 }
184 #endif /* CONFIG_BOOTM_RTEMS */
185
186 #if defined(CONFIG_BOOTM_OSE)
187 static int do_bootm_ose(int flag, int argc, char *const argv[],
188                         bootm_headers_t *images)
189 {
190         void (*entry_point)(void);
191
192         if (flag != BOOTM_STATE_OS_GO)
193                 return 0;
194
195 #if defined(CONFIG_FIT)
196         if (!images->legacy_hdr_valid) {
197                 fit_unsupported_reset("OSE");
198                 return 1;
199         }
200 #endif
201
202         entry_point = (void (*)(void))images->ep;
203
204         printf("## Transferring control to OSE (at address %08lx) ...\n",
205                (ulong)entry_point);
206
207         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
208
209         /*
210          * OSE Parameters:
211          *   None
212          */
213         (*entry_point)();
214
215         return 1;
216 }
217 #endif /* CONFIG_BOOTM_OSE */
218
219 #if defined(CONFIG_BOOTM_PLAN9)
220 static int do_bootm_plan9(int flag, int argc, char *const argv[],
221                           bootm_headers_t *images)
222 {
223         void (*entry_point)(void);
224         char *s;
225
226         if (flag != BOOTM_STATE_OS_GO)
227                 return 0;
228
229 #if defined(CONFIG_FIT)
230         if (!images->legacy_hdr_valid) {
231                 fit_unsupported_reset("Plan 9");
232                 return 1;
233         }
234 #endif
235
236         /* See README.plan9 */
237         s = env_get("confaddr");
238         if (s != NULL) {
239                 char *confaddr = (char *)simple_strtoul(s, NULL, 16);
240
241                 if (argc > 0) {
242                         copy_args(confaddr, argc, argv, '\n');
243                 } else {
244                         s = env_get("bootargs");
245                         if (s != NULL)
246                                 strcpy(confaddr, s);
247                 }
248         }
249
250         entry_point = (void (*)(void))images->ep;
251
252         printf("## Transferring control to Plan 9 (at address %08lx) ...\n",
253                (ulong)entry_point);
254
255         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
256
257         /*
258          * Plan 9 Parameters:
259          *   None
260          */
261         (*entry_point)();
262
263         return 1;
264 }
265 #endif /* CONFIG_BOOTM_PLAN9 */
266
267 #if defined(CONFIG_BOOTM_VXWORKS) && \
268         (defined(CONFIG_PPC) || defined(CONFIG_ARM))
269
270 static void do_bootvx_fdt(bootm_headers_t *images)
271 {
272 #if defined(CONFIG_OF_LIBFDT)
273         int ret;
274         char *bootline;
275         ulong of_size = images->ft_len;
276         char **of_flat_tree = &images->ft_addr;
277         struct lmb *lmb = &images->lmb;
278
279         if (*of_flat_tree) {
280                 boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree);
281
282                 ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size);
283                 if (ret)
284                         return;
285
286                 /* Update ethernet nodes */
287                 fdt_fixup_ethernet(*of_flat_tree);
288
289                 ret = fdt_add_subnode(*of_flat_tree, 0, "chosen");
290                 if ((ret >= 0 || ret == -FDT_ERR_EXISTS)) {
291                         bootline = env_get("bootargs");
292                         if (bootline) {
293                                 ret = fdt_find_and_setprop(*of_flat_tree,
294                                                 "/chosen", "bootargs",
295                                                 bootline,
296                                                 strlen(bootline) + 1, 1);
297                                 if (ret < 0) {
298                                         printf("## ERROR: %s : %s\n", __func__,
299                                                fdt_strerror(ret));
300                                         return;
301                                 }
302                         }
303                 } else {
304                         printf("## ERROR: %s : %s\n", __func__,
305                                fdt_strerror(ret));
306                         return;
307                 }
308         }
309 #endif
310
311         boot_prep_vxworks(images);
312
313         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
314
315 #if defined(CONFIG_OF_LIBFDT)
316         printf("## Starting vxWorks at 0x%08lx, device tree at 0x%08lx ...\n",
317                (ulong)images->ep, (ulong)*of_flat_tree);
318 #else
319         printf("## Starting vxWorks at 0x%08lx\n", (ulong)images->ep);
320 #endif
321
322         boot_jump_vxworks(images);
323
324         puts("## vxWorks terminated\n");
325 }
326
327 static int do_bootm_vxworks_legacy(int flag, int argc, char *const argv[],
328                                    bootm_headers_t *images)
329 {
330         if (flag != BOOTM_STATE_OS_GO)
331                 return 0;
332
333 #if defined(CONFIG_FIT)
334         if (!images->legacy_hdr_valid) {
335                 fit_unsupported_reset("VxWorks");
336                 return 1;
337         }
338 #endif
339
340         do_bootvx_fdt(images);
341
342         return 1;
343 }
344
345 int do_bootm_vxworks(int flag, int argc, char *const argv[],
346                      bootm_headers_t *images)
347 {
348         char *bootargs;
349         int pos;
350         unsigned long vxflags;
351         bool std_dtb = false;
352
353         /* get bootargs env */
354         bootargs = env_get("bootargs");
355
356         if (bootargs != NULL) {
357                 for (pos = 0; pos < strlen(bootargs); pos++) {
358                         /* find f=0xnumber flag */
359                         if ((bootargs[pos] == '=') && (pos >= 1) &&
360                             (bootargs[pos - 1] == 'f')) {
361                                 vxflags = simple_strtoul(&bootargs[pos + 1],
362                                                          NULL, 16);
363                                 if (vxflags & VXWORKS_SYSFLG_STD_DTB)
364                                         std_dtb = true;
365                         }
366                 }
367         }
368
369         if (std_dtb) {
370                 if (flag & BOOTM_STATE_OS_PREP)
371                         printf("   Using standard DTB\n");
372                 return do_bootm_linux(flag, argc, argv, images);
373         } else {
374                 if (flag & BOOTM_STATE_OS_PREP)
375                         printf("   !!! WARNING !!! Using legacy DTB\n");
376                 return do_bootm_vxworks_legacy(flag, argc, argv, images);
377         }
378 }
379 #endif
380
381 #if defined(CONFIG_CMD_ELF)
382 static int do_bootm_qnxelf(int flag, int argc, char *const argv[],
383                            bootm_headers_t *images)
384 {
385         char *local_args[2];
386         char str[16];
387         int dcache;
388
389         if (flag != BOOTM_STATE_OS_GO)
390                 return 0;
391
392 #if defined(CONFIG_FIT)
393         if (!images->legacy_hdr_valid) {
394                 fit_unsupported_reset("QNX");
395                 return 1;
396         }
397 #endif
398
399         sprintf(str, "%lx", images->ep); /* write entry-point into string */
400         local_args[0] = argv[0];
401         local_args[1] = str;    /* and provide it via the arguments */
402
403         /*
404          * QNX images require the data cache is disabled.
405          */
406         dcache = dcache_status();
407         if (dcache)
408                 dcache_disable();
409
410         do_bootelf(NULL, 0, 2, local_args);
411
412         if (dcache)
413                 dcache_enable();
414
415         return 1;
416 }
417 #endif
418
419 #ifdef CONFIG_INTEGRITY
420 static int do_bootm_integrity(int flag, int argc, char *const argv[],
421                               bootm_headers_t *images)
422 {
423         void (*entry_point)(void);
424
425         if (flag != BOOTM_STATE_OS_GO)
426                 return 0;
427
428 #if defined(CONFIG_FIT)
429         if (!images->legacy_hdr_valid) {
430                 fit_unsupported_reset("INTEGRITY");
431                 return 1;
432         }
433 #endif
434
435         entry_point = (void (*)(void))images->ep;
436
437         printf("## Transferring control to INTEGRITY (at address %08lx) ...\n",
438                (ulong)entry_point);
439
440         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
441
442         /*
443          * INTEGRITY Parameters:
444          *   None
445          */
446         (*entry_point)();
447
448         return 1;
449 }
450 #endif
451
452 #ifdef CONFIG_BOOTM_OPENRTOS
453 static int do_bootm_openrtos(int flag, int argc, char *const argv[],
454                              bootm_headers_t *images)
455 {
456         void (*entry_point)(void);
457
458         if (flag != BOOTM_STATE_OS_GO)
459                 return 0;
460
461         entry_point = (void (*)(void))images->ep;
462
463         printf("## Transferring control to OpenRTOS (at address %08lx) ...\n",
464                 (ulong)entry_point);
465
466         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
467
468         /*
469          * OpenRTOS Parameters:
470          *   None
471          */
472         (*entry_point)();
473
474         return 1;
475 }
476 #endif
477
478 #ifdef CONFIG_BOOTM_OPTEE
479 static int do_bootm_tee(int flag, int argc, char *const argv[],
480                         bootm_headers_t *images)
481 {
482         int ret;
483
484         /* Verify OS type */
485         if (images->os.os != IH_OS_TEE) {
486                 return 1;
487         };
488
489         /* Validate OPTEE header */
490         ret = optee_verify_bootm_image(images->os.image_start,
491                                        images->os.load,
492                                        images->os.image_len);
493         if (ret)
494                 return ret;
495
496         /* Locate FDT etc */
497         ret = bootm_find_images(flag, argc, argv);
498         if (ret)
499                 return ret;
500
501         /* From here we can run the regular linux boot path */
502         return do_bootm_linux(flag, argc, argv, images);
503 }
504 #endif
505
506 #ifdef CONFIG_BOOTM_EFI
507 static int do_bootm_efi(int flag, int argc, char *const argv[],
508                         bootm_headers_t *images)
509 {
510         int ret;
511         efi_status_t efi_ret;
512         void *image_buf;
513
514         if (flag != BOOTM_STATE_OS_GO)
515                 return 0;
516
517         /* Locate FDT, if provided */
518         ret = bootm_find_images(flag, argc, argv);
519         if (ret)
520                 return ret;
521
522         /* Initialize EFI drivers */
523         efi_ret = efi_init_obj_list();
524         if (efi_ret != EFI_SUCCESS) {
525                 printf("## Failed to initialize UEFI sub-system: r = %lu\n",
526                        efi_ret & ~EFI_ERROR_MASK);
527                 return 1;
528         }
529
530         /* Install device tree */
531         efi_ret = efi_install_fdt(images->ft_len
532                                   ? images->ft_addr : EFI_FDT_USE_INTERNAL);
533         if (efi_ret != EFI_SUCCESS) {
534                 printf("## Failed to install device tree: r = %lu\n",
535                        efi_ret & ~EFI_ERROR_MASK);
536                 return 1;
537         }
538
539         /* Run EFI image */
540         printf("## Transferring control to EFI (at address %08lx) ...\n",
541                images->ep);
542         bootstage_mark(BOOTSTAGE_ID_RUN_OS);
543
544         image_buf = map_sysmem(images->ep, images->os.image_len);
545
546         efi_ret = efi_run_image(image_buf, images->os.image_len);
547         if (efi_ret != EFI_SUCCESS) {
548                 printf("## Failed to run EFI image: r = %lu\n",
549                        efi_ret & ~EFI_ERROR_MASK);
550                 return 1;
551         }
552
553         return 0;
554 }
555 #endif
556
557 static boot_os_fn *boot_os[] = {
558         [IH_OS_U_BOOT] = do_bootm_standalone,
559 #ifdef CONFIG_BOOTM_LINUX
560         [IH_OS_LINUX] = do_bootm_linux,
561 #endif
562 #ifdef CONFIG_BOOTM_NETBSD
563         [IH_OS_NETBSD] = do_bootm_netbsd,
564 #endif
565 #ifdef CONFIG_LYNXKDI
566         [IH_OS_LYNXOS] = do_bootm_lynxkdi,
567 #endif
568 #ifdef CONFIG_BOOTM_RTEMS
569         [IH_OS_RTEMS] = do_bootm_rtems,
570 #endif
571 #if defined(CONFIG_BOOTM_OSE)
572         [IH_OS_OSE] = do_bootm_ose,
573 #endif
574 #if defined(CONFIG_BOOTM_PLAN9)
575         [IH_OS_PLAN9] = do_bootm_plan9,
576 #endif
577 #if defined(CONFIG_BOOTM_VXWORKS) && \
578         (defined(CONFIG_PPC) || defined(CONFIG_ARM) || defined(CONFIG_RISCV))
579         [IH_OS_VXWORKS] = do_bootm_vxworks,
580 #endif
581 #if defined(CONFIG_CMD_ELF)
582         [IH_OS_QNX] = do_bootm_qnxelf,
583 #endif
584 #ifdef CONFIG_INTEGRITY
585         [IH_OS_INTEGRITY] = do_bootm_integrity,
586 #endif
587 #ifdef CONFIG_BOOTM_OPENRTOS
588         [IH_OS_OPENRTOS] = do_bootm_openrtos,
589 #endif
590 #ifdef CONFIG_BOOTM_OPTEE
591         [IH_OS_TEE] = do_bootm_tee,
592 #endif
593 #ifdef CONFIG_BOOTM_EFI
594         [IH_OS_EFI] = do_bootm_efi,
595 #endif
596 };
597
598 /* Allow for arch specific config before we boot */
599 __weak void arch_preboot_os(void)
600 {
601         /* please define platform specific arch_preboot_os() */
602 }
603
604 /* Allow for board specific config before we boot */
605 __weak void board_preboot_os(void)
606 {
607         /* please define board specific board_preboot_os() */
608 }
609
610 int boot_selected_os(int argc, char *const argv[], int state,
611                      bootm_headers_t *images, boot_os_fn *boot_fn)
612 {
613         arch_preboot_os();
614         board_preboot_os();
615         boot_fn(state, argc, argv, images);
616
617         /* Stand-alone may return when 'autostart' is 'no' */
618         if (images->os.type == IH_TYPE_STANDALONE ||
619             IS_ENABLED(CONFIG_SANDBOX) ||
620             state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */
621                 return 0;
622         bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED);
623         debug("\n## Control returned to monitor - resetting...\n");
624
625         return BOOTM_ERR_RESET;
626 }
627
628 boot_os_fn *bootm_os_get_boot_func(int os)
629 {
630 #ifdef CONFIG_NEEDS_MANUAL_RELOC
631         static bool relocated;
632
633         if (!relocated) {
634                 int i;
635
636                 /* relocate boot function table */
637                 for (i = 0; i < ARRAY_SIZE(boot_os); i++)
638                         if (boot_os[i] != NULL)
639                                 boot_os[i] += gd->reloc_off;
640
641                 relocated = true;
642         }
643 #endif
644         return boot_os[os];
645 }