Boot loader concept

xiaoxiao2021-03-19  203

I. Introduction

Running in dedicated embedded boards

GNU / Linux

The system has become more and more popular. An embedded

Linux

The system can usually be divided into four levels from the perspective of software:

1.

Boot load program

. Including curing in firmware

(firmware)

middle

Boot

Code

(

Optional

)

,with

Boot loader

Two parts.

2. Linux

Kernel

. The custom kernel of the embedded plate and the startup parameters of the kernel.

3.

File system

. Including root file system and built

Flash

The file system above the memory device. Usually used

Ram Disk

Come as

root fs

.

4.

User application. User-specific applications. Sometimes there may be an embedded graphical user interface between the user application and the kernel layer.

Common embedded GUI

MicroWindows

MINIGUI

understand.

The boot loader is the first software code running after the system is powered up. Recall

PC

Architecture we can know,

PC

The boot loader in the machine is

BIOS

Its essence is a firmware program

)

And on the hard disk

MBR

middle

OS boot loadinger

(such as,

LILO

with

Grub

Wait a composition together.

BIOS

After completing hardware detection and resource allocation, the hard disk

MBR

middle

Boot loader

Readily

Ram

In, then hand over the control

OS boot loadinger

.

Boot loader

The main operating task is to read the kernel image from the hard disk.

Ram

In, then jump to the entry point of the kernel to run, that is, start the operating system. In the embedded system, it is usually not like

BIOS

Such a firmware program (note, some embedded

CPU

It will also embed a short start program), so the loading start task of the entire system is completely

Boot loader

To be done. For example, in one basis

ARM7TDMI CORE

In the embedded system, the system is usually from the address when power-on or reset.

0x00000000

Start execution, and it is usually the system at this address.

Boot loader

program.

This article will be from Boot Loader

Concept, boot loadinger

Main task, boot loader

Frame structure and boot loader

Four aspects of installations to discuss Boot Loader of embedded systems

.

two

Boot loader

the concept of

Simply put, boot loader

A small program running before the operating system kernel runs.

Through this small program, we can initialize the hardware device to create a mapping map of memory space, bringing the software hardware and software environment to a suitable state to prepare the correct environment for the final call operating system kernel. usually,

Boot loader

It is true that it is severely relying on hardware, especially in embedded world. Therefore, establish a general purpose in the embedded world

Boot loader

Almost impossible. Despite this, we can still

Boot loader

Some universal concepts are summarized to guide users specific

Boot loader

Design and implementation.

Boot loader

Supported CPU

Embedded board

Each different CPU

The architecture has different boot loaders

. Some boot loadinger

Also supports a variety of architectural CPUs

, Such as u-boot

Support ARM at the same time

Architecture and MIPS

Architecture.

In addition to

CPU

Outside the architecture,

Bootloader

In fact, it also depends on the configuration of the specific embedded plate-based device. That is to say, for two different embedded panels, even if they are based on the same

CPU

And build, you want to let the boot loader running on a plate

The program can also be run on another board, usually need to be modified

Boot loader

Source program.

2. Boot loader

Installation Media (Installation Medium

)

After power is powered or reset, all CPUs

Usually from a CPU

The manufacturer is pre-arranged on the address. For example, based on ARM7TDMI CORE

CPU

Usually from the address 0x00000000 during reset

Take its first instruction. Based on CPU

The built-in embedded system usually has some type of solid state storage device (

For example: ROM

, EEPROM

Or Flash

Wait)

Mapped to this pre-scheduled address. Therefore, after the system is powered up, CPU

Boot loader will be executed first

program.

3.

Used to control boot loader

Equipment or mechanism

The host and target machine are generally established through the serial port, boot loader

Software is usually performed by serial port when executing I / O

For example: output print information to the serial port, read user control characters from the serial port.

4. Boot loadinger

Startup process

The boot loader's startup process is a single stage or a multi-stage usually multi-stage Boot Loader to provide more complex features, and better portability. Most boot loaders starting from solid state storage devices are 2-stage startup process, i.e., the startup process can be divided into two parts and STAGE 2. As for which tasks are done in STAGE 1 and STAGE 2, it will be discussed below. 5. Operation Mode Mode Most Boot Loader contains two different operating modes: "Launch Load" mode and "Download" mode, this difference is only for developers. However, from the perspective of end users, the role of Boot Loader is to load the operating system, and there is no distinction between the so-called start-load mode and the download mode. Boot loading mode: This mode is also called "autonomous) mode. That is, Boot Loader loads the operating system into the RAM from a certain solid storage device on the target, and the entire process does not intervene. This mode is the normal working mode of Boot Loader, so the boot loader must work in this mode when the embedded product is released. Download (Downloading) Mode: In this mode, Boot Loader on the target will download files from the host (Host) through communication means such as serial connections or network connectivity, such as downloading kernel images and root file system images. The files downloaded from the host typically be saved in the RAM of the target machine by Boot Loader, and then written by Boot Loader to the Flash Category Solid Storage Device on the Tall. This mode of Boot Loader is usually used when the first installation kernel is used when the root file system is installed; in addition, the future system update will use the work mode of Boot Loader. Boot Loader working in this mode usually provides a simple command line interface to its end users. Strong Boot Loader like blob or u-boot usually supports both working modes simultaneously, and allows users to switch between the two working modes. For example, Blob is in normal startup mode at startup, but it will delay 10 seconds waiting for the end user to press any key to switch BLOB to download mode. If there is no user button in 10 seconds, the blob continues to start the Linux kernel. 6. The most common case of communication devices and protocols used between BootLoader and the host is that Boot Loader on the target machine is transmitted through the serial port and the host, the transfer protocol is usually one of the XMODEM / YMODEM / ZMODEM protocol. . However, the speed of serial port transmission is limited, so it is a better selection through the Ethernet connection and downloading the file with the TFTP protocol. In addition, the software used by the host is also considered in the theory and this topic. For example, when downloading the file by an Ethernet connection and a TFTP protocol, the host must have a software to provide TFTP services. After discussing the above concept of Bootloader, let's take a look at what tasks should be done by Bootloader. III. Boot loader's main task and typical structure framework first we have assumed it before, first we do it, that is: assume that the kernel image and root file system image are loaded into the RAM.

The reason why such a hypothesis is that the kernel image and root file system image can be run directly in a solid storage device such as a ROM or Flash in a solid state storage device such as a ROM or FLASH in a solid state storage device such as a ROM or Flash. But this approach is undoubtedly at the expense of running speed. From the perspective of the operating system, the total goal of Boot Loader is to correctly call the kernel. In addition, due to the implementation of Boot Loader depends on the architecture of the CPU, most Boot Loader is divided into two parts: STAGE1 and STAGE2. Depending on the code of the CPU architecture, such as equipment initialization code, etc., usually in Stage1, and usually use assembly language to achieve short escape purposes. Stage2 is usually implemented in a C language, which can be implemented for complex functions, and the code will have better readability and portability. Boot Loader's Stage1 typically includes the following steps (in the order of execution): Hardware devices initialize. Prepare the RAM space for the STAGE2 of the Boot Loader. Copy Boot Loader's Stage2 to RAM Space. Set up a stack. Jump to the C entry point of Stage2. The STAGE2 of Boot Loader typically includes the following steps (in the order of execution): The hardware device to be used initialized this phase. Detect system memory mapping. Read the kernel image and root file system image from the Flash to the RAM space. Set the startup parameters for the kernel. Call the kernel. 3.1 boot loading stage13.1.1

Basic hardware initialization This is the operation performed at the beginning of Boot Loader, with the purpose of the execution of Stage2 and the subsequent Kernel's execution to prepare some basic hardware environments. It typically includes the following steps (in the order of execution): 1. Shield all interrupts. To interrupt service is usually the responsibility of the OS device driver, so there is no need to respond to any interrupt in the full process of bootloader. Interrupt masking can be done by writing a CPU's interrupt shield register or status register (such as ARM's CPSR register). 2. Set the speed and clock frequency of the CPU. 3. RAM initialization. The function register and each of the memory control registers, including the correct setting of the system. 4. Initialize the LED. Typically, the LEDs are driven by GPIO, which indicates that the state of the system is OK or Error. If there is no LED on the board, you can also print the LOGO character information of the Boot Loader to the serial port by initializing the UART. 5. Turn off CPU internal instruction / data Cache. 3.1.2 To load the STAGE2 Prepare RAM Space To obtain a faster execution speed, the STAGE2 is usually loaded into the RAM space, so it is necessary to prepare a STAGE2 that is loaded with the boot loader. Since STAGE2 is usually a C language execution code, in consideration of space size, in addition to the STAGE2 executable image, the stack space must also be considered. In addition, the space size is the multiple of the MEMORY PAGE (usually 4KB). In general,

1M

The RAM space is already enough. The specific address range can arrange any arrangement, such as BLOB, schedule its STAGE2 executable image to starting from the system RAM start address 0xC0200000

1M

Perform in space. However, the STAGE2 is arranged to the top 1MB of the entire RAM space (ie, Ramend-1MB) - RAMEND is a recommended method. For the later narrative convenience, the size of the arranged RAM spatial range is here: Stage2_size (byte), separate the start address and termination address as: Stage2_start and Stage2_end (these two addresses are 4-byte boundary Align). So: STAGE2_END = Stage2_Start Stage2_size In addition, it is necessary to ensure that the range of address ranges is indeed readable RAM space, so you must test the address range you arrange. The specific test method can use a method similar to blob, i.e., as the MEMORY PAGE is tested, and the two words starting every MEMORY PAGE are readable. For the convenience of the following description, we remember this detection algorithm as: test_mempage, the specific steps are as follows: 1. First save the contents of the MEMORY PAGE. 2. Write any numbers to these two words. For example: writing 0x55 to the first word, 0xAA is written in the second word. 3. Then, immediately read the contents of the two words immediately. Obviously, what we read should be 0x55 and 0xAA, respectively. If not, the address range occupied by this Memory Page is not a valid RAM space. 4. Write any numbers in these two words. For example, write 0xAA to the first word, and write 0x55.5 in the second word. Then, immediately read the contents of the two words immediately. Obviously, what we read should be 0xAA and 0x55, respectively. If not, the address range occupied by this Memory Page is not a valid RAM space. 6. Restore the original content of these two words. The test is completed. In order to get a clean RAM spatial range, we can also clear the arranged RAM spatial range. 3.1.3 Copying STAGE2 to RAM When copying to copy the two points: (1) The executable image of the STAGE2 is stored at the start address and termination address of the solid state storage device; (2) the start address of the RAM space. 3.1.4 Setting the Stack Pointer SP Stack The settings are prepared to perform C language code. Usually we can set the value of the SP to (STAGE2_END-4), that is, the 1MB RAM space arranged at 3.1.2 (the stack is growing down). In addition, the LED light can also be turned off before setting the stack pointer SP to prompt users to jump to Stage2. After the above execution steps, the physical memory layout of the system should be shown in Figure 2 below. 3.1.5 Jump to the C entry point of STAGE2 After all of the above is ready, you can jump to the Stage2 of Boot Loader. For example, in the ARM system, this can be implemented by modifying the PC register as the appropriate address. 3.2 STAGE2 of Boot Loader As mentioned earlier, the code of Stage2 usually implements in C language to facilitate more complex features and better code readability and portability. But in different ways with ordinary C language applications, we cannot use any support functions in the GLIBC library when compiling and linking Boot Loader. The reason is obvious. This brings us a question, which is where you jump into the main () function? Directly put the origin of the main () function as the entry point of the entire Stage2 execution image may be the most direct idea.

But there are two shortcomings in this way: 1) Unable to pass the main () function transfer function parameter; 2) The case where the main () function returns cannot be processed. A more clever way is to use the concept of trampoline (spring bed). That is, write a TRAMPOLINE applet with assembly language and use this Trampoline applet to perform entry points for the STAGE2 executable image. Then we can jump into the main () function in the Trampoline assembly appler; and when the main () function returns, the CPU execution path obviously returns to our Trampoline program. In short, this method is to use this TRAMPOLINE applet as an External Wrapper of the main () function. The following is given a simple trampoline program example (from blob) :. text .globl _trampoline _trampoline: bl main / * if main EVER RETURNS We just call it aga * / b _trpoline It can be seen that when the main () function returns, We re-execute the Trampoline program with a jump command - of course, the main () function is also re-executed, which is the meaning of the word trampoline. 3.2.1 Hardware devices to be used in the initialization stage This usually includes: (1) initializing at least one serial port, so that the terminal user performs I / O output information; (2) initialization timer, etc. Before initializing these devices, you can also turn the LED light to indicate that we have entered the main () function. After the device is initialized, some print information, program name string, version number, etc. can be output. 3.2.2 Memory Map Memory Map The memory mapping refers to what address ranges are allocated in the entire 4GB physical address space to address the RAM unit of the address. For example, in SA-1100 CPU, 512M starting from 0xC000, 0000

The address space is used as the system's RAM address space, while in Samsung S

3C

44B0X CPU, from 0x

0C

0000 to 0x1000,000 to 0x1000,000 to 0x1000,000

64M

The address space is used as the system's RAM address space. Although the CPU typically reserves a large number of sufficient address spaces to the system RAM, it does not necessarily implement all RAM address spaces reserved in the CPU when building a specific embedded system. That is, the specific embedded system often maps a portion of the entire RAM address space reserved in the CPU to the RAM unit, and the remaining portion of the RAM address space is in an unused state. Due to the above fact, Boot Loader's Stage2 must detect the memory mapping of the entire system before it wants to do anything (for example, reading the kernel image stored in the Flash to the RAM space) before detecting the memory mapping of the entire system before you have detected the memory mapping of the entire system before it must know the CPU pre- Which of the RAM address spaces remains true to the RAM address unit, which are in the "unused" state. (1) Description of memory maps can be used as the following data structure to describe a continuous address range in the RAM address space: typedef struct memory_area_struct {u32 start; / * the base address of the memory region * / u32 size; / * The byte number of the memory;} Memory_Area_t; the continuous address range in this RAM address space can be one of two states: (1) Used = 1, then the continuous address range has been It is achieved, i.e., is truly mapped to the RAM unit. (2) Used = 0, the continuous address range is not implemented by the system, but is in an unused state. Based on the above MEMORY_AREA_T data structure, the entire CPU reserved RAM address space can be represented by an array of MEMORY_AREA_T types, as shown below: Memory_Area_t memory_map [Num_Mem_areas] = {[0 ... (Num_Mem_areas - 1)] = {.start = 0, .size = 0, .USED = 0},}; (2) Detection of memory maps Under We give a simple and efficient algorithm that can be used to detect memory mappings of the entire RAM address space: / * Array initialization * / for (i = 0; i

* / Call the algorithm in Section 3.1.2 Test_MemPage (); if (Current Memory Page ISNOT a Valid Ram Page) {/ * no ram here * / if (Memory_Map [i] .used) i ; Continue;} / * * The current page is already a valid address range that is mapped to the RAM. * / if (* (u32 *) addr! = 0) {/ * alias? * / / * This memory page is an alias * / if (Memory_MAP [i] .used) i ; Continue;} / * * The current page is already a valid address range * that is mapped to the RAM and it is not an alias of an address page in the 4GB address space. * / if (Memory_MAP [i] .used == 0) {MEMORY_MAP [i] .Start = addr; memory_map [i] .size = Page_Size; Memory_MAP [i] .used = 1;} else {Memory_MAP [i]. Size = page_size;}} / * end of for for (...) * / After using the above algorithm, the Boot Loader can also print more details of the memory map to the serial port. 3.2.3 Loading the kernel image and root file system image (1) Planning memory usage The layout here includes two aspects: (1) The memory range occupied by the kernel image; (2) The range of memory occupied by the root file system. When planning memory, it is mainly considered two aspects of the size of the base address and the image. For kernel images, it is generally copied to approximately 1m from (MEM_START 0x8000).

Within B size memory (the kernel of embedded Linux generally does not operate 1MB). Why is it to empty the memory from MEM_START to MEM_START 0x8000? This is because Linux kernels have placed some global data structures in this memory, such as starting parameters and kernel page tables. For root file system images, it is generally copied to the MEM_START 0x0010, 0000. If RAMDisk is used as a root file system image, the size after decompression is generally

1M

B. (2) Copy from Flash Since the embedded CPU like ARM is usually to address Flash and other solid storage devices in a unified memory address space, read data from the ram unit and read data from the RAM unit. no difference. With a simple loop, you can complete the work from the flash device: while (count) {* dest = * src ; / * They area all aligned with word boundary * / count - = 4; / * byte number * / }; 3.2.4 Setting the kernel's startup parameters should be said that after copying the kernel image and root file system image to the RAM space, you can prepare to start the Linux kernel. But before calling the kernel, you should have a step preparation, namely: Set the startup parameters of the Linux kernel. The kernel after Linux 2.4.x expects to pass the startup parameters in the form of tagged list. Start the parameter tag list to mark Atag_core to mark the Atag_none end. Each tag is composed of a Tag_Header structure that is identified, and subsequent parameter values ​​data structures. Data Structure Tag and Tag_ Header Include / ASM / SETUP.H header file defined in Linux kernel source: / * The list ends with an atag_none node. * / #Define atag_none 0x00000000 Struct Tag_Header {u32 size; / * Note, here size is the number of words in units * / u32 tag;}; ...... struct tag {struct tag_header hdr; union {struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; / * * Acorn specific * / struct tag_acorn acorn; / * * DC21285 specific * / struct tag_memclk memclk;} u;}; in the embedded Linux system, usually set by the Boot Loader The common startup parameters are: ATAG_CORE, ATAG_MEM, ATAG_CMDLINE, ATAG_RAMDISK, ATAG_INITRD, and more. For example, the code sets ATAG_CORE is as follows: params = (struct tag *) Boot_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); where boot_params represents the starting base address of the kernel launch parameter in memory, pointer params is a struct tag Type pointer. Macro tag_next () will point to the current tag of the pointer as a parameter, calculate the starting address of the next tag that is currently marked.

Note that the device ID of the root file system of the kernel is set here. Below is an example code for setting memory mapping: for (i = 0; i hdr.tag = atag_mem; params-> hdr.size = tag_size (tag_mem32); params-> u.mem.start = memory_map [i] .start; params-> u.Mem.size = memory_map [i] .size; params = tag_next (params);}} It can be seen that Memory_map [] Amor group, each valid memory segment corresponds to an ATAG_MEM parameter tag. The Linux kernel can receive information in the form of command line parameters at startup, using this, we can provide the kernel to provide those kernels that cannot be detected by themselves, or the override core you detect. For example, we use such a command line parameter string "console = TTYS0, 115200N8" to notify the core as the console in ttys0, and the serial port is set with "115200bps, no parity, 8-bit data bit". Below is an example code for setting up the kernel command line parameter string: char * p; / * Eat Leading white space * / for (p = commandline; * p == '; p ); / * Skip non-eviStent 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); Please note that in the above code, set the Tag_Header's larger hours must include The terminator '/ 0' of the string, and the number of bytes is rounded up to 4 bytes, as the SIZE member in the Tag_Header represents the number of words.

Below is an example code for setting ATAG_Initrd, which tells the kernel where you can find the initrd image (compressed format) and its size: params-> hdr.tag = ATAG_Initrd2; params-> hdr.size = tag_size (tag_initrd ); params-> u.initrd.start = ramdisk_ram_base; params-> u.initrd.size = init_len; params = tag_next (params); below is an example code for setting ATAG_RAMDISK, how big is it in Ramdisk after decompression? Is KB): params-> hdr.tag = atag_ramdisk; params-> hdr.size = tag_size (tag_ramdisk); params-> u.ramdisk.start = 0; params-> u.ramdisk.size = ramdisk_size; / * Please Note that the unit is KB * / params-> u.ramdisk.flags = 1; / * AutomaticLoad RamDisk * / params = tag_next (params); Finally, set the ATAG_NONE tag to end the entire startup parameter list: static void setup_end_tag ​​(void) {params-> hdr.tag = ATAG_NONE; params-> hdr.size = 0;} 3.2.5 Calling the kernel Boot loader Method for calling the Linux kernel is to directly jump to the first instruction of the kernel, that is, directly jump Go to the MEM_START 0x8000 address. At the time of jump, the following conditions should be satisfied: 1. Setting: r0 = 0; R1 = Machine Type ID; About Machine Type Number, see Linux / Arch / ARM / Tools / Mach-Types. R2 = Starting parameter marker list starts in the RAM; 2. CPU mode: Interrupt (IRQS and FIQS) must be disabled; the CPU must be SVC mode; 3. Cache and MMU settings: MMU must be turned off; instruction cache can be turned off; data Cache must be turned off; if you use C language, you can call the kernel as the following sample code: void (* thekernel) (int Zero, int Arch, U32 params_addr) = (void (*) (int, int, u32)) kernel_ram_base; ... Thekernel (0, Arch_Number, (U32) kernel_pamas_start); Note that thekernel () function call should never return. If this call returns, the error is explained. IV. About the design and implementation of the serial terminal in the Boot Loader program, there is nothing to be more excited than the printing information from the serial port terminal. In addition, printing information to the serial terminal is also a very important and effective debugging means. However, we often touch the serial terminal to display garbled or have no problems at all. There are two main reason for this problem: (1) Boot loader is incorrect to the initialization setting of the serial port.

(2) The terminal emulation program running at the Host end is incorrect to the serial port, including: baud rate, parity, data bit, and stop bit settings. In addition, sometimes this problem will be encountered, that is: In the operation of the Boot Loader, we can correctly output information to the serial terminal, but when the Boot Loader starts the kernel, it is not possible to see the launch output information of the kernel. The reason for this problem can be considered from the following aspects: (1) First confirm that your kernel is configured with support for serial terminals when compiling and configures the correct serial driver. (2) Your Boot Loader's initialization settings for serial port may be inconsistent with the initial setting of the serial port. In addition, for such as S3C

44B0X, the CPU, the CPU clock frequency setting also affects the serial port, so if the Boot Loader and the kernel are inconsistent with its CPU clock frequency, the serial terminal will not display information correctly. (3) Finally, it is also necessary to confirm that the kernel base address used by Boot Loader must be consistent with the running base address used in compilation, especially for Uclinux. Suppose your kernel image is using the base address for compiling is 0xc0008000, but your Boot Loader loads it to the 0xC0010000, then the kernel image must not be executed correctly. 5. Conclusion Boot Loader design and implementation is a very complex process. If you can't receive the exciting "uncompressing linux .................. Done, booting the kernel ...", I can't say anyone. " Hi, my boot loader has been successfully turned! ".

转载请注明原文地址:https://www.9cbs.com/read-130041.html

New Post(0)