arm-linux-boot-process
uboot:
https://blog.csdn.net/zhoudengqing/article/details/41345859
https://m2m-tele.com/blog/2021/10/24/u-boot-initialization-sequence/
U-boot has to reserve 3 regions in memory that stores: 1) u-boot itself, 2) uImage (compressed kernel), and 3) uncompressed kernel. These 3 regions must be carefully placed in u-boot to prevent conflict.
However, the previous stage boot-loader, (BL2 or BL1) that brings u-boot into DRAM memory don't know u-boot's planing on these 3 regions. So it can only loads u-boot onto a lower address in DRAM memory and jump to it. Then, after u-boot execute some basic initialization and detect current PC is not in planed location, u-boot call relocate function that move u-boot to the planned location and jump to it.
First stage executes on starting memory area. Than during relocation procedure U-boot copies itself to the end of RAM memory by address calculated on last steps of first stage and jumps to new address after that. Second stage executes on final memory area and results on booting kernel or providing user access console depending whether user typed anything in console or not. Each stage ends by calling same initcall_run_list function but with different arguments. That function (initcall_run_list) accepts array of initialization function pointers to be called in loop. On first stage there is init_sequence_f (common/board_f.c) array of function pointers provided to that function as input argument:
After uboot relocates the code and vector table, run board_init_r function. This function will call init_sequence_r functions in the list to initialize various periperal drivers, and finally run main_loop().
Why spl is required uboot-spl understanding.
--------------------------------------------------------------------
uboot is divided into two components: uboot-spl and uboot. SPL is the abbreviation of Secondary Program Loader, the second stage
program loader. The so-called second stage here is relative to the BROM in the SOC. The previous article has already introduced it.
The first thing to be executed when starting the SOC is the BROM. Curing program.
BROM will load the second-stage bootloader by detecting the startup method. uboot is already a bootloader, so why is there another
uboot spl?
The main reason for this is that for some SOCs, their internal SRAM may be relatively small, too small to load the next complete uboot
image, so spl is needed, which is mainly responsible for initializing the external RAM and environment, and loading the real uboot
image. to external RAM for execution.
So from this point of view, SPL should be a very small loader program that can run in the internal SRAM of the SOC. Its main function is
to load the real uboot and run it.
uboot startup is divided into three stages
1.BLO
Firmware on ROM
2. BL1 (u-boot-spl)
The main tasks are:
initialize part of the clock (related to SDRAM),
initialize DDR (external SDRAM),
load the BL2 image from the storage medium (such as SD\eMMC\nand flash) to SDRAM, and
verify the legality of the BL2 image.
Jump to the address where the BL2 mirror is located
Compile the script through uboot-spl: u-boot/arch/arm/cpulu-boot-spl.ids
ENTRY(_start)
Therefore, the code entry function of uboot-spl is _start
corresponding to the _start of the path u-boot/arch/arm/lib/vector.S. The subsequent analysis starts from this function.
3. BL2 (uboot)
The main tasks include: initializing some hardware, including clock, memory, etc., loading the kernel into the memory , loading the file
system, atags or dtb into the memory, and correctly configuring some hardware according to the operating system startup requirements.
U-boot passes kernel command line arguments via /chosen node in DTS.
https://www.kernel.org/doc/Documentation/devicetree/usage-model.txt
https://labs.dese.iisc.ac.in/embeddedlab/device-tree-and-boot-flow/
How and When Linux loads DTB?
U-Boot passes device tree information to the Linux kernel using the Flattened Device Tree (FDT) mechanism. When U-Boot loads the Linux kernel, it also loads the device tree blob
into memory and passes its location to the kernel using a specific register. The Linux kernel then parses the device tree blob to obtain information about the hardware configuration and peripherals
There is one single entry point to the kernel, at the start of the kernel image. That entry point supports two calling conventions.
ATAGS interface : Minimal information is passed from firmware to the kernel with a tagged list of predefined parameters.
r0 : 0
r1 : Machine type number
r2 : Physical address of tagged list in system RAM
Entry with a flattened device-tree block : Firmware loads the physical address of the flattened device tree block (dtb) into r2, r1 is not used.
r0 : 0
r1 : Valid machine type number. When using a device tree, a single machine type number will often be assigned to represent a class or family of SoCs.
r2 : physical pointer to the device-tree block in RAM. Device tree can be located anywhere in system RAM, but it should be aligned on a 64 bit boundary.
The kernel will differentiate between ATAGS and device tree booting by reading the memory pointed to by r2 and looking for either the flattened device tree block magic value (0xd00dfeed) or the ATAG_CORE value at offset 0x4 from r2 (0x54410001).
Device Tree Blob header is defined in include/linux/of_fdt.h
------------------------------------------------------------------------------------------------------
https://www.marcusfolkesson.se/blog/fit-vs-legacy-image-format/
https://people.kernel.org/linusw/how-the-arm32-linux-kernel-decompresses#:~:text=It%20begins%20with%208%20or,the%20start%20of%20physical%20memory.
https://www.armlinux.org.uk/developer/booting.php
Setup and initialise the RAM.
Initialise one serial port.
Detect the machine type.
Setup the kernel tagged list.
Call the kernel image.
https://blog.csdn.net/zhoudengqing/article/details/41345859
Execution in Assembly
Entry point for Kernel uncompressed ~ arch/arm/kernel/head.S
4. Read processor ID in coprocessor and obtain proc_info_list pointer
2. Lookup machine type based on mach-id passed by bootloader (R1), get the mach_desc pointer and save mach.id
3. Determine vaildty of ATAGS pointer (R2) and save
4, Setup minimal pagetable to get Kernel running on virtual address, also setup one-to-one mapping for the page that turns on MM
5. Inlialize TLB, caches, setup MMU to be turned on
6. Turn on MMU
7. Branch to start_kernel
Execution in C
entry point— start_kernel() init/main.c
1. Disable IRQ local_irq disable()
2. Print Linux banner
3. Arch specific setup setup_arch() (arch/arm/kernel/setup.c)
1. Using mach-id, get pointer to machine_desc (defined in board file)
2. Using ATAG physical pointer, obtain virtual address
3. Invoke machine specific fixup
4, Save & parse ATAGS parse _atags() (arch/arm/kernel/setup.c)
5. Parse early params parse early params
6.arm_memblock init (arch/arm/mm/init.c)
1. Invoke machine specific reserve
7. Setup pagetables paging init (arch/arm/mm/mmu.c)
1. prepare_pagetable
2. Map memory banks
3. Map machine vectors devicemaps_init
4. Invoke machine specific map_io
5. Allocate zero page
6. bootmem init (arch/arm/mm/init.c)
8. Install vectors early trap init (arch/arm/kernel/traps.c)
9. Invoke machine specific init_early
4. Build memory zone list build_all_zonelists() (mm/pagealloc.c)
5. Print Kernel command line
6. Parse early params
7. Parse commandline args parse_args (kernel/params.c)
8. pid hashtable initialization
9. Setup Kemel memory allocators mm_init
1. Mark free areas in memory & print memory details
2. Initialize page cache
3. Initialize vmalloc
10.Initialize scheduler sched_init
11.Disable preemption
12.Initialize RCU. radix tree
13.early_irq_init ( kernel/irq/irqdesc.c)
14.Invoke machine specific init_irq
15.Initialize software timers, soft irq init_timers ( kernel/timer.c) , softirq_init ( kernel/softirq .c)
16.Initialize clocksource & common timekeeping values timekeeping_init ( kernel/time/timekeeping .c)
17. time_init ( arch/arm/kernel/time.c)
1. Get machine specific timer
2. Invoke machine specific timer->init
3. sched_clock post init
18.Enable IRQ
19.Initialize console device (if console_initcall) console_init (drivers/tty/tty_io.c)
20.kmem_leak_init ( mm/kmemleak.c)
21.Allocate per_cpu pagesets setup_per_cpu_pageset ( mm/ pagealloc .c)
22.Initialize NUMA policy numa_policy_init (mm/mempolicy c )
23.calibrate_delay ( init/calibrat e .c)
24.Initialize pid map p idma p_init ( kernel/pid.c)
25.Initialize anonymous vma anon_vma_init (mm/rmap.c)
26.Initialize init process structure fork_init ( kernel/fork.c)
27. Initialize security framework security_init ( security/security.c)
28.Initialize kdb kdb_init (kernel/debug/kdb/kdb_main.c )
29.Initialize caches for VFS vfs_caches_init ( fs/dcache.c)
30.Initialize proc FS proc_root_init ( fs/proc/root.c )
31.Initialize ftrace ftrace init ( kernel/trace/ftrace.c)
32.rest_init in non-init section)
1. Spawn init process
2. Spawn kthreadd
3. Wait till kthreadd is setup
4. Enable preemption
5. schedule schedule ( kernel/sched.c)
6. Disable preemption
7. cpu_idle (arch/arm/kernel/process.c)
process - 1 (init process) kernel_init ( init/main .c )
1.Wait till kthreadd issetup
2.do_basic_setup
1. Create usermode helper wq usermodeheper_init ( kernel/kmod.c)
2. Initialize tmpfs init_tmpfs ( mm/shmem .c )
3. Initialize driver model driver_init ( drivers/base/init )
4. Call initcalls as per the order (machine specific init_machine would get called due to this) do_initcalls
3.prepare_namespace ( init/do_mounts.c)
1. Load initrd initrd_load ( init/do_mounts_initrd.c )
1. Identify decompressor method identify ramdisk image (init/do_mounts initrd.c)
2. Create memory for uncompressed FS
3. Decompress FS and copy crd_load (init/do_mounts_rd)
2. Mount root FS mount_root (init/do_mounts.c)
4. init_post (in non-init section)
1. Free memory in init section free_initmem (arch/arm/mm/init.c)
2. Replace process — 1 (a kernel thread) with userspace init process, by exec in kernel run_init_process
----------------
initcalls
start_kernel(main.c) -> setup_arch (setup.c) -> unflatten_device_tree (fdt.c) ->
-> arch_call_rest_init (main.c) -> rest_init (main.c) -> kernel_init (main.c) -> kernel_init_freeable(main.c) -> do_basic_setup(main.c) -> do_initcalls(main.c)
---------------
uboot splash
https://software-dl.ti.com/processor-sdk-linux/esd/docs/latest/linux/Foundational_Components/U-Boot/Apps-Splash-Screen.html
more links:
https://blog.csdn.net/p942005405/article/details/80226834
detailed kernel boot sequence: https://blog.csdn.net/gqb_driver/article/details/26954425
----------------------------------------------------------------------------
uboot boot commands:
https://u-boot.denx.narkive.com/hN6HB7bp/patch-config-use-booti-instead-of-bootz-on-64-bit-arm
https://www.linux4sam.org/bin/view/Linux4SAM/U-Boot
Traditional way of kernel booting:
So far, to boot kernel with zImage and normal DTB, we would use a command like this:
fatload mmc 0:1 0x24000000 zImage; fatload mmc 0:1 0x21000000 board.dtb; bootz 0x24000000 - 0x21000000;
First argument is the address in memory of the Linux kernel, second one is the initrd (missing in the example, we can see a dash), third one is the address of the DTB binary.
Loading FIT image with U-boot
The FIT image is a placeholder that has the zImage and the base Device Tree, plus additional overlays that can be selected at boot time.
Load the FIT image like you would normally load the uImage or zImage.
There is no need to load additional Device Tree Blob, the FIT image includes it
When booting the FIT image, specify the FIT configuration to use. Several configurations can be appended to the basic configuration, which we name 'kernel_dtb'
bootm command can boot below type
bootm [fit_addr]#<conf>[#extra-conf]
bootm [[fit_addr]:<os_subimg>] [[<fit_addr2>]:<rd_subimg2>] [[<fit_addr3>]:<fdt_subimg>]
bootm <addr1> [[<addr2> [<addr3>]] # Legacy boot
Comments
Post a Comment