1 CORTEX-M
1.1 boot program
Take stm32 as an example, add an online upgrade boot program with rom address:
LR_IROM1 0x08000000 0x00004000 { ; load region size_region ER_IROM1 0x08000000 0x00004000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } }
boot boots the application:
#define APPLICATION_ADDRESS (uint32_t)0x08004000 JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4); /* Jump to user application */ Jump_To_Application = (pFunction) JumpAddress; /* Initialize user application's Stack Pointer */ __set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS); Jump_To_Application();
1.2 Application
rom address
LR_IROM1 0x08004000 0x0001c000 { ; load region size_region ER_IROM1 0x08004000 0x00300000 { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } RW_IRAM1 0x20000000 0x00020000 { ; RW data .ANY (+RW +ZI) } }
Start assembly file startup***.s
Stack_Size EQU 0x00000400 ;Stack_Size EQU 0x00001000 ,,, Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT SystemInit IMPORT __main
2 The boot and start process for LINUX
2.1 uboot
In mian_loop
#define CONFIG_BOOTDELAY 3 //Set Startup Delay Time //Boot the kernel if the delay is greater than or equal to zero and no keys are received during the delay if (bootdelay >= 0 && s && !abortboot (bootdelay)) { // # ifdef CONFIG_AUTOBOOT_KEYED intprev = disable_ctrlc(1);/* disable Control C checking */ # endif //Status Settings # ifndef CFG_HUSH_PARSER { printf("Booting Linux ...\n"); //start-up linux run_command (s, 0); //Run commands to boot the kernel,s=getenv("bootcmd") }
The variables "bootcmd" and "bootargs" are used when loading the linux kernel.
The values of variables "bootcmd" and "bootargs" can be used before the linux kernel is loaded.
Modification in uboot's command console
bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
The first command Read kernel from flash kernel is a partition flag
The second command The startup command indicates the startup address
bootargs are other parameter information, run_command (getenv ("bootcmd"), flag)
bootm in bootcmd, boot application image from memory
Parameter form:'bootm addr', bootm loads default configuration macro when addr is omitted
#define CONFIG_SYS_LOAD_ADDR 0x30008000 /* default load address */
2.2 linux Kernel State
Find the location of the label u mmap_switched: /linux/arch/arm/kernel/head-common.S
__mmap_switched: /* * The following fragment of code is executed with the MMU on in MMU mode, * and uses absolute addresses; this is not position independent. * * r0 = cp#15 control register * r1 = machine ID * r2 = atags/dtb pointer * r9 = processor ID */ //Save device information, device tree, and boot parameter storage addresses ,, b start_kernel
Function location: /linux/init/Main.c, start_kernel involves a lot of initialization work, only the important ones are listed.
asmlinkage void __init start_kernel(void) { ...... //Type Judgment smp_setup_processor_id(); //smp Relevant, back to startup CPU Number ...... local_irq_disable(); //Close the current CPU interrupt early_boot_irqs_disabled = true; /* * Interrupts are still disabled. Do necessary setups, then * enable them */ boot_cpu_init(); page_address_init(); //Initialization Page Address pr_notice("%s", linux_banner); //Display Kernel Version Information setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ build_all_zonelists(NULL, NULL); page_alloc_init(); //Page memory request initialization pr_notice("Kernel command line: %s\n", boot_command_line); //Print kernel boot command line parameters parse_early_param(); parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, -1, -1, &unknown_bootoption); ...... /* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a functioning scheduler. */ sched_init(); //Process Scheduler Initialization /* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */ preempt_disable(); //Kernel Preemption Prohibited if (WARN(!irqs_disabled(), "Interrupts were enabled *very* early, fixing it\n")) local_irq_disable(); //Check off CPU interrupt /*Mass Initialization Content Knowledgeable*/ idr_init_cache(); rcu_init(); tick_nohz_init(); context_tracking_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ early_irq_init(); init_IRQ(); tick_init(); init_timers(); hrtimers_init(); softirq_init(); timekeeping_init(); time_init(); sched_clock_postinit(); perf_event_init(); profile_init(); call_function_init(); WARN(!irqs_disabled(), "Interrupts were enabled early\n"); early_boot_irqs_disabled = false; local_irq_enable(); //Local interrupts are available kmem_cache_init_late(); /* * HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ console_init(); //Initialize the console to use printk Yes if (panic_later) panic("Too many boot %s vars at `%s'", panic_later, panic_param); lockdep_info(); /* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */ locking_selftest(); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { pr_crit("initrd overwritten (0x%08lx < 0x%08lx) - disabling it.\n", page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn); initrd_start = 0; } #endif page_cgroup_init(); debug_objects_mem_init(); kmemleak_init(); setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) late_time_init(); sched_clock_init(); calibrate_delay(); pidmap_init(); anon_vma_init(); acpi_early_init(); #ifdef CONFIG_X86 if (efi_enabled(EFI_RUNTIME_SERVICES)) efi_enter_virtual_mode(); #endif #ifdef CONFIG_X86_ESPFIX64 /* Should be run before the first non-init thread is created */ init_espfix_bsp(); #endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); //Initialization fork proc_caches_init(); buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(totalram_pages); //Virtual File System Initialization signals_init(); /* rootfs populating might need page-writeback */ page_writeback_init(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif cgroup_init(); cpuset_init(); taskstats_init_early(); delayacct_init(); check_bugs(); sfi_init_late(); if (efi_enabled(EFI_RUNTIME_SERVICES)) { efi_late_init(); efi_free_boot_services(); } ftrace_init(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); } //Function last calls rest_init() function /*Most Important Mission: Create the kernel_init process and subsequently initialize it*/ static noinline void __init_refok rest_init(void) { int pid; rcu_scheduler_starting(); /* * We need to spawn init first so that it obtains pid 1, however * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); //Establish kernel_init process numa_default_policy(); pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES); rcu_read_lock(); kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns); rcu_read_unlock(); complete(&kthreadd_done); /* * The boot idle thread must execute schedule() * at least once to get things moving: */ init_idle_bootup_task(current); schedule_preempt_disabled(); /* Call into cpu_idle with preempt disabled */ //cpu_idle This is the idle function used to reduce power usage and heat generation when the system is idle. The function is not returned at this point, and the rest of the work is done from kernel_init Process Initiation cpu_startup_entry(CPUHP_ONLINE); } kernel_init The function will complete the initialization of the device driver and call init_post Function Starts User Process //Some books describe the kernel startup process based on the classic version 2.6. The kernel_init function also calls the init_post function, which is responsible for the startup of the _init process. The current version has been integrated. static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); //Completed in this function smp Turn on driver initialization, shared memory initialization, etc. /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); free_initmem(); //Initialization End, Clear Memory Unused Data mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput(); if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. *Find the init function and create process 1_init (the first user space process)*/ if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d). Attempting defaults...\n", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance."); } static int __ref kernel_init(void *unused) { int ret; kernel_init_freeable(); //Completed in this function smp Turn on driver initialization, shared memory initialization, etc. /* need to finish all async __init code before freeing the memory */ async_synchronize_full(); free_initmem(); //Initialization End, Clear Memory Unused Data mark_rodata_ro(); system_state = SYSTEM_RUNNING; numa_default_policy(); flush_delayed_fput(); if (ramdisk_execute_command) { ret = run_init_process(ramdisk_execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d)\n", ramdisk_execute_command, ret); } /* * We try each of these until one succeeds. * * The Bourne shell can be used instead of init if we are * trying to recover a really broken machine. *Find the init function and create process 1_init (the first user space process)*/ if (execute_command) { ret = run_init_process(execute_command); if (!ret) return 0; pr_err("Failed to execute %s (error %d). Attempting defaults...\n", execute_command, ret); } if (!try_to_run_init_process("/sbin/init") || !try_to_run_init_process("/etc/init") || !try_to_run_init_process("/bin/init") || !try_to_run_init_process("/bin/sh")) return 0; panic("No working init found. Try passing init= option to kernel. " "See Linux Documentation/init.txt for guidance."); }
At this point, the kernel initialization is nearing its end, and all initialization functions have been called, so the free_initmem function can discard data between u init_begin and u init_end in memory.
When the kernel is booted and initialized, the kernel starts its first user space application, init, which is the first program called to compile using the standard C library with a process number clock of 1.
_init is responsible for starting other necessary processes to make the system available as a whole.
The following is a kernel startup flowchart:
Source of picture: https://blog.csdn.net/perfect1t/article/details/81741531
Reference: https://blog.csdn.net/perfect1t/article/details/81741531
---
https://blog.csdn.net/perfect1t/article/details/81741531