Archive for the ‘bootloader’ Category

android: how bootloader little kernel sends initrd information to kernel

December 13, 2015

This post discusses how how bootloader little kernel sends initrd information to kernel.

reference code base
Qualcomm msm8994 LA.BF64.1.1-06510-8×94.0 with Android 5.0.2(LRX22G), bootloader (L)ittle (K)ernel and Linux kernel 3.10.49.

kernel config

CONFIG_BLK_DEV_INITRD=y

bootloader little kernel add initrd information into device tree
Bootloader little kernel update device tree in update_device_tree(). It adds initrd information in /chosen/linux,initrd-start and /chosen/linux,initrd-end.

	if (ramdisk_size) {
		/* Adding the initrd-start to the chosen node */
		ret = fdt_setprop_u32(fdt, offset, "linux,initrd-start",
				      (uint32_t)ramdisk);
		if (ret)
		{
			dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-start]\n");
			return ret;
		}

		/* Adding the initrd-end to the chosen node */
		ret = fdt_setprop_u32(fdt, offset, "linux,initrd-end",
				      ((uint32_t)ramdisk + ramdisk_size));
		if (ret)
		{
			dprintf(CRITICAL, "ERROR: Cannot update chosen node [linux,initrd-end]\n");
			return ret;
		}
	}

An example of device tree with initrd information
The device’s device tree shows initrd starts at 0x2000000 and ends at 0x21a1dde. The initrd is put at 32 MB from the start address of physical memory, and its size is 167 KB.

    chosen {
        linux,initrd-end = <0x21a1dde>;
        linux,initrd-start = <0x2000000>;

how kernel loads initrd information
kernel calls early_init_dt_check_for_initrd() to read initrd information from device tree. It reads /chosen/linux,initrd-start and /chosen/linux,initrd-end. It then calls early_init_dt_setup_initrd_arch() to assign these values to phys_initrd_start and phys_initrd_size.

start_kernel()
-> setup_arch()
   -> setup_machine_fdt()
      -> early_init_dt_scan()
         -> early_init_dt_scan_chosen()
            -> early_init_dt_check_for_initrd()
               -> early_init_dt_setup_initrd_arch()
         -> early_init_dt_scan_root()
         -> early_init_dt_scan_memory()
            -> early_init_dt_add_memory_arch()
               -> memblock_add()
      -> of_flat_dt_get_machine_name()
   -> arm64_memblock_init()
   -> paging_init()
-> build_all_zonelists()
/**
 * early_init_dt_check_for_initrd - Decode initrd location from flat tree
 * @node: reference to node containing initrd location ('chosen')
 */
void __init early_init_dt_check_for_initrd(unsigned long node)
{
	u64 start, end;
	int len;
	const __be32 *prop;

	pr_debug("Looking for initrd properties... ");

	prop = of_get_flat_dt_prop(node, "linux,initrd-start", &len);
	if (!prop)
		return;
	start = of_read_number(prop, len/4);

	prop = of_get_flat_dt_prop(node, "linux,initrd-end", &len);
	if (!prop)
		return;
	end = of_read_number(prop, len/4);

	early_init_dt_setup_initrd_arch(start, end);
	pr_debug("initrd_start=0x%llx  initrd_end=0x%llx\n",
		 (unsigned long long)start, (unsigned long long)end);
}
static unsigned long phys_initrd_start __initdata = 0;
static unsigned long phys_initrd_size __initdata = 0;

phys_addr_t memstart_addr __read_mostly = 0;

void __init early_init_dt_setup_initrd_arch(u64 start, u64 end)
{
	phys_initrd_start = start;
	phys_initrd_size = end - start;
}

conclusion
This post discusses how bootloader little kernel tells kernel the start and end address of initrd. Bootloade little kernel addes these information into device tree and kernel reads device tree to get initrd information.

Advertisements

android: add arguments in kernel command line

December 13, 2015

This post discusses adding arguments in kernel command line at build time. Another post android: append arguments to kernel command line of boot.img shows how to add arguments into kernel command line by modifying boot.img directly.

reference code base
Qualcomm msm8994 LA.BF64.1.1-06510-8×94.0 with Android 5.0.2(LRX22G), bootloader (L)ittle (K)ernel and Linux kernel 3.10.49.

what is kernel command line
bootloader loads and kernel and sends kernel command line are argument to kernel. Kernel parses it to set up system and reports it at /proc/cmdline.

how to add arguments into kernel cmdline
In the reference code base, kernel command line comes from below four sources. Arguments which are added in each of them could also be added into kernel command line

  • CONFIG_CMDLINE of kernel
  • bootargs of device tree
  • BOARD_KERNEL_CMDLINE of BoardConfig.mk
  • update_cmdline() of LK

add kernel command line arguments with CONFIG_CMDLINE
In below example patch, CONFIG_CMDLINE=”foo=y” could add foo=y into kernel command line. In the reference code base, CONFIG_CMDLINE_EXTEND=y is also needed. Otherwise, kernel will only read bootargs in device tree as kernel command line.

CONFIG_JOYSTICK_XPAD=y
CONFIG_QCOM_NPA_DUMP=y
CONFIG_KERNEL_TEXT_MPU_PROT
CONFIG_MSM_CORE_CTL_HELPER=y
diff --git a/arch/arm64/configs/msm8994_defconfig b/arch/arm64/configs/msm8994_defconfig
index 0c0cdc5..1143f0c 100644
--- a/arch/arm64/configs/msm8994_defconfig
+++ b/arch/arm64/configs/msm8994_defconfig
@@ -634,3 +634,5 @@ CONFIG_JOYSTICK_XPAD=y
 CONFIG_QCOM_NPA_DUMP=y
 CONFIG_KERNEL_TEXT_MPU_PROT
 CONFIG_MSM_CORE_CTL_HELPER=y
+CONFIG_CMDLINE="foo=1"
+CONFIG_CMDLINE_EXTEND=y

add kernel command line arguments with bootargs of device tree
Kernel will read chosen.bootargs of device tree as kernel command line. This patch could add foo=1 into bootargs and also kernel command line.

	chosen {
		bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1";
	};
diff --git a/arch/arm/boot/dts/qcom/msm8994.dtsi b/arch/arm/boot/dts/qcom/msm8994.dtsi
index a4db1a1..3f83293 100644
--- a/arch/arm/boot/dts/qcom/msm8994.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8994.dtsi
@@ -24,7 +24,7 @@
        interrupt-parent = <&intc>;
 
        chosen {
-               bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1";
+               bootargs = "foo=1 sched_enable_hmp=1 sched_enable_power_aware=1";
        };
 
        aliases {

add kernel command line arguments with BOARD_KERNEL_CMDLINE of BoardConfig.mk
BOARD_KERNEL_CMDLINE is put in the header of boot.img. Then bootloader parse boot.img and appends BOARD_KERNEL_CMDLINE to bootargs of device tree. The blow patch could add foo=1 into kernel command line.

BOARD_KERNEL_CMDLINE := console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 boot_cpus=0-5
diff --git a/BoardConfig.mk b/BoardConfig.mk
index b36a0f8..f51c5c0 100644
--- a/BoardConfig.mk
+++ b/BoardConfig.mk
@@ -51,7 +51,7 @@ TARGET_USES_ION := true
 TARGET_USES_NEW_ION_API :=true
 TARGET_USES_OVERLAY := true

-BOARD_KERNEL_CMDLINE := console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 boot_cpus=0-5
+BOARD_KERNEL_CMDLINE := foo=1 console=ttyHSL0,115200,n8 androidboot.console=ttyHSL0 androidboot.hardware=qcom user_debug=31 msm_rtb.filter=0x37 ehci-hcd.park=3 lpm_levels.sleep_disabled=1 boot_cpus=0-5

 BOARD_EGL_CFG := device/qcom/$(TARGET_BOARD_PLATFORM)/egl.cfg
 BOARD_KERNEL_SEPARATED_DT := true

Below shows that the command line in the header boot.img is different before and after the patch.

$ xxd out/target/product/msm8994/boot.img | less
0000000: 414e 4452 4f49 4421 182f 8101 0080 0000  ANDROID!./......
0000010: 8af2 0e00 0000 0001 0000 0000 0000 f000  ................
0000020: 0001 0000 0010 0000 00e0 9b00 0000 0000  ................
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 636f 6e73 6f6c 653d 7474 7948 534c 302c  console=ttyHSL0,
0000050: 3131 3532 3030 2c6e 3820 616e 6472 6f69  115200,n8 androi
0000060: 6462 6f6f 742e 636f 6e73 6f6c 653d 7474  dboot.console=tt
0000070: 7948 534c 3020 616e 6472 6f69 6462 6f6f  yHSL0 androidboo
0000080: 742e 6861 7264 7761 7265 3d71 636f 6d20  t.hardware=qcom 
0000090: 7573 6572 5f64 6562 7567 3d33 3120 6d73  user_debug=31 ms
00000a0: 6d5f 7274 622e 6669 6c74 6572 3d30 7833  m_rtb.filter=0x3
00000b0: 3720 6568 6369 2d68 6364 2e70 6172 6b3d  7 ehci-hcd.park=
00000c0: 3320 6c70 6d5f 6c65 7665 6c73 2e73 6c65  3 lpm_levels.sle
00000d0: 6570 5f64 6973 6162 6c65 643d 3120 626f  ep_disabled=1 bo
00000e0: 6f74 5f63 7075 733d 302d 3500 0000 0000  ot_cpus=0-5.....
$ xxd out/target/product/msm8994/boot.img | less
0000000: 414e 4452 4f49 4421 182f 8101 0080 0000  ANDROID!./......
0000010: 8cf2 0e00 0000 0001 0000 0000 0000 f000  ................
0000020: 0001 0000 0010 0000 00e0 9b00 0000 0000  ................
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 666f 6f3d 3120 636f 6e73 6f6c 653d 7474  foo=1 console=tt
0000050: 7948 534c 302c 3131 3532 3030 2c6e 3820  yHSL0,115200,n8 
0000060: 616e 6472 6f69 6462 6f6f 742e 636f 6e73  androidboot.cons
0000070: 6f6c 653d 7474 7948 534c 3020 616e 6472  ole=ttyHSL0 andr
0000080: 6f69 6462 6f6f 742e 6861 7264 7761 7265  oidboot.hardware
0000090: 3d71 636f 6d20 7573 6572 5f64 6562 7567  =qcom user_debug
00000a0: 3d33 3120 6d73 6d5f 7274 622e 6669 6c74  =31 msm_rtb.filt
00000b0: 6572 3d30 7833 3720 6568 6369 2d68 6364  er=0x37 ehci-hcd
00000c0: 2e70 6172 6b3d 3320 6c70 6d5f 6c65 7665  .park=3 lpm_leve
00000d0: 6c73 2e73 6c65 6570 5f64 6973 6162 6c65  ls.sleep_disable
00000e0: 643d 3120 626f 6f74 5f63 7075 733d 302d  d=1 boot_cpus=0-
00000f0: 3500 0000 0000 0000 0000 0000 0000 0000  5...............

add kernel command line arguments with update_cmdline() of bootloader LK
In entry_func_ptr(), LK sets up linux address, ramdisk address, and tag/device address. It call update_cmdline() to update command line before setting up tag/device. So developers could add kernel arguments in update_cmdline(). In arm32, tags/device address is passed to kernel in register r2 because it’s one of the argument while calling kernel. entry(0, machtype, (unsigned*)tags_phys). In arm64, I am not sure how LK passes tags/device address to linux.

typedef void entry_func_ptr(unsigned, unsigned, unsigned*);
void boot_linux(void *kernel, unsigned *tags,
		const char *cmdline, unsigned machtype,
		void *ramdisk, unsigned ramdisk_size)
{
	unsigned char *final_cmdline;
#if DEVICE_TREE
	int ret = 0;
#endif

	void (*entry)(unsigned, unsigned, unsigned*) = (entry_func_ptr*)(PA((addr_t)kernel));
	uint32_t tags_phys = PA((addr_t)tags);
	struct kernel64_hdr *kptr = (struct kernel64_hdr*)kernel;

	ramdisk = (void *)PA((addr_t)ramdisk);

	final_cmdline = update_cmdline((const char*)cmdline);

#if DEVICE_TREE
	dprintf(INFO, "Updating device tree: start\n");

	/* Update the Device Tree */
	ret = update_device_tree((void *)tags,(const char *)final_cmdline, ramdisk, ramdisk_size);
	if(ret)
	{
		dprintf(CRITICAL, "ERROR: Updating Device Tree Failed \n");
		ASSERT(0);
	}
	dprintf(INFO, "Updating device tree: done\n");
#else
	/* Generating the Atags */
	generate_atags(tags, final_cmdline, ramdisk, ramdisk_size);
#endif

	free(final_cmdline);

#if VERIFIED_BOOT
	/* Write protect the device info */
	if (target_build_variant_user() && mmc_write_protect("devinfo", 1))
	{
		dprintf(INFO, "Failed to write protect dev info\n");
		ASSERT(0);
	}
#endif

	/* Perform target specific cleanup */
	target_uninit();

	/* Turn off splash screen if enabled */
#if DISPLAY_SPLASH_SCREEN
	target_display_shutdown();
#endif


	dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d), tags/device tree @ %p\n",
		entry, ramdisk, ramdisk_size, (void *)tags_phys);

	enter_critical_section();

	/* do any platform specific cleanup before kernel entry */
	platform_uninit();

	arch_disable_cache(UCACHE);

#if ARM_WITH_MMU
	arch_disable_mmu();
#endif
	bs_set_timestamp(BS_KERNEL_ENTRY);

	if (IS_ARM64(kptr))
		/* Jump to a 64bit kernel */
		scm_elexec_call((paddr_t)kernel, tags_phys);
	else
		/* Jump to a 32bit kernel */
		entry(0, machtype, (unsigned*)tags_phys);
}

conclusion
This post discusses adding arguments in kernel command line. It shows four methods to update kernel command line. These changes are updated at compile time by building boot.img or bootloader lk.

android: arm: bootloader: how (L)ittle (K)ernel loads boot.img

September 22, 2015

This post is to discuss how (L)ittle (K)ernel loads boot.img in arm architecture. The reference source code here is CodeAuora LA.BR.1.3.1-09730-8952.0.

download the reference code

$ repo init -u git://codeaurora.org/quic/la/platform/manifest -b refs/tags/LA.BR.1.3.1-09730-8952.0 -m LA.BR.1.3.1-09730-8952.0.xml
$ repo sync -cd -j32

code revision

  • android-4.0.4_r1.2 BUILD_ID=IMM76I
  • kernel 3.0.21 Sneaky Weasel

boot image format supported by this LK
In android: boot.img , we discuss boot image format. In this case, the boot image is customized by SOC vendors. This customized boot image has additional device tree part. The image in this part is called dt.img.

/*
** +-----------------+ 
** | boot header     | 1 page
** +-----------------+
** | kernel          | n pages  
** +-----------------+
** | ramdisk         | m pages  
** +-----------------+
** | second stage    | o pages
** +-----------------+
** | device tree     | p pages
** +-----------------+
**
** n = (kernel_size + page_size - 1) / page_size
** m = (ramdisk_size + page_size - 1) / page_size
** o = (second_size + page_size - 1) / page_size
** p = (dt_size + page_size - 1) / page_size
** 0. all entities are page_size aligned in flash

little kernel memory layout
Within the memory layout, LK loads boot image into SCRATCH_ADDR at first. In this case, SCRATCH_ADDR functions as temporary buffer.

static mmu_section_t mmu_section_table[] = {
/*           Physical addr,         Virtual addr,            Size (in MB),     Flags */
	{    MEMBASE,               MEMBASE,                 (MEMSIZE / MB),   LK_MEMORY},
	{    MSM_IOMAP_BASE,        MSM_IOMAP_BASE,          MSM_IOMAP_SIZE,   IOMAP_MEMORY},
	{    APPS_SS_BASE,          APPS_SS_BASE,            APPS_SS_SIZE,      IOMAP_MEMORY},
	{    MSM_SHARED_IMEM_BASE,  MSM_SHARED_IMEM_BASE,    1,                COMMON_MEMORY},
	{    SCRATCH_ADDR,          SCRATCH_ADDR,            512,              SCRATCH_MEMORY},
	{    MIPI_FB_ADDR,          MIPI_FB_ADDR,            42,              COMMON_MEMORY},
};
MEMBASE := 0x8F600000 # SDRAM
MEMSIZE := 0x00300000 # 3MB

BASE_ADDR        := 0x80000000
SCRATCH_ADDR     := 0x90000000

loads boot image from emmc into DDR

  • find boot partition in emmc
  • int boot_linux_from_mmc(void)
    {
    ......
    	if (!boot_into_recovery) {
    		index = partition_get_index("boot");
    		ptn = partition_get_offset(index);
    		if(ptn == 0) {
    			dprintf(CRITICAL, "ERROR: No boot partition found\n");
                        return -1;
    		}
    	}
    ......
    }
    
  • load boot image header
  • int boot_linux_from_mmc(void)
    {
    ......
    	if (mmc_read(ptn + offset, (uint32_t *) buf, page_size)) {
    		dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");
                    return -1;
    	}
    
    	if (memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
    		dprintf(CRITICAL, "ERROR: Invalid boot image header\n");
                    return -1;
    	}
    ......
    }
    
  • calculate image size
  • Assign image_addr as SCRATCH_ADDR.

    int boot_linux_from_mmc(void)
    {
    ......
    	kernel_actual  = ROUND_TO_PAGE(hdr->kernel_size,  page_mask);
    	ramdisk_actual = ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);
    
    	image_addr = (unsigned char *)target_get_scratch_address();
    
    #if DEVICE_TREE
    	dt_actual = ROUND_TO_PAGE(hdr->dt_size, page_mask);
    	imagesize_actual = (page_size + kernel_actual + ramdisk_actual + dt_actual);
    #else
    	imagesize_actual = (page_size + kernel_actual + ramdisk_actual);
    #endif
    ......
    }
    
  • copy boot image to DDR
  • LK loads boot image into image_addr at first.

    int boot_linux_from_mmc(void)
    {
    ......
    	dprintf(INFO, "Loading boot image (%d): start\n", imagesize_actual);
    	bs_set_timestamp(BS_KERNEL_LOAD_START);
    
    	/* Read image without signature */
    	if (mmc_read(ptn + offset, (void *)image_addr, imagesize_actual))
    	{
    		dprintf(CRITICAL, "ERROR: Cannot read boot image\n");
    		return -1;
    	}
    
    	dprintf(INFO, "Loading boot image (%d): done\n", imagesize_actual);
    ......
    }
    

load kernel into DDR
Check if boot image is gzip format. If yes, decompress compressed kernel.

int boot_linux_from_mmc(void)
{
......
	if (is_gzip_package((unsigned char *)(image_addr + page_size), hdr->kernel_size))
	{
		out_addr = (unsigned char *)(image_addr + imagesize_actual + page_size);
		out_avai_len = target_get_max_flash_size() - imagesize_actual - page_size;
		dprintf(INFO, "decompressing kernel image: start\n");
		rc = decompress((unsigned char *)(image_addr + page_size),
				hdr->kernel_size, out_addr, out_avai_len,
				&dtb_offset, &out_len);
		if (rc)
		{
			dprintf(CRITICAL, "decompressing kernel image failed!!!\n");
			ASSERT(0);
		}

		dprintf(INFO, "decompressing kernel image: done\n");
		kptr = (struct kernel64_hdr *)out_addr;
		kernel_start_addr = out_addr;
		kernel_size = out_len;
	} else {
		kptr = (struct kernel64_hdr *)(image_addr + page_size);
		kernel_start_addr = (unsigned char *)(image_addr + page_size);
		kernel_size = hdr->kernel_size;
	}
......
}

update kernel, ramdisk, atags address

int boot_linux_from_mmc(void)
{
......
	/*
	 * Update the kernel/ramdisk/tags address if the boot image header
	 * has default values, these default values come from mkbootimg when
	 * the boot image is flashed using fastboot flash:raw
	 */
	update_ker_tags_rdisk_addr(hdr, IS_ARM64(kptr));

	/* Get virtual addresses since the hdr saves physical addresses. */
	hdr->kernel_addr = VA((addr_t)(hdr->kernel_addr));
	hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));
	hdr->tags_addr = VA((addr_t)(hdr->tags_addr));
......
}

static void update_ker_tags_rdisk_addr(struct boot_img_hdr *hdr, bool is_arm64)
{
	/* overwrite the destination of specified for the project */
#ifdef ABOOT_IGNORE_BOOT_HEADER_ADDRS
	if (is_arm64)
		hdr->kernel_addr = ABOOT_FORCE_KERNEL64_ADDR;
	else
		hdr->kernel_addr = ABOOT_FORCE_KERNEL_ADDR;
	hdr->ramdisk_addr = ABOOT_FORCE_RAMDISK_ADDR;
	hdr->tags_addr = ABOOT_FORCE_TAGS_ADDR;
#endif
}
DEFINES += CRYPTO_BAM=1
DEFINES += SPMI_CORE_V2=1
DEFINES += ABOOT_IGNORE_BOOT_HEADER_ADDRS=1
#define SDRAM_START_ADDR                   0x80000000

#define DDR_START                          get_ddr_start()
#define ABOOT_FORCE_KERNEL_ADDR            DDR_START + 0x8000
#define ABOOT_FORCE_KERNEL64_ADDR          DDR_START + 0x80000
#define ABOOT_FORCE_RAMDISK_ADDR           DDR_START + 0x2000000
#define ABOOT_FORCE_TAGS_ADDR              DDR_START + 0x1E00000

move kernel, ramdisk and device tree to correct address

int boot_linux_from_mmc(void)
{
......
	/* Move kernel, ramdisk and device tree to correct address */
	memmove((void*) hdr->kernel_addr, kernel_start_addr, kernel_size);
	memmove((void*) hdr->ramdisk_addr, (char *)(image_addr + page_size + kernel_actual), hdr->ramdisk_size);
......
}

setup atags
This version of LK supports loading device tree from dt.img. It also supports decompress dt.img if it is compressed in gzip format. The LK also supports loading device tree appended to kernel.

int boot_linux_from_mmc(void)
{
......
#if DEVICE_TREE
	if(hdr->dt_size) {
		dt_table_offset = ((uint32_t)image_addr + page_size + kernel_actual + ramdisk_actual + second_actual);
		table = (struct dt_table*) dt_table_offset;

		if (dev_tree_validate(table, hdr->page_size, &dt_hdr_size) != 0) {
			dprintf(CRITICAL, "ERROR: Cannot validate Device Tree Table \n");
			return -1;
		}

		/* Find index of device tree within device tree table */
		if(dev_tree_get_entry_info(table, &dt_entry) != 0){
			dprintf(CRITICAL, "ERROR: Getting device tree address failed\n");
			return -1;
		}

		if (is_gzip_package((unsigned char *)dt_table_offset + dt_entry.offset, dt_entry.size))
		{
			unsigned int compressed_size = 0;
			out_addr += out_len;
			out_avai_len -= out_len;
			dprintf(INFO, "decompressing dtb: start\n");
			rc = decompress((unsigned char *)dt_table_offset + dt_entry.offset,
					dt_entry.size, out_addr, out_avai_len,
					&compressed_size, &dtb_size);
			if (rc)
			{
				dprintf(CRITICAL, "decompressing dtb failed!!!\n");
				ASSERT(0);
			}

			dprintf(INFO, "decompressing dtb: done\n");
			best_match_dt_addr = out_addr;
		} else {
			best_match_dt_addr = (unsigned char *)dt_table_offset + dt_entry.offset;
			dtb_size = dt_entry.size;
		}

		/* Validate and Read device device tree in the tags_addr */
		if (check_aboot_addr_range_overlap(hdr->tags_addr, dtb_size))
		{
			dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
			return -1;
		}

		memmove((void *)hdr->tags_addr, (char *)best_match_dt_addr, dtb_size);
	} else {
		/* Validate the tags_addr */
		if (check_aboot_addr_range_overlap(hdr->tags_addr, kernel_actual))
		{
			dprintf(CRITICAL, "Device tree addresses overlap with aboot addresses.\n");
			return -1;
		}
		/*
		 * If appended dev tree is found, update the atags with
		 * memory address to the DTB appended location on RAM.
		 * Else update with the atags address in the kernel header
		 */
		void *dtb;
		dtb = dev_tree_appended((void*)(image_addr + page_size),
					hdr->kernel_size, dtb_offset,
					(void *)hdr->tags_addr);
		if (!dtb) {
			dprintf(CRITICAL, "ERROR: Appended Device Tree Blob not found\n");
			return -1;
		}
	}
	#endif
......
}

boot linux

  • If device tree is enabled, cmdline is stored in bootargs node of device tree.
  • In update_cmdline(), LK loads cmdline from boot image header, and adds the commands in this cmdline into bootargs node of device tree.
  • LK is arm-32 bit. If kernel is also arm-32 bit, then LK could jump to kernel directly with entry(0, machtype, (unsigned*)tags_phys)
  • According to Documentation/arm/Booting, before jumping to kernel, r0 is set as 0, r1 is set as machine type, and r3 is set as atags address.
  • int boot_linux_from_mmc(void)
    {
    ......
    	boot_linux((void *)hdr->kernel_addr, (void *)hdr->tags_addr,
    		   (const char *)hdr->cmdline, board_machtype(),
    		   (void *)hdr->ramdisk_addr, hdr->ramdisk_size);
    ......
    }
    
    typedef void entry_func_ptr(unsigned, unsigned, unsigned*);
    void boot_linux(void *kernel, unsigned *tags,
    		const char *cmdline, unsigned machtype,
    		void *ramdisk, unsigned ramdisk_size)
    {
    	unsigned char *final_cmdline;
    #if DEVICE_TREE
    	int ret = 0;
    #endif
    
    	void (*entry)(unsigned, unsigned, unsigned*) = (entry_func_ptr*)(PA((addr_t)kernel));
    	uint32_t tags_phys = PA((addr_t)tags);
    	struct kernel64_hdr *kptr = (struct kernel64_hdr*)kernel;
    
    	ramdisk = (void *)PA((addr_t)ramdisk);
    
    	final_cmdline = update_cmdline((const char*)cmdline);
    
    #if DEVICE_TREE
    	dprintf(INFO, "Updating device tree: start\n");
    
    	/* Update the Device Tree */
    	ret = update_device_tree((void *)tags,(const char *)final_cmdline, ramdisk, ramdisk_size);
    	if(ret)
    	{
    		dprintf(CRITICAL, "ERROR: Updating Device Tree Failed \n");
    		ASSERT(0);
    	}
    	dprintf(INFO, "Updating device tree: done\n");
    #else
    	/* Generating the Atags */
    	generate_atags(tags, final_cmdline, ramdisk, ramdisk_size);
    #endif
    ......
    	/* do any platform specific cleanup before kernel entry */
    	platform_uninit();
    
    	arch_disable_cache(UCACHE);
    
    #if ARM_WITH_MMU
    	arch_disable_mmu();
    #endif
    	bs_set_timestamp(BS_KERNEL_ENTRY);
    
    	if (IS_ARM64(kptr))
    		/* Jump to a 64bit kernel */
    		scm_elexec_call((paddr_t)kernel, tags_phys);
    	else
    		/* Jump to a 32bit kernel */
    		entry(0, machtype, (unsigned*)tags_phys);
    }
    

    conclusion
    This post goes through the code flow in which how LK loads boot.img into DDR, sets up kernel, ramdisk, atgas, and then jump to kernel. LK is arm 32-bit. If kernel is arm 32-bit, LK could jump to kernel directly. If kernel is arm-64 bit, LK couldn’t jump to kernel directly. We’ll cover this topic in another post.


    %d bloggers like this: