PowerPC-based Linux kernel tour: 1st-early_init

Source: Internet
Author: User

A long time ago, I wrote an article about Linux source code analysis based on the PowerPC architecture. However, the Linux source code is too large and the logic is complicated. In addition, I do not know much about PowerPC assembly, there is not much leisure time and there is no chance at all. Last month, I learned a lot about PowerPC assembly when I analyzed the source code of U-boot, after porting the driver source code of Linux SPI and NAND Flash to VxWorks, I feel that the time is quite mature. I started the trial analysis with just a little bit, I also hope that the experts who have the shortcomings can point out. This article will only focus on the PowerPC platform. I am more interested in the CPU internal mechanism, so most of the content is related to the specific CPU initialization. in the s file, I will selectively analyze the internal interrupt mechanism and peripheral drivers based on my experience. In addition, most of the source code in the analysis phase of the startup code is under the arch/PowerPC/kernel folder. If it is not, I will give a relative path.

The Linux Startup steps in the PowerPC system are similar to those in most other architectures. The system boot will start from arch/PowerPC/kernel/head_32.s and then go to init/main. the start_kernel function in C initializes the kernel. First, let's take a look at the next entry point file head_32.s, where R1 ~ The content and meaning of the five registers R5 are not very clear, but we can determine that the initial value of R5 is 0, and then jump to the start function to execute the early_init function in setup_32.c to view the definition of the function, the Code is as follows:

/* Notrace is a function attribute: Disable trace, defined as _ attribute _ (no_instrument_function )), if you don't understand it, you can refer to my previous introduction to this keyword */notrace unsigned long _ init early_init (unsigned long dt_ptr) {/* reloc_offset is defined in Misc. s file, used to return the value */unsigned long offset = reloc_offset (); struct cpu_spec * spec;/* First clears the BSS segment, memset_io is used here, because no cache is available for the moment */memset_io (void _ iomem *) ptrreloc (& __ bss_start), 0 ,__ bss_stop-_ bss_start ); /* determine the CPU type. The mfspr (sprn_pvr) command obtains the CPU version number */spec = identify_cpu (offset, mfspr (sprn_pvr); do_feature_fixups (spec-> cpu_features, ptrreloc (& __ start ___ ftr_fixup), ptrreloc (& __ stop ___ ftr_fixup); do_feature_fixups (spec-> mmu_features, ptrreloc (& __ start ___ timeout ), ptrreloc (& __ stop ___ mmu_ftr_fixup); substring (spec-> cpu_features, ptrreloc (& __ start ___ lwsync_fixup), ptrreloc (& __ stop ___ lwsync_fixup )); return kernelbase + offset ;}

In early_init, note that during this phase, the address of the kernel runtime may be different from its connection address. Therefore, you must use the reloc and ptrreloc functions when accessing static data, the two functions are defined in Misc. the code in the s file is as follows:

/* This function returns the value (current run address) minus the value (Program link address). This function is used to use */_ global (reloc_offset) when the program and data are not mapped to kernelbase) the value of the mflrr0/* link register */bl1f/* jumps to the address where 1 is located, which is the actual address of the current Code, in this way, mflr R3 stores the current run address of the current BL 1f in R3 */1: mflrr3ppc_llr4, (2f-1b) (r3)/* ppc_ll indicates lwz, and the number of loads immediately, the resulting R4 is the 1f link address */subfr3, R4, R3, get the offset of the current running address and link address */mtlrr0/* restore the saved function address */BLR. align32: ppc_long 1b

As mentioned above, kernelbase is defined in ASM \ page. H, which is the starting virtual address of the kernel and is usually equal to page_offset. Because the link address is 0xc0000000 (defined in configs/* _ defconfig by default), the subtraction result must be negative. According to the PowerPC Abi specification, R3 is the function return value register, so the offset between the current running address and the link address is obtained. Note: There is an important implicit convention in this function: assume that if MMU is enabled, the mming relationship of this MMU is, that is, the virtual address and physical address are the same. This is because when calculating the offset, reloc_offset uses "currently running address"-"code link address". This so-called currently running address can be understood as a real address, if the MMU ing is not, after the MMU is disabled, the current running address must be added with an offset to obtain the real address, therefore, the offset returned by the current function is the offset address when MMU is mapped. Remember this. To use MMU in bootloader, you must use a ing when ing the bat register.

Then, the function clears BSS. The values of _ bss_start and _ bss_stop are in Boot/zimage. lDs. in S, determine the CPU type (identify_cpu), and then perform related fix_up operations based on the specific CPU. The code in identify_cpu is defined in MISC in earlier versions of kernel. the assembly code in S is now in the C language, which is not very difficult to understand. The general step is to first find the type corresponding to the CPU in the cpu_specs array through PVR & pvr_mask = pvr_value, after finding it, call the setup_cpu_spec function when the matching set of CPU parameters are input values. This is also a very simple function. Comments are better than code. I believe you can understand it. The main function is to make up for the deficiencies defined in the original array, such as PMC (Performance
Monitor countor Performance Monitor), there is also an oprofile, is a performance analysis tool in Linux, the specific mechanism is not detailed, wait for the expert analysis, and work in the compatibility mode solution. I will not go into details here. Let's take a look at the three fixup functions behind early_init. The variables _ start ___ ** _ fixup and _ Stop ___ ** _ fixup are all in vmlinux. lDs. defined in the s file. Here, Linux uses a very advanced technique to reuse code. Because different processors have different features, such as separate commands and data caches, unified command data caches, dynamic power management features, and hardware or software TLB lookup, the vast majority of CPUs only have different features. The other components, such as the instruction set, are the same. Different source files provided for these processors are redundant, which is not easy to maintain in a unified manner. Therefore, Linux adds a special macro definition before and after operating the Code related to the processor features to put such code into a separate segment. Once the CPU type is determined, if the CPU has a certain feature, the code that operates on this feature is not processed. If the CPU does not have this feature, replace all the code that operates on this feature with a null Operation Command (NOP ), all processing is completed in this separate segment. This method is very useful when there is a small amount of code to operate on a certain feature of the Code, which is more effective than using the combined command to judge and then jump. The six variables mentioned above are in vmlinux. lDs. the segment in S is defined:

. = Align (8 );

_ Ftr_fixup: At (ADDR (_ ftr_fixup)-load_offset ){

_ Start ___ ftr_fixup = .;

* (_ Ftr_fixup)

_ Stop ___ ftr_fixup = .;

}

In addition, the include/ASM/Feature-fixup.h has the following definitions:

# Define begin_ftr_section_nested (Label) start_ftr_section (Label)

# Define begin_ftr_section start_ftr_section (97)

 

# Define end_ftr_section_nested (MSK, Val, label )\

Ftr_section_else_nested (Label )\

Make_ftr_section_entry (MSK, Val, label, _ ftr_fixup)

 

# Define end_ftr_section (MSK, Val )\

End_ftr_section_nested (MSK, Val, 97)

Code related to all features is stored in begin_ftr_section and end_ftr_section. End_ftr_section_ifset indicates that when the CPU has a certain feature, the Code contained in the middle is valid and does not need to be replaced. For end_ftr_section_ifclr, it indicates that when the CPU does not have a certain feature, the Code contained in the middle is valid and does not need to be replaced. The Code included in the in_xxx and end_xxx macros is stored in the _ ftr_fixup segment during the link. As for the three functions, that is, to implement such a function, you can obtain its features through a specific CPU. If special processing is required, add the corresponding functions; otherwise, leave them blank.

After the early_init function is completed, you can jump to and execute the mmu_off function to disable the MMU function, including clearing bat and TLB (fast table). This is to prepare for MMU initialization and ensure the system environment is clean. After the mmu_off function, the program will continue to jump to the clear_bats and flush_tlbs functions to clear the tasks, to ensure that the impact of MMU enabling is completely eliminated. The specific code is as follows:

Mmu_off: addir4, R3, _ after_mmu_off-_ start/* The _ after_mmu_off offset address of R4 */mfmsrr3andi. r0, R3, msr_dr | msr_ir/* checks the DR and IR bits of MSR to determine whether MMU is enabled */beqlr/* If not, the system returns */andc R3, R3, r0/* Otherwise, reverse the R0, that is, disable MMU */mtsprsprn_srr0, r4mtsprsprn_srr1, r3syncrfi/* return from interrupt, interrupt return, and re-load from SRR */

The SRR is the machine status save/restore register in PowerPC. The CPU has two SSR in administrator mode. ssr0 is used to store the command address and RFI when the interrupt occurs (the interrupt is returned) the return address of the CPU when the command is executed, and ssr1 stores the status of the machine during interruption and recovery. R3 In this function is returned by early_init and contains the physical address currently running. After this is explained, the Code section is easy to understand. First, load the current machine status and the running address of _ after_mmu_off for R3 and R4 respectively, and then determine whether the MMU of the current CPU is enabled. If yes, then, the _ after_mmu_off command is interrupted, and the result is returned after completion. The code for _ after_mmu_off is very simple, that is, two calls jump to clear_bats and flush_tlbs. The code for these two functions is as follows:

Clear_bats: lir10, 0mfsprr9, sprn_pvrrlwinmr9, R9, 16, 16, 31/* R9 shifted to 16 places right, R9 = 1 for 601, 4 for 604 */cmpwir9, 1beq1f/* write 0 clear */mtsprsprn_dbat0u, R10 ..... Mtsprsprn_dbat3l, r101: mtsprsprn_ibat0u, R10 ..... Mtsprsprn_ibat3l, r10blrflush_tlbs: lisr10, 0x401: addic. r10, R10,-0x1000tlbier10/* tlbie is the PowerPC operation code, and the corresponding power is tlbi, meaning to make The TLB entry invalid */bgt1b/* greater than the jump */syncblr

The process is simple, and the comments are clear. After these functions are executed, the CPU reinitializes the bat register. Note: The purpose of using BAT here is to set the Linux runtime environment by using a simple bat ing before MMU is fully enabled, so that the subsequent complex operations can be completed using the C language. The CPU jumps to execute the initial_bats Code as follows:

Initial_bats: lisr11, page_offset @ H/* The default value is 0xc0000000, which is defined in configs/mpc83xx_defconfig */mfsprr9, sprn_pvrrlwinmr9, R9, 16,16, 31/* R9 = 1, 4 For 604 */cmpwi0, R9, 1bne4forir11, R11, 4/* set up bat registers for 601 */lir8, 0x7f/* Valid, block length = 8 Mb */mtsprsprn_ibat0u, r11/* n. b. 601 has valid bit in */region, R8/* lower bat register */addisr11, R11, 0x800000 @ haddisr8, R8, 0x800000 @ hmtsprsprn_ibat1u, clerk, r8addisr11, R11, 0x800000 @ haddisr8, R8, 0x800000 @ random, random, r8isyncblr4: tophys (R8, R11)/* this is equivalent to Addis R8, R11, 0 command */# ifdef config_smporir8, R8, 0x12/* r/W access, M = 1 */# elseorir8, R8, 2/* r/W access */# endif/* config_smp */orir11, R11, bl_256m <2 | 0x2/* set up bat registers for 604 */mtsprsprn_dbat0l, R8/* n. b. 6XX (not 601) have valid */mtsprsprn_dbat0u, R11/* bit in upper BAT register */mtsprsprn_ibat0l, r8mtsprsprn_ibat0u, r11isyncblr

I have very few comments on this code, because the datasheet of my board does not provide detailed descriptions of the bat register, so let's take a look at the original explanation. The basic meaning is that on the 601 platform, three 8 m Bat files are used and are located in the 24 m memory starting with page_offset. on other platforms, the memory size is M. The 24 m space of the three sets of BAT is sufficient for the current Linux kernel and there is no dynamic memory allocation during startup. After the initialization is completed, the CPU starts to call the setup_cpu function to initialize the CPU. The function is called call_setup_cpu and is located in the misc_32.s file. (Note: MMU has been disabled at this time, therefore, you must call reloc_offset at the beginning to get the offset and pass it out through R3. The following functions are also used to save space when listing code.) The specific code is as follows:

_ Global (call_setup_cpu) addisr4, R3, cur_cpu_spec @ haaddir4, R4, cur_cpu_spec @ l/* cur_cpu_spec is the corresponding struct */lwzr4, 0 (R4) addr4, R4, small, cpu_spec_setup (R4)/* Find the cpu_setup_t member in the cpu_spec struct */cmpwi0, R5, 0addr5, R5, r3beqlrmtctrr5bctr

Here the macro definition cpu_spec_setup is defined in the asm-offsets.c file, specifically defined as define (cpu_spec_setup, offsetof (struct cpu_spec, cpu_setup), that is, find cpu_setup_t members. In the function, R3 is the data offset, and R4 is the pointer to cpu_spec (reloc_offset ). Here, the CPU of my mpc83xx series is 603, which has a simple structure and needs to be initialized including FPU (floating
Point Unit), hid0, and clear LRU, the specific code is as follows:

_ Global (_ setup_cpu_603) macro/* macro defined in the Feature-fixups.h, this sentence is MMU feature dependent sections */lir10, 0mtsprsprn_sprg_603_lru, R10/* init sw lru tracking, sprg4 write 0, clear LRU: least recently used */kernel (feature) begin_ftr_section/* CPU feature dependent sections */BL _ init_fpu_registers/* initialize FPU register */end_ftr_section_ifclr (feature) blsetup_common_caches/* initialize and configure hid0 to enable cache */mtlrr4/* re-load LR and jump back */BLR

As mentioned above, the meaning of the four in _ ** macros is the same as that mentioned above. For the purpose of code reuse, the operation between macros is determined based on the features of a specific CPU. The first sentence is to write sprg4 to 0. For the sprg concept, you can refer to my previous article about the e300 processor kernel. In a 32-bit PowerPC processor, sprg0 and 1 are used to store the abnormal vector table, and sprg2 is used to specify that the current table is in RTAs (run-time
Using action services), sprg3 is used to store the pointer of thread information (thread_info), and sprg4 is filled with data of TLB and LRU. The two main functions of this function are to initialize FPU and configure hid0. The Code is as follows:

Initialize FPU:

_ Global (_ partition) mfmsrr10orir11, R10, msr_fp/* write R10 | floating point enable */combine, R3, empty_zero_page @ HA/* defined in head_32.s */addir9, r9, empty_zero_page @/* load empty_zero_page to R9 */lrest_32fprs (0, R9)/* multiple LFD (floating point loading) commands */syncmtmsrr10isyncblr

When initializing FPU, it mainly depends on mtmsr and rest_32fprs. The former is to enable the MSR floating point position 1, and the latter is to initialize all FPR registers. The empty_zero_page variable is defined in head_32.s and is an empty element aligned by page. Rest_32fprs is defined in ppc_asm.h, as follows:

# Define rest_fpr (n, base) lfd n, thread_fpr0 + 8 * ts_fprwidth * (N) (base)

# Define rest_2fprs (n, base) rest_fpr (n, base); rest_fpr (n + 1, Base)

# Define rest_4fprs (n, base) rest_2fprs (n, base); rest_2fprs (n + 2, Base)

# Define rest_8fprs (n, base) rest_4fprs (n, base); rest_4fprs (n + 4, Base)

# Define rest_16fprs (n, base) rest_8fprs (n, base); rest_8fprs (n + 8, Base)

# Define rest_32fprs (n, base) rest_16fprs (n, base); rest_16fprs (n + 16, Base)

It is actually multiple LFD commands pushed by bit. thread_fpr0 in macro definition is defined in asm_offsets.c:

Define (thread_fpr0, offsetof (struct thread_struct, FPR [0]);

It means the offset of the array double FPR [32] [ts_fprwidth] In the thread_struct struct.

Configure hid0 again:

Setup_common_caches: mfsprr11, sprn_hid0/* hardware implementation register 0 */Andi. r0, R11, hid0_dce orir11, R11, hid0_ice | hid0_dce/* enable data cache and command cache */orir8, R11, hid0_icfi/* enable but invalid command cache */bne1f/* If not, jump to execute 1 * // * Don't invalidate the D-Cache */orir8, R8, hid0_dci/* unless it wasn' t enabled */1: syncmtsprsprn_hid0, R8/* enable and invalidate caches */syncmtsprsprn_hid0, R11/* enable caches */syncisyncblr

The specific configuration meaning varies with the CPU model. You can refer to the definition in Datasheet. The specific function is to enable the cache. If the data cache has not been enabled, the cache content needs to be cleared before enabling, so that the residual invalid data after the data cache is enabled will affect the data integrity. As for the instruction cache, it is cleared unconditionally (flush & invalidate ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.