Armlinux bootloader detailed

xiaoxiao2021-03-18  219

Online Bootloader article on Linux, but most of them are vivi, blob, etc., is not convenient to read, compiled files, and more guidance code for development, When the product is reduced, this has affected the development speed, which is relatively large for beginners, and analyzes a simple bootloader in the 2410 bootloader provided by Samsung, compiled. The file size does not exceed 4K, I hope to help everyone. 1. Several important concepts compressed kernel and decressed kernel compressed kernel, according to documentation, do not advocate using Decompressed Kernel, and use compressed kernel, it includes The decompression. Therefore, it is necessary to provide sufficient space when the RAM is assigned, so they do not cover each other. When the execution instruction jumps to the compressed kernel, the decapulver starts working, if the decompression detects the decompressed code You will override the compressed kernel, then you will send data directly to the compressed kernel, and reposition KERNEL, so if there is not enough space, it will be wrong. Jffs2 File System can save the data generated in the armlinux application on Flash, I The board has not been used yet. RamDisk uses ramdisk to make root file system boots without other devices. Generally there are two ways to load, I will introduce the most commonly used, put the compressed ramdisk image to the specified address, then By bootloader passes this address to kernel. Specific Code Analysis. Startup parameters (Excerpted from IBM Developer) 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 define in the include / asm / setup.h header file of the Linux kernel source code. In embedded Linux systems, usually need to be set by the bootloader settings: ATAG_CORE, ATAG_MEM, ATAG_CMDLINE, ATAG_RAMDISK, ATAG_INITRD, etc. .

(Note) The parameters can also be set with CommandLine. In my bootloader, I use it. 2. Development Environment and Development Board Configuration: CPU: S3C2410, Bank6 has 64M SDRAM (two), Bank0 There are 32m nor flash, the serial port is of course an escape. This way, according to the data manual, address assignment is as follows: 0x4000_0000 Starting with 4K on-chip DRAM. 0x0000_0000 Starting is 32M Flash 16bit Width 0x3000_0000 Starting is 64M SDRAM 32bit Width Note: Control register BANK6 and BANK7 portion must be the same. 0x4000_0000 (chip DRAM) stored within 4k BOOTLOADER IMAGE 0x3000_0100 stored starting startup parameters 0x3120_0000 store COMPRESSED KERNEL IMAGE 0x3200_0000 store COMPRESSED RAMDISK 0x3000_8000 designated dECOMPRESSED KERNEL IMAGE ADDRESS 0x3040_0000 designated dECOMPRESSED RAMDISK IMAGE ADDRESS Development Environment: Redhat Linux, Armgcc Toolchain, ARMLINUX KERNEL How to build ARMGCC Compilation Environment: It is recommended to use Toolchain, not to compile Armgcc, and do you have a failure. First download ARM-GCC 3.3.2 Toolchain ARM-Linux-GCC-3.3.2.tar.bz2 Decomposed to / Toolchain # tar jxvf arm-linux-gcc-3.3.2.tar.bz2 # mv /usr/local/Arm/3.3.2 / Toolchain in Makefile There is an incrude = -i ../include -i /root/my/USR /CLUDE -I /ROOT/MY/USR /CLUDE -I /ROOT/MY/USR /CLUDE-I / ROOT/MY/USR /CLUDE-I /ROOT/MY/USR/INCLUDE-I / ROOT/MY/USR /LOCAL/ARM/3.3.2/include. Otherwise, the library function cannot be used 3 Startup mode: You can start in Flash, or use the JTAG emulator. Due to the NOR FLASH, according to the 2410 manual, the 4K DRAM can be used directly without setting, and other memory must initialize first, such as telling Memory Controller, BA There are two SDRAMs in NK6, and the data width is 32bit, = =. Otherwise, Memory Control will process the memory in accordance with the default value after the reset. This read / write will generate an error. So the first step, put the execution code through the emulator 0x4000_0000, (When compiled, set text_base = 0x40000000) Step 2, put Linux Kernel Image in the Target Address (SDRAM) through AXD, waiting to call the third step, execute the bootloader code, get debug data from the serial port, boot Armlinux 4. Code analysis told the steps that executed more execution, I want everyone to have a probably impression of startup, then the code analysis of the bootloader internal code, there are a lot of online content, I am streamlined here, delete unnecessary Function. BootLoader is generally divided into two parts, assembly parts, and C language part. The assembly section performs simple hardware initialization, and the C part is responsible for copying data, setting startup parameters, serial communication and other functions. Bootloader lifecycle: 1. Initialize hardware, for example Set UART (at least one), detect memory = =. 2. Set the startup parameters, this is to tell the kernel hardware information, such as which boot interface, baud rate = =. 3. Jump to Linux Kernel's first address 4. Dividation, of course, in the guidance phase, like vivi, etc., use the virtual address, if you are more troublesome, you use the real address, all the same. Let's see the code: 2410init.s .Global _Start // Start executive _ START: / / The following is the interrupt vector b reset @ supervisor mode // Restart Jump ⋯⋯ ⋯⋯ RESET: LDR R0, =

WTCON / WTCON address is 53000000, Watchdog's control register * / ldr r1, = 0x0 / * Off WatchDog * / STR R1, [R0] LDR R1, = INTMSK LDR R1, = 0xffffffff / * Shield all interrupt * / STR R1, [r0] LDR R0, = INTSUBMSK LDR R1, = 0x3ff / * subinterpreted also * / STR R1, [R0] / * Initialize Ports ... for Display LED. * / LDR R0, = GPFCON LDR R1, = 0x55AA STR R1, [R0] LDR R0, = GPFUP LDR R1, = 0xFF STR R1, [R0] LDR R0, = GPFDAT LDR R1, = PowerOffled1 STR R1, [R0] / * Setup Clock Divider Control Register * You Must Configure CLKDIVN Before LockTime or MPLL UPLL * BECAUSE DEFAULT CLKDIVN 1,1,1 set the sdmram Timing conflictnop * fclk: hclk: pCLK = 1: 2: 4 in this case * / ldr r0, = CLKDIVN LDR R1, = 0x3 STR R1, [ R0] / * TO Reduce PLL LOCK TIME, Adjust The LockTime Register. * / LDR R0, = LockTime LDR R1, = 0xfffffff STR R1, [R0] / * Configure MPLL * / LDR R0, = MPLLCON LDR R1, = ((( M_MDIV << 12) (M_PDIV << 4) m_sdiv) // FIN = 12MHz, Fout = 203MHz STR R1, [R0] LDR R1, = Gstatus2 LDR R10, [R1] TST R10, # offrst Bne 1000F //// In this paragraph, I didn't move, I used Samsung. The following is the main change / * MEMORY C0NTROLLER (MC) setting * / add r0, pc, # mcdata - (. 8) // r0 point to McData Address, there is a MC initialization there to use The data LDR R1, = BWSCON // R1 points to the first address ADD R2, R0, # 52 // copy number of MC controller registers, offset 52 words 1: // Recycle LDR R3 according to the offset, [ R0], # 4 STR R3, [R1], # 4 CMP R2, R0 BNE 1b .align 2 McData: .word (0 (b1_bwscon << 4) (b2_bwscon << 8) (b3_bwscon << 12) (B4_bwscon << 16) (b5_bwscon << 20) (b6_bwscon << 28) (b7_bwscon << 28)) The above line is BWSCON's data, the specific parameters are as follows: You need to change settings DW6 and DW7 settings It is 10, ie 32bit, DW0 is set to 01, that is, 16bit is the controller data of each Bank, mostly the clock correlation, the default value, after setting the MC, jump to the part of the calling main function

.word ((b0_tacs << 13) (b0_tcos << 11) (b0_tacc << 8) (B0_TAH << 4) (B0_TACP << 2) (B0_PMC)). Word ((b1_tacs << 13) (b1_tcos << 11) (b1_tacc << 8) (B1_TAH << 4) (B1_TAH << 2) (b1_pmc)) .word. ((B2_TACS << 13) (B2_TCOS << 11) (B2_TACC << 8) (B2_TAH << 4) (B2_TAH << 2) (B2_TACP << 2) (B2_PMC)) .Word ( (B3_TACS << 13) (B3_TCOS << 11) (B3_TACC << 8) (B3_TCOH << 6) (B3_TAH << 2) (B3_TACP << 2) (B3_PMC)) .Word ((((( B4_TACS << 13) (B4_TCOS << 11) (B4_TACC << 8) (B4_TCOH << 4) (B4_TAH << 2) (B4_TACP << 2) (B4_PMC)) .Word ((B5_TACS) << 13) (B5_TCOS << 11) (B5_TACC << 8) (B5_TCOH << 6) (B5_TAH << 4) (B5_TACP << 2) (B5_PMC)) .Word ((b6_mt < <15) (b6_trcd << 2) (b6_scan)) .Word ((b7_mt << 15) (b7_trcd << 2) (b7_scan)) .Word ((Refen << 23) (TREFMD << 22) (TRP << 20) (TCHR << 18) (tchr << 16) Refcnt) .word 0xB2 / * Refresh Control Register * / .word 0x30 / * Banksize Register: BURST MODE * / .WORD 0x30 / * sdram mode register * / .align 2 .Global call_main // Call the main function, the function parameters are 0 call_main: LDR SP, Stack_Start MoV FP, # 0 / * No Previous Frame, SO FP = 0 * / MOV A1 , # 0 / * SET ARGC to 0 * / mov a2, # 0 / * set argv to NUL * / bl main / * call main * / STACK_START: .word STACK_BASE undefined_instruction: software_interrupt: prefetch_abort: data_abort: not_used: irq: fiq: / * These are the main The assembly section implements the clock setting, the serial port setting WatchDog is closed, the interrupt closing function (if necessary, you can use it), then transfer to main * /

2410init.c file int main (int Argc, char ** argv) {u32 test = 0; void (* thekernel) (int zero, int arch, unsigned long params_addr) = (void (*) (int, int, unsigned long " )) RAM_COMPRESSED_KERNEL _BASE; // the address of the compressed IMAGE int i, k = 0; // downPt = (RAM_COMPRESSED_KERNEL_BASE); chkBs = (_ RAM_STARTADDRESS); // where the SDRAM start // fromPt = (FLASH_LINUXKERNEL); MMU_EnableICache () ChangeClockdivider (1, 1); // 1: 2: 4 CHANGEMPLLVALUE (m_mdiv, m_pdiv, m_sdiv); // fin = 12MHz FCLK = 200MHz port_init (); // Setting I / O port, before using the COM port, This function must be called, otherwise the communication chip does not get the data uART_INIT (PCLK, 115200); // PCLK uses the default 200000, the call special rate 115200 / *************** ** (Check RAM Space) ******************* / UART_SENDSTRING ("NTLINUX S3C2410 NOR BootLoadern); UART_SENDSTRING (" Ntchecking SDRAM 2410Loader.c ... n " ); for (; chkbs <0x33fa0140; chkbs = chkbs 0x4, test ) /// /////> According to my experience, it is best to increase with one byte, and our board, it is no problem when 256byte is increasing. However, it is wrong with the increment of 1byte, and the 13th and the data line will take "1", and the test is hardware problem. The phenomenon is as follows // Using the simulation of the code to test SDRAM, starting with the 28f128a3j flash film, test The result is very good, but after the Flash film //, the test data (DATA) is written for the 0x00000400, and the memory space will be erroneous, // and random. The error data is always turned to 0x00002400, and the data bus is 10 and 13 bits without short circuit. Test with other data //, such as 0x00000200; 0x00000800 is not there. DX help. // Since it is not solved, I can't use Flash. {Chkpt1 = chkbs; * (u32 *) chkpt1 = test; // write data IF (* (U32 *) chkpt1 == 1024) // read data and write Is there? {Chkpt1 = 4; led_display (1); led_display (2); led_display (3); led_display (4);} else goto error;} uart_sendstring ("NTSDRAM Check Successful! NTMEMORY MAPING ...") get_memory_map (); // Get the available Memory information, make a list, which will be transmitted as a start-up parameter to kernel // so-called memory map refers to what address ranges in the 4GB physical address space are allocated to address the system RAM unit. UART_SENDSTRING ("NTMEMORY MAP SUCCESSFUL! N"); // I use the emulator to put the kernel, ramdisk directly on the SDRAM, so the following paragraph is unnecessary, but if kernel, ramdisk is in Flash, it needs.

/ *********************************************** / UART_SENDSTRING ("Tloading kernel Image from flash ... n "); UART_SENDSTRING (" TAND Copy Kernel Image to SDRAM AT 0x31000000N "); UART_SENDSTRING (" TTBY Leijun Dong Dongleijun4000@hotmail.com N "); for (k = 0; k <196608; k , Downpt = 1, FROMPT = 1) // 3 * 1024 * 1024 / 32Linux kernel des, src, length = 3m * (u32 *) DOWNPT = * (U32 *) frompt; / ******* ************ (loading ramdisk) ****************** / UART_SENDSTRING ("TTLoading Compressed Ramdisk ... N"); Downpt = (RAM_COMPRESSED_RAMDISK_BASE); home_ramdisk_base; for (k = 0; K <196608; K , Downpt = 1, frompt = 1) // 3 * 1024 * 1024 / 32linux kernel des, src, length = 3m * (U32 *) DOWNPT = * (U32 *) frompt; / ****** Jffs2 file system, if you do not use Flash in development, this paragraph can not ******** / uart_sendstring ( "TTLOADING JFFS2 ... N"); DOWNPT = (RAM_JFFS2); frompt = (flash_jffs2); for (k = 0; K <(1024 * 1024/32); k , Downpt = 1, frompt = 1) * (u32 *) DOWNPT = * (U32 *) frompt; uart_sendstring ("Load Success ... run ... n"); / ******************* (setup param) ********* ********** / setup_start_tag (); // Start setting startup parameter setup_memory_tags (); // Memory impression setup_commandline_tag ("console = ttys0, 115200n8); // Start command line setup_initrd2_tag (); / / root device setup_ramdisk_tag (); // ramdisk image setup_end_tag ​​(); / * Off I-Cache * / ASM ("MRC P15, 0,% 0, C1, C0, 0": "= R" (i)); I & = ~ 0x1000; ASM ("MCR P15, 0,% 0, C1, C0, 0":: "r" (i)); / * flush i-cache * / asm ("MCR P15, 0,% 0, C7, C5, 0 "::" R "(i));

// The following is jumped to the first address of Compressed Kernel Thekernel (0, Arch_Number, (Uns_Boot_Params)); // When starting KERNEL, i-cache can be opened, R0 must be 0, R1 Must be the CPU model (can be found from Linux / Arch / ARM / Tools / Mach-Types), R2 must be the physical start address of the parameter

/ ******************* * ******************* / Error: UART_SENDSTRING ("NNPANIC SDRAM CHECK ERROR ! n "); return 0;} static void setup_start_tag (void) {params = (struct tag *) RAM_BOOT_PARAMS; // Start the address started address 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);} static void setup_memory_tags (void) {INT i; 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] .len; params = tag_next (params);}}} static void setup_commandline_tag (char * commandline) {int i = 0; / * SKIP NON-EXISTENT Command Lines So The Kernel Will Still * Use its default command line. * / params-> hdr.tag = atag_cmdline; params-> hdr.size = 8; // console = TTYS0, 115200N8 STRCPY (params-> u.cmdline.cmdline, p); params = tag_next (params);} static void setup_initrd . 2_tag (void) {/ * an ATAG_INITRD node tells the kernel where the compressed * ramdisk can be found ATAG_RDIMG is a better name, actually * / params-> hdr.tag = ATAG_INITRD2;. Params-> hdr.size = tag_size ( tag_initrd); params-> u.initrd.start = RAM_COMPRESSED_RAMDISK_BASE; params-> u.initrd.size = 2047; // k byte params = tag_next (params);} static void setup_ramdisk_tag (void) {/ * an ATAG_RAMDISK node tells The Kernel How Large The * Decompressed Ramdisk Will Become. * / params-> HDr.tag = atag_ramdisk; params-> hdr.size = tag_size (tag_ramdisk); params-> u.ramdisk.Start =

RAM_DECOMPRESSED_RAMDISK_BASE; params-> u.ramdisk.size = 7.8 * 1024; // k byte params-> u.ramdisk.flags = 1; // automatically load ramdisk params = tag_next (params);} static void setup_end_tag ​​(void) { Params-> hdr.tag = atag_none; params-> hdr.size = 0;} Void UART_INIT (INT PCLK, INT BAUD) // Serial port is important {INT i; if (PCLK == 0) PCLK = PCLK; rUFCON0 = 0x0; // UART channel 0 FIFO control register, FIFO dISAble rUMCON0 = 0x0; // UART chaneel 0 MODEM control register, AFC dISAble // UART0 rULCON0 = 0x3; // Line control register: Normal, No parity, 1 stop , 8 bits, Samsung is not very wrong, but I am calculated according to Normal, No Parity, 1 Stop, 8 BITS is indeed 0x245 // [10] [9] [8] [7] [6] [5] [4] [3: 2] [1: 0] // Clock SEL, TX INT, RX INT, RX TIME OUT, RX ERR, LOOP-BACK, Send Break, Transmit Mode, Receive Mode // 0 1 0, 0 1 0 0, 01 01 // PCLK Level Pulse Disable Generate Normal Normal Interrupt or Polling RuCon0 = 0x245; // Control Register Rubrdiv0 = (PCLK / 16. / BAUD) -1); // BAUD Rate Divisior Register 0 delay (10);} After the above toss, next is the life of Kernel. Can you Can start KERNEL, you can see your level of Kernel.

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

New Post(0)