Hello,
We have a J721e based Custom board and we were able to boot the board from eMMC. Boot loaders (i.e. tiboot3.bin, sysfw.itb, tispl.bin & u-boot.img) works good however while making attempts to load Linux Kernel it hangs at "Starting kernel ...".
I have made changes in UART where in EVM uart0 (serial2) is used for main debug log and on our board we have uart7 (serial9) for main debug log. Also I have enabled other UART0 , 1, 2, 3, 4, 6,7,& 8. These changes are made in u-boot and Kernel device tree.
Since Linux kernel hangs at 'Starting Kernel ..." I added some debug trace in u-boot i.e. <git>/arch/arm/lib/bootm.c to check where exactly it is failing (See attached file). and following is the place where it is stuck. Check Line 380 in attached "bootm.c" file.
After adding these logs here is my log from terminal.
MMC: sdhci@4f80000: 0, sdhci@4fb0000: 1 Loading Environment from MMC... OK In: serial@2870000 Out: serial@2870000 Err: serial@2870000 Reading on-board EEPROM at 0x50 failed 1 Net: can't parse phy-handle port 1 (-2) can't find cpsw-phy-sel No ethernet found. Hit any key to stop autoboot: 0 switch to partitions #0, OK mmc0(part 0) is current device SD/MMC found on device 0 ** Unrecognized filesystem type ** 16654344 bytes read in 51 ms (311.4 MiB/s) 88989 bytes read in 2 ms (42.4 MiB/s) ## Flattened Device Tree blob at 82000000 Booting using the fdt blob at 0x82000000 Loading Device Tree to 00000000fdda1000, end 00000000fdeb9fff ... OK IVP HW DEBUG: boot_jump_linux: ## Transferring control to Linux (at address 80080000)... Starting kernel ... IVP HW DEBUG: announce_and_cleanup: fake val =0 IVP HW DEBUG: announce_and_cleanup: 0 IVP HW DEBUG: announce_and_cleanup: 1 IVP HW DEBUG: boot_jump_linux: 0 IVP HW DEBUG: boot_jump_linux: 2 IVP HW DEBUG: boot_jump_linux: 3 IVP HW DEBUG: boot_jump_linux: 4.3 images->ft_addr= 0xfdda1000 IVP HW DEBUG: boot_jump_linux: 4.3 images->ep= 0x80080000
I have used following command from u-boot to load the kernel -
setenv bootargs console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000 vm.overcommit_ratio=5 vm.overcommit_memory=2 root=/dev/mmcblk0p1
setenv bootcmd "setenv autoload no;ext4load mmc 0:1 0x82000000 /boot/Image;ext4load mmc 0:1 0x81200000 /boot/k3-j721e-common-proc-board.dtb; booti 0x82000000 - 0x81200000"
Could you please let me know to debug & fix this issue?
Thank you,
Satish
// SPDX-License-Identifier: GPL-2.0+ /* Copyright (C) 2011 * Corscience GmbH & Co. KG - Simon Schwarz <schwarz@corscience.de> * - Added prep subcommand support * - Reorganized source - modeled after powerpc version * * (C) Copyright 2002 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Marius Groeger <mgroeger@sysgo.de> * * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) */ #include <common.h> #include <command.h> #include <cpu_func.h> #include <dm.h> #include <dm/root.h> #include <env.h> #include <image.h> #include <u-boot/zlib.h> #include <asm/byteorder.h> #include <linux/libfdt.h> #include <mapmem.h> #include <fdt_support.h> #include <asm/bootm.h> #include <asm/secure.h> #include <linux/compiler.h> #include <bootm.h> #include <vxworks.h> #ifdef CONFIG_ARMV7_NONSEC #include <asm/armv7.h> #endif #include <asm/setup.h> DECLARE_GLOBAL_DATA_PTR; static struct tag *params; static ulong get_sp(void) { ulong ret; asm("mov %0, sp" : "=r"(ret) : ); return ret; } void arch_lmb_reserve(struct lmb *lmb) { ulong sp, bank_end; int bank; /* * Booting a (Linux) kernel image * * Allocate space for command line and board info - the * address should be as high as possible within the reach of * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused * memory, which means far enough below the current stack * pointer. */ sp = get_sp(); debug("## Current stack ends at 0x%08lx ", sp); /* adjust sp by 4K to be safe */ sp -= 4096; for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { if (!gd->bd->bi_dram[bank].size || sp < gd->bd->bi_dram[bank].start) continue; /* Watch out for RAM at end of address space! */ bank_end = gd->bd->bi_dram[bank].start + gd->bd->bi_dram[bank].size - 1; if (sp > bank_end) continue; lmb_reserve(lmb, sp, bank_end - sp + 1); break; } } __weak void board_quiesce_devices(void) { } /** * announce_and_cleanup() - Print message and prepare for kernel boot * * @fake: non-zero to do everything except actually boot */ static void announce_and_cleanup(int fake) { bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); #ifdef CONFIG_BOOTSTAGE_FDT bootstage_fdt_add_report(); #endif #ifdef CONFIG_BOOTSTAGE_REPORT bootstage_report(); #endif #ifdef CONFIG_USB_DEVICE udc_disconnect(); #endif board_quiesce_devices(); printf("\nStarting kernel ...%s\n\n", fake ? "(fake run for tracing)" : ""); printf("IVP HW DEBUG: announce_and_cleanup: fake val =%d \n",fake); /* * Call remove function of all devices with a removal flag set. * This may be useful for last-stage operations, like cancelling * of DMA operation or releasing device internal buffers. */ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); printf("IVP HW DEBUG: announce_and_cleanup: 0 \n"); cleanup_before_linux(); printf("IVP HW DEBUG: announce_and_cleanup: 1 \n"); } static void setup_start_tag (bd_t *bd) { params = (struct tag *)bd->bi_boot_params; params->hdr.tag = ATAG_CORE; params->hdr.size = tag_size (tag_core); params->u.core.flags = 0; params->u.core.pagesize = 0; params->u.core.rootdev = 0; params = tag_next (params); } static void setup_memory_tags(bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params); } } static void setup_commandline_tag(bd_t *bd, char *commandline) { char *p; if (!commandline) return; /* eat leading white space */ for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still * use its default command line. */ if (*p == '\0') return; params->hdr.tag = ATAG_CMDLINE; params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params); } static void setup_initrd_tag(bd_t *bd, ulong initrd_start, ulong initrd_end) { /* an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found. ATAG_RDIMG is a better name, actually. */ params->hdr.tag = ATAG_INITRD2; params->hdr.size = tag_size (tag_initrd); params->u.initrd.start = initrd_start; params->u.initrd.size = initrd_end - initrd_start; params = tag_next (params); } static void setup_serial_tag(struct tag **tmp) { struct tag *params = *tmp; struct tag_serialnr serialnr; get_board_serial(&serialnr); params->hdr.tag = ATAG_SERIAL; params->hdr.size = tag_size (tag_serialnr); params->u.serialnr.low = serialnr.low; params->u.serialnr.high= serialnr.high; params = tag_next (params); *tmp = params; } static void setup_revision_tag(struct tag **in_params) { u32 rev = 0; rev = get_board_rev(); params->hdr.tag = ATAG_REVISION; params->hdr.size = tag_size (tag_revision); params->u.revision.rev = rev; params = tag_next (params); } static void setup_end_tag(bd_t *bd) { params->hdr.tag = ATAG_NONE; params->hdr.size = 0; } __weak void setup_board_tags(struct tag **in_params) {} #ifdef CONFIG_ARM64 static void do_nonsec_virt_switch(void) { smp_kick_all_cpus(); dcache_disable(); /* flush cache before swtiching to EL2 */ } #endif __weak void board_prep_linux(bootm_headers_t *images) { } /* Subcommand: PREP */ static void boot_prep_linux(bootm_headers_t *images) { char *commandline = env_get("bootargs"); if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) { #ifdef CONFIG_OF_LIBFDT debug("using: FDT\n"); if (image_setup_linux(images)) { printf("FDT creation failed! hanging..."); hang(); } #endif } else if (BOOTM_ENABLE_TAGS) { debug("using: ATAGS\n"); setup_start_tag(gd->bd); if (BOOTM_ENABLE_SERIAL_TAG) setup_serial_tag(¶ms); if (BOOTM_ENABLE_CMDLINE_TAG) setup_commandline_tag(gd->bd, commandline); if (BOOTM_ENABLE_REVISION_TAG) setup_revision_tag(¶ms); if (BOOTM_ENABLE_MEMORY_TAGS) setup_memory_tags(gd->bd); if (BOOTM_ENABLE_INITRD_TAG) { /* * In boot_ramdisk_high(), it may relocate ramdisk to * a specified location. And set images->initrd_start & * images->initrd_end to relocated ramdisk's start/end * addresses. So use them instead of images->rd_start & * images->rd_end when possible. */ if (images->initrd_start && images->initrd_end) { setup_initrd_tag(gd->bd, images->initrd_start, images->initrd_end); } else if (images->rd_start && images->rd_end) { setup_initrd_tag(gd->bd, images->rd_start, images->rd_end); } } setup_board_tags(¶ms); setup_end_tag(gd->bd); } else { printf("FDT and ATAGS support not compiled in - hanging\n"); hang(); } board_prep_linux(images); } __weak bool armv7_boot_nonsec_default(void) { #ifdef CONFIG_ARMV7_BOOT_SEC_DEFAULT return false; #else return true; #endif } #ifdef CONFIG_ARMV7_NONSEC bool armv7_boot_nonsec(void) { char *s = env_get("bootm_boot_mode"); bool nonsec = armv7_boot_nonsec_default(); if (s && !strcmp(s, "sec")) nonsec = false; if (s && !strcmp(s, "nonsec")) nonsec = true; return nonsec; } #endif #ifdef CONFIG_ARM64 __weak void update_os_arch_secondary_cores(uint8_t os_arch) { } #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 static void switch_to_el1(void) { if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && (images.os.arch == IH_ARCH_ARM)) armv8_switch_to_el1(0, (u64)gd->bd->bi_arch_number, (u64)images.ft_addr, 0, (u64)images.ep, ES_TO_AARCH32); else armv8_switch_to_el1((u64)images.ft_addr, 0, 0, 0, images.ep, ES_TO_AARCH64); } #endif #endif /* Subcommand: GO */ static void boot_jump_linux(bootm_headers_t *images, int flag) { #ifdef CONFIG_ARM64 void (*kernel_entry)(void *fdt_addr, void *res0, void *res1, void *res2); int fake = (flag & BOOTM_STATE_OS_FAKE_GO); kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1, void *res2))images->ep; debug("## Transferring control to Linux (at address %lx)...\n", (ulong) kernel_entry); bootstage_mark(BOOTSTAGE_ID_RUN_OS); printf("IVP HW DEBUG: boot_jump_linux: ## Transferring control to Linux (at address %lx)...\n",(ulong) kernel_entry); announce_and_cleanup(fake); printf("IVP HW DEBUG: boot_jump_linux: 0 \n"); if (!fake) { #ifdef CONFIG_ARMV8_PSCI armv8_setup_psci(); printf("IVP HW DEBUG: boot_jump_linux: 1 \n"); #endif do_nonsec_virt_switch(); printf("IVP HW DEBUG: boot_jump_linux: 2 \n"); update_os_arch_secondary_cores(images->os.arch); printf("IVP HW DEBUG: boot_jump_linux: 3 \n"); #ifdef CONFIG_ARMV8_SWITCH_TO_EL1 printf("IVP HW DEBUG: boot_jump_linux: 3 images->ft_addr= %#08lx \n",images->ft_addr); armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, (u64)switch_to_el1, ES_TO_AARCH64); printf("IVP HW DEBUG: boot_jump_linux: 4 \n"); printf("IVP HW DEBUG: boot_jump_linux: images->ft_addr= %#08lx \n",images->ft_addr); #else if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) && (images->os.arch == IH_ARCH_ARM)){ printf("IVP HW DEBUG: boot_jump_linux: 4.1 images->ft_addr= %#08lx \n",images->ft_addr); armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number, (u64)images->ft_addr, 0, (u64)images->ep, ES_TO_AARCH32); printf("IVP HW DEBUG: boot_jump_linux: 4.2 images->ft_addr= %#08lx \n",images->ft_addr); } else{ printf("IVP HW DEBUG: boot_jump_linux: 4.3 images->ft_addr= %#08lx \n",images->ft_addr); printf("IVP HW DEBUG: boot_jump_linux: 4.3 images->ep= %#08lx \n",images->ep); armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0, (u64)images->ep, ES_TO_AARCH64); printf("IVP HW DEBUG: boot_jump_linux: 4.4 images->ft_addr= %#08lx \n",images->ft_addr); } printf("IVP HW DEBUG: boot_jump_linux: 5 \n"); #endif } #else unsigned long machid = gd->bd->bi_arch_number; char *s; void (*kernel_entry)(int zero, int arch, uint params); unsigned long r2; int fake = (flag & BOOTM_STATE_OS_FAKE_GO); kernel_entry = (void (*)(int, int, uint))images->ep; printf("IVP HW DEBUG: boot_jump_linux: 6 \n"); #ifdef CONFIG_CPU_V7M ulong addr = (ulong)kernel_entry | 1; kernel_entry = (void *)addr; printf("IVP HW DEBUG: boot_jump_linux: 7 \n"); #endif printf("IVP HW DEBUG: boot_jump_linux: 7.1 \n"); s = env_get("machid"); if (s) { if (strict_strtoul(s, 16, &machid) < 0) { debug("strict_strtoul failed!\n"); return; } printf("Using machid 0x%lx from environment\n", machid); } printf("IVP HW DEBUG: boot_jump_linux: 8 \n"); debug("## Transferring control to Linux (at address %08lx)" \ "...\n", (ulong) kernel_entry); bootstage_mark(BOOTSTAGE_ID_RUN_OS); announce_and_cleanup(fake); printf("IVP HW DEBUG: boot_jump_linux: 9 \n"); if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) r2 = (unsigned long)images->ft_addr; else r2 = gd->bd->bi_boot_params; printf("IVP HW DEBUG: boot_jump_linux: 10 \n"); if (!fake) { #ifdef CONFIG_ARMV7_NONSEC if (armv7_boot_nonsec()) { armv7_init_nonsec(); secure_ram_addr(_do_nonsec_entry)(kernel_entry, 0, machid, r2); } else #endif kernel_entry(0, machid, r2); } #endif } /* Main Entry point for arm bootm implementation * * Modeled after the powerpc implementation * DIFFERENCE: Instead of calling prep and go at the end * they are called if subcommand is equal 0. */ int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images) { /* No need for those on ARM */ if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE) return -1; if (flag & BOOTM_STATE_OS_PREP) { boot_prep_linux(images); return 0; } if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { boot_jump_linux(images, flag); return 0; } boot_prep_linux(images); boot_jump_linux(images, flag); return 0; } #if defined(CONFIG_BOOTM_VXWORKS) void boot_prep_vxworks(bootm_headers_t *images) { #if defined(CONFIG_OF_LIBFDT) int off; if (images->ft_addr) { off = fdt_path_offset(images->ft_addr, "/memory"); if (off > 0) { if (arch_fixup_fdt(images->ft_addr)) puts("## WARNING: fixup memory failed!\n"); } } #endif cleanup_before_linux(); } void boot_jump_vxworks(bootm_headers_t *images) { #if defined(CONFIG_ARM64) && defined(CONFIG_ARMV8_PSCI) armv8_setup_psci(); smp_kick_all_cpus(); #endif /* ARM VxWorks requires device tree physical address to be passed */ ((void (*)(void *))images->ep)(images->ft_addr); } #endif