Wednesday 21 June 2017

how do I find which interrupt handler to use for shared interrupt line ?

The kernel will sequentially invoke all the handlers for that particular shared line.

Exactly. Say Dev1 and Dev2 shares the IRQ10. When the interrupt is generated, ISRs registered with this line will be invoked one by one.

In our scnario, say Dev2 is generating interrupt. If Dev1's ISR is registered first, then that ISR only called first. In that ISR, interrupt status register will be verified for interrupt. If no interrupt bit is set then we can confirm that interrupt is not Dev2's. so next ISR(i.e Dev1's ISR) will be called.

see the return values IRQ_NONE/IRQ_HANDLED for more information.

How does the handler know that the corresponding device issued the interrupt or not ?

By reading the Interrupt status register only.

Is this information relayed through the interrupt controller that is between the devices and the processor interrupt line ??

Im not sure about this. But the OS will take care of calling ISRs based on the return values from ISR.

  1. Interrupt vectors can be shared between multiple devices. By returning IRQ_NONE/IRQ_HANDLED, an interrupt handler can indicate that the interrupt was/was not from the device it is specifically interested in. If IRQ_NONE is returned, the next handler in the list should be called.
  2. Even if the IRQ is not shared, an interrupt handler can indicate to the interrupt subsystem that there were problems handling the interrupt and that it should be disabled to prevent system hangs from an irq loop

Tuesday 20 June 2017

how u-boot works



We will look at boot process of linux kernel on dragonboard410c system-on-chip, built around the ARMv7 ARM Thumb processor. Kwick byte builds an embedded board called dragonboard410c based on dragonboard410c. We will take this board as an example and see how Linux boots up on this board.



You also need to read ARM Architecture Reference Manual for better understanding the boot process.You can download it with the following link.

http://www.lysator.liu.se/~kjell-e/embedded/ARM-ARM.pdf


U-boot boot Process:

Linux boot sequence involves execution of the following components.

                Boot-loader initialization
                Kernel initialization
                User space initialization

Boot-loader:

A boot-loader is a small program which will load the kernel image into RAM and boots up the kernel image. This is also called bootstrap as it brings(pulls) up system by loading an operating system.  Boot-loader starts before any other software starts and initializes the processor and makes CPU ready to execute a program like an operating system. Most processors have a default address from which the first bytes of code are fetched upon power is applied or board is reset. Hardware designers use this information to store the boot-loader code at that address in ROM or flash. Since it should initialize the cpu and should run a program which is located at architecture specific address boot-loaders are highly processor specific and board specific.  Every embedded board comes with a bootstrap to download the kernel image or standalone application into the board and start executing the kernel image or application.  Boot-loader will be executed when power is applied to a processor board. Basically it will have some minimal features to load the image and boot it up.

It is also possible to control the system using a hardware debug interface such as J TAG. This interface may be used to write the boot loader program into boo-table non-volatile memory (e.g. flash) by instructing the processor core to perform the necessary actions to program non-volatile memory.  Generally done for first time to download the basic boot-loader and for some recovery process. J TAG is a standard and popular interface provided by many board vendors. Some micro-controllers provide special hardware interfaces which can’t be used to take arbitrary control of  a system or directly run code, but instead they allow the insertion of boot code into boot-able non-volatile memory (like flash memory) via simple protocols. Then at the manufacturing phase, such interfaces are used to inject boot code (and possibly other code) into non-volatile memory. After system reset, the micro-controller begins to execute code programmed into its non-volatile memory, just like usual processors are using ROM’s for booting. In many cases such interfaces are implemented by hardwired logic. In other cases such interfaces could be created by software running in integrated on-chip boot ROM from GPIO pins.

There are some other third party boot-loaders available which provide rich set of features and easy user interface. You can download these third party boot-loaders into board and can make them default boot-loaders for your board. Generally boot-loaders provided by board vendors are replaced with these third party boot-loader.  There are a quite few third party boot-loader available and some of them are open source (or free boot-loaders) and some are commercial. Some of them are Das U-Boot, Red boot, GRUB (for desktops),   LILO , Loadlin, , bootsect-loader, SYSLINUX,  EtherBoot, ELILO.

We will look at U-boot boot-loader . U-boot is the widely used boot-loader in embedded systems. I will explain code from the u-boot-2017.01  source. You can download U-boot from the following site.

http://www.denx.de/wiki/U-Boot

How U-boot is built:

————————-

Based on the configuration of U-boot, all the assembly files (.S) and C files (.c) are compiled using  cross compiler which is built for a particular architecture and object files(.o) will be generated. All these object files are linked by linker and an executable file will be created. An object file or executable file is a collection of sections like .text, .data, .bss etc.  Object files and executable files have a file format like elf.  All the sections of the object files will be arranged in the executable file based on a script called linker script. This script tells where all the sections are to be loaded in the memory when it runs. Understanding this script is very important to know how boot-loader and kernel are composed and how different sections of boot-loader or kernel are loaded in the memory.

Generally,  when a program is run (executed) a loader reads executable file and loads different sections of the executable file in the specified memory location and starts executing the start function(entry point) specified in the linker script. But, if you want to run(load) a boot-loader there will not be any loader to load(basically to understand the file format) different sections of executable file into the memory.  Then you need to use a tool called objcopy which will take all sections from the executable file and create a binary file which doesn’t have any file format. This binary file can be loaded into the memory and executed or can be written in to the ROM at a particular address (specific to the architecture) which will be executed by CPU when power is applied to the board. You can find good tutorial on linker script in the following location.



file board\qualcomm\dragonboard410c\u-boot.lds

11 OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
 12 OUTPUT_ARCH(aarch64)
 13 ENTRY(_arm64_header)
 14 SECTIONS
 15 {
 16         . = 0x00000000;
 17
 18         . = ALIGN(8);
 19         .text :
 20         {
 21                 *(.__image_copy_start)
 22                 board/qualcomm/dragonboard410c/head.o (.text*)
 23                 CPUDIR/start.o (.text*)
 24                 *(.text*)
 25         }
 26
 27         . = ALIGN(8);
 28         .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
 29
 30         . = ALIGN(8);
 31         .data : {
 32                 *(.data*)
 33         }
 34
 35         . = ALIGN(8);
 36
 37         . = .;
 38
 39         . = ALIGN(8);
 40         .u_boot_list : {
 41                 KEEP(*(SORT(.u_boot_list*)));
 42         }
 43
 44         . = ALIGN(8);
 45
 46         .image_copy_end :
 47         {
 48                 *(.__image_copy_end)
 49         }
 50
 51         . = ALIGN(8);
 52
 53         .rel_dyn_start :
 54         {
 55                 *(.__rel_dyn_start)
 56         }
 57
 58         .rela.dyn : {
 59                 *(.rela*)
 60         }
 61
 62         .rel_dyn_end :
  63         {
 64                 *(.__rel_dyn_end)
 65         }
 66
 67         _end = .;
 68
 69         . = ALIGN(8);
 70
 71         .bss_start : {
 72                 KEEP(*(.__bss_start));
 73         }
 74
 75         .bss : {
 76                 *(.bss*)
 77                  . = ALIGN(8);
 78         }
 79
 80         .bss_end : {
 81                 KEEP(*(.__bss_end));
 82         }
 83
 84         /DISCARD/ : { *(.dynsym) }
 85         /DISCARD/ : { *(.dynstr*) }
 86         /DISCARD/ : { *(.dynamic*) }
 87         /DISCARD/ : { *(.plt*) }
 88         /DISCARD/ : { *(.interp*) }
 89         /DISCARD/ : { *(.gnu*) }
 90 }

 OUTPUT_FORMAT in line #11 specify the file format of the executable file. Here the executable   file format is elf64 and endianness is little endian.  OUTPUT_ARCH in line # 12 specify the architecture on which this code runs. ENTRY in line #22 specifies the start function(entry point) of  u-boot program.  Here the entry point is _start. SECTIONS in line #24 defines how different sections are mapped in the executable file. Loader uses the addresses specified in this section to load different section of the program into the memory.  ‘.’ in the line #16 specifies the start address where the following sections should be loaded. In this case start address is 0x00000000. After this in line #18 the memory is aligned by 4 bytes and the .text section follows in the line #19.

19         .text :
 20         {
 21                 *(.__image_copy_start)
 22                 board/qualcomm/dragonboard410c/head.o (.text*)
 23                 CPUDIR/start.o (.text*)
 24                 *(.text*)
 25         }
At the ‘.’ position (0x00000000) the code in board/qualcomm/dragonboard410c/head.o is mapped and follows the code that is there in .text sections of all other object (.o) files. /arm/cpu/armv7/ start.o contains the _start() function(in assembly language) which is entry point of this program.

Now the ‘.’ will be at 0x00000000 + sizeof (.text).  Again memory is aligned by 8 bytes and .rodata section follows in line #27.
27         . = ALIGN(8);
 28         .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

.rodata sections from all objects files are mapped at this address. Follows the .data and  .git sections.

30         . = ALIGN(8);
 31         .data : {
 32                 *(.data*)
 33         }
 34
 35         . = ALIGN(8);
Each U-boot command is an object of type ‘cmd_tbl_t’ which contains command name, help string  and function pointer to be executed when this command is run. All these command objects are placed in the memory sequentilly. Each of this command object is built into an U-boot defined section called  .u_boot_cmd in the object file.  These all .u_boot_cmd  sections are placed in the memory after the above sections(.data and .git).
36
 37         . = .;
 38
 39         . = ALIGN(8);
 40         .u_boot_list : {
 41                 KEEP(*(SORT(.u_boot_list*)));
 42         }
 43

.u_boot_list contains the start of the commands objects and keep on add to the command objects. And next follows the .bss (uninitialized global variables) sections.
60         . = ALIGN(4);
61         __bss_start = .;
62         .bss (NOLOAD) : { *(.bss) . = ALIGN(4); }
63         _end = .;

__bss_start points to the .bss start address and _end contains the end of the all sections.
Using this linker script linker will generate an executable file called u-boot. Objcopy tool is used to generate a binary file from the u-boot executable file

u-boot.bin:   u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@

U-boot binary file will be  copied to the board RAM or written in the flash disk. dragonboard410c board comes with a boot programmer  A tiny program called qualcomm bootstrap that can be used to download the image into flash or RAM and start execute or to reset a corrupted board. U-boot will be copied to flash disk or internal RAM( if it is less size) and will be downloaded to RAM and will be executed when the board power is applied to the board. For this board(dragonboard410c) the code is always downloaded from device address 0x8000_0000 to the address 0x8000_0000 of the SRAM after remap. That ‘s why we have given the start address of the .text section as 0x80000000.  If you want to load the code any where in the RAM and want to execute U-boot you need to build you code as position independent code(PIC).   Then the instructions addresses will be offset into to PC(cpu register) value. So the downloaded code must be position-independent or linked at address 0x8000_0000. For our explanation purpose assume that U-boot code is linked at 0x80000000 and the Boot program downloaded U-boot from the data flash(Check dragonboard410c spec for downloading process))  and call entry point into the U-boot.

U-boot Execution:

As specified in the linker script U-boot starting function (entry point) will be _start():\board\qualcomm\dragonboard410c\head.S

File: \board\qualcomm\dragonboard410c\head.S

_start is written in assembly language. First instuction executed by _start() is a call to start_code: \board\qualcomm\dragonboard410c\head.S.
.globl _start

    _start: b       start_code

start_code: Will perform the following tasks.

1) Set the cpu in supervisor mode. The current operating processor status is in the Current Program Status Register (CPSR). The CPSR holds:

• four ALU flags (Negative, Zero, Carry, and Overflow),

• two interrupt disable bits (one for each type of interrupt (FIQ and IRQ),

• one bit to indicate ARM or Thumb execution

• five bits to encode the current processor mode

Check ARM processor data sheet for more details on the CPSR register.
     
file \arch\arm\cpu\armv7\start.S

 32         .globl  reset
 33         .globl  save_boot_params_ret
 34 #ifdef CONFIG_ARMV7_LPAE
 35         .global switch_to_hypervisor_ret
 36 #endif
 37
 38 reset:
 39         /* Allow the board to save important registers */
 40         b       save_boot_params
 41 save_boot_params_ret:
 42 #ifdef CONFIG_ARMV7_LPAE
 43 /*
 44  * check for Hypervisor support
 45  */
 46         mrc     p15, 0, r0, c0, c1, 1           @ read ID_PFR1
 47         and     r0, r0, #CPUID_ARM_VIRT_MASK    @ mask virtualization bits
 48         cmp     r0, #(1 << CPUID_ARM_VIRT_SHIFT)
 49         beq     switch_to_hypervisor
 50 switch_to_hypervisor_ret:
 51 #endif
 52         /*
 53          * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
 54          * except if in HYP mode already
 55          */
 56         mrs     r0, cpsr
 57         and     r1, r0, #0x1f           @ mask mode bits
 58         teq     r1, #0x1a               @ test for HYP mode
 59         bicne   r0, r0, #0x1f           @ clear all mode bits
 60         orrne   r0, r0, #0x13           @ set SVC mode
 61         orr     r0, r0, #0xc0           @ disable FIQ and IRQ
 62         msr     cpsr,r0
 63
 64 /*
 65  * Setup vector:
 66  * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 67  * Continue to use ROM code vector only in OMAP4 spl)
 68  */
 69 #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
 70         /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
 71         mrc     p15, 0, r0, c1, c0, 0   @ Read CP15 SCTLR Register
 72         bic     r0, #CR_V               @ V = 0
 73         mcr     p15, 0, r0, c1, c0, 0   @ Write CP15 SCTLR Register
 74
 75         /* Set vector address in CP15 VBAR register */
 76         ldr     r0, =_start
 77         mcr     p15, 0, r0, c12, c0, 0  @Set VBAR
 78 #endif
 79
 80         /* the mask ROM code should have PLL and others stable */
 81 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
 82         bl      cpu_init_cp15
 83 #ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
 84         bl      cpu_init_crit
 85 #endif
 86 #endif
 87
 88         bl      _main

 1. whenever power on,reset function called from specific location
 2. Disable FIQ and IRQ
 3. call the cpu_init_cp15
 4. call _main
file : \arch\arm\lib\crt0.S

 67 ENTRY(_main)
 68
 69 /*
 70  * Set up initial C runtime environment and call board_init_f(0).
 71  */
 72
 73 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
 74         ldr     sp, =(CONFIG_SPL_STACK)
 75 #else
 76         ldr     sp, =(CONFIG_SYS_INIT_SP_ADDR)
 77 #endif
 78 #if defined(CONFIG_CPU_V7M)     /* v7M forbids using SP as BIC destination */
 79         mov     r3, sp
 80         bic     r3, r3, #7
 81         mov     sp, r3
 82 #else
 83         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */
 84 #endif
 85         mov     r0, sp
 86         bl      board_init_f_alloc_reserve
 87         mov     sp, r0
 88         /* set up gd here, outside any C code */
 89         mov     r9, r0
 90         bl      board_init_f_init_reserve
 91
 92         mov     r0, #0
 93         bl      board_init_f
 94
 95 #if ! defined(CONFIG_SPL_BUILD)
 96
 97 /*
 98  * Set up intermediate environment (new sp and gd) and call
 99  * relocate_code(addr_moni). Trick here is that we'll return
100  * 'here' but relocated.
101  */
102
103         ldr     sp, [r9, #GD_START_ADDR_SP]     /* sp = gd->start_addr_sp */
104 #if defined(CONFIG_CPU_V7M)     /* v7M forbids using SP as BIC destination */
105         mov     r3, sp
106         bic     r3, r3, #7
107         mov     sp, r3
108 #else
109         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */
110 #endif
111         ldr     r9, [r9, #GD_BD]                /* r9 = gd->bd */
112         sub     r9, r9, #GD_SIZE                /* new GD is below bd */
113
114         adr     lr, here
115         ldr     r0, [r9, #GD_RELOC_OFF]         /* r0 = gd->reloc_off */
116         add     lr, lr, r0
117 #if defined(CONFIG_CPU_V7M)
118         orr     lr, #1                          /* As required by Thumb-only */
119 #endif
120         ldr     r0, [r9, #GD_RELOCADDR]         /* r0 = gd->relocaddr */
121         b       relocate_code
121         b       relocate_code
122 here:
123 /*
124  * now relocate vectors
125  */
126
127         bl      relocate_vectors
128
129 /* Set up final (full) environment */
130
131         bl      c_runtime_cpu_setup     /* we still call old routine here */
132 #endif
133 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
134 # ifdef CONFIG_SPL_BUILD
135         /* Use a DRAM stack for the rest of SPL, if requested */
136         bl      spl_relocate_stack_gd
137         cmp     r0, #0
138         movne   sp, r0
139         movne   r9, r0
140 # endif
141         ldr     r0, =__bss_start        /* this is auto-relocated! */
142
143 #ifdef CONFIG_USE_ARCH_MEMSET
144         ldr     r3, =__bss_end          /* this is auto-relocated! */
145         mov     r1, #0x00000000         /* prepare zero to clear BSS */
146
147         subs    r2, r3, r0              /* r2 = memset len */
148         bl      memset
149 #else
150         ldr     r1, =__bss_end          /* this is auto-relocated! */
151         mov     r2, #0x00000000         /* prepare zero to clear BSS */
152
153 clbss_l:cmp     r0, r1                  /* while not at end of BSS */
154 #if defined(CONFIG_CPU_V7M)
155         itt     lo
156 #endif
157         strlo   r2, [r0]                /* clear 32-bit BSS word */
158         addlo   r0, r0, #4              /* move to next */
159         blo     clbss_l
160 #endif
161
162 #if ! defined(CONFIG_SPL_BUILD)
163         bl coloured_LED_init
164         bl red_led_on
165 #endif
166         /* call board_init_r(gd_t *id, ulong dest_addr) */
167         mov     r0, r9                  /* gd_t */
168         ldr     r1, [r9, #GD_RELOCADDR] /* dest_addr */
169         /* call board_init_r */
170 #if defined(CONFIG_SYS_THUMB_BUILD)
171         ldr     lr, =board_init_r       /* this is auto-relocated! */
172         bx      lr
173 #else
174         ldr     pc, =board_init_r       /* this is auto-relocated! */
175 #endif
176         /* we should not return here. */
177 #endif
178
179 ENDPROC(_main)


   This file handles the target-independent stages of the U-Boot
   start-up where a C runtime environment is needed. Its entry point
   is _main and is branched into from the target's start.S file.

  _main execution sequence is:

  1. Set up initial environment for calling board_init_f().
     This environment only provides a stack and a place to store
     the GD ('global data') structure, both located in some readily
     available RAM (SRAM, locked cache...). In this context, VARIABLE
     global data, initialized or not (BSS), are UNAVAILABLE; only
     CONSTANT initialized data are available. GD should be zeroed
     before board_init_f() is called.

  2. Call board_init_f(). This function prepares the hardware for
     execution from system RAM (DRAM, DDR...) As system RAM may not
     be available yet, , board_init_f() must use the current GD to
     store any data which must be passed on to later stages. These
     data include the relocation destination, the future stack, and
     the future GD location.

  3. Set up intermediate environment where the stack and GD are the
     ones allocated by board_init_f() in system RAM, but BSS and
     initialized non-const data are still not available.

  4a.For U-Boot proper (not SPL), call relocate_code(). This function
     relocates U-Boot from its current location into the relocation
     destination computed by board_init_f().

  4b.For SPL, board_init_f() just returns (to crt0). There is no
     code relocation in SPL.

  5. Set up final environment for calling board_init_r(). This
     environment has BSS (initialized to 0), initialized non-const
     data (initialized to their intended value), and stack in system
     RAM (for SPL moving the stack and GD into RAM is optional - see
     CONFIG_SPL_STACK_R). GD has retained values set by board_init_f().

  6. For U-Boot proper (not SPL), some CPUs have some work left to do
     at this point regarding memory, so call c_runtime_cpu_setup.

  7. Branch to board_init_r().


174         ldr     pc, =board_init_r

file : commomn\board_r.c

1. call  board_init_r function

void board_init_r(void):


1)  Call a set of functions which initialize all subsystems.
if (initcall_run_list(init_sequence_r))
        hang();
file lib\initcall.c
2. it call initcall_run_list

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        unsigned long reloc_ofs = 0;
        ret = (*init_fnc_ptr)();
      
        }
init_sequence is an array of function pointers defined in commomn\board_r.c. The above loop takes each function pointer and calls it.

The following are some of important functions called.

174         ldr     pc, =board_init_r 

file : commomn\board_r.c

1. call  board_init_r function

void board_init_r(void):


1)  Call a set of functions which initialize all subsystems.
if (initcall_run_list(init_sequence_r))
        hang();
file lib\initcall.c
2. it call initcall_run_list

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        unsigned long reloc_ofs = 0;
        ret = (*init_fnc_ptr)();
       
        }
init_sequence is an array of function pointers defined in commomn\board_r.c. The above loop takes each function pointer and calls it.

The following are some of important functions called.

init_sequence_f_r is the list of init functions which are run when
  U-Boot is executing from Flash with a semi-limited 'C' environment.
  The following limitations must be considered when implementing an
  '_f_r' function:
   - 'static' variables are read-only
   - Global Data (gd->xxx) is read/write

  The '_f_r' sequence must, as a minimum, copy U-Boot to RAM (if
  supported).  It _should_, if possible, copy global data to RAM and
  initialise the CPU caches (to speed up the relocation process)
 
 board_init():
int board_init (void)

    {
    return 0;
 }
This is board specific function and should definitely be defined by each board. This function should some board specific initialization if there are any.
When you are porting u-boot to a new board you must define this function.
This function just sets its board number and tells where the boot params for Linux are stored.


 This is a common function called to setup the serial port. This function internally calls cpu or board specific serial_init() function
int serial_init (void)        
    {     
                if (!(gd->flags & GD_FLG_RELOC) || !serial_current) {
                        struct serial_device *dev = default_serial_console ();

                        return dev->init ();
                }

                return serial_current->init ();
    }

   
3.timer_init():

This is a cpu specific function and each cpu code must define it. It should basically initilize the timer services in the cpu.  timer_init() for  AT91RM9200 cpu defined in /arch/arm/cpu/armv7/arch_timer.c.

GD_FLG_RELOC will be set when the u-boot is relocated to RAM. serial_current will point to an object of type struct serial_device of current serial device that we are using. As we have not yet initialized any serial device serial_current will be NULL. default_serial_console() is a function which points to __default_serial_console() defined in common/serial.c.
            console_init_f,         /* stage 1 init of console */

display_banner,         /* say that we are here */

    dram_init,              /* configure available RAM banks */

       Based on configurations the following functions are also called.
     #if defined(CONFIG_ARCH_CPU_INIT)

           arch_cpu_init,          /* basic arch cpu dependent setup */

     #endif

   board_init,             /* basic board dependent setup */

     #if defined(CONFIG_USE_IRQ)

           interrupt_init,         /* set up exceptions */

     #endif

     #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

                  init_func_i2c,

     #endif

4) Initialize NAND if configured.
#if defined(CONFIG_CMD_NAND)

        puts ("NAND:  ");

        nand_init();            /* go init the NAND */

#endif

nand_init() function is defined in drivers/mtd/nand/nand.c

5) Initialize if dataflash is configured
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();

        dataflash_print_info();

#endif

6) Get all  input and output devices list.
stdio_init ();  /* get the devices list */
stdio_init() is defined in common/stdio.c.

7) Call console_init_r () to setup the console info like where the input(stdin) should be taken and where the output(stdout) to be returned.

console_init_r ();      /* fully init console as a device */

8)  Enable the interrupts

enable_interrupts ();

enable_interrupt() for arm boards is defined in lib_arm/interrupts.c. Writing 0x80 into cpsr register will enable the interrupts. enable_interrupts() is the following assembly code.
__asm__ __volatile__("mrs %0, cpsr\n"

                             "bic %0, %0, #0x80\n"

                             "msr cpsr_c, %0"

                             : "=r" (temp)

                             :

                             : "memory");

9) Get the ‘loadaddr’ environment variable defined in u-boot through setenv. Kernel image is loaded at the load address.
        /* Initialize from environment */

if ((s = getenv ("loadaddr")) != NULL) {

                load_addr = simple_strtoul (s, NULL, 16);

}

10) All initialization has been done, now enter a main loop where we accept commands from the user and execute them.
/* run_main_loop() can return to retry autoboot, if so just run it again. */


11) the run_main_loop call ,can return to retry autoboot, if so just run it again.
      for (;;)
        main_loop();

        main_loop() function is defined in common/main.c. This function is common for all boards

When the U-boot is booting up it will take a default command that is given in the U-boot enviroment variable bootcmd and executes that command.
If this variable is not defined U-boot will provide U-boot prompt where user can enter the commands.
U-boot can be made to go to its  boot prompt even if the bootcmd is defined by giving some bootdelay.
U-boot waits until the bootdelay expires or user presses any key. If user does not press any key U-boot will execute the default command defined in bootcmd else U-boot goes to its prompt.

/* We come here after U-Boot is initialised and ready to process commands */

void main_loop(void)
{
    const char *s;

    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifdef CONFIG_VERSION_VARIABLE
    setenv("ver", version_string);  /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */

    cli_init();

    run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
    update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */

    s = bootdelay_process();
    if (cli_process_fdt(&s))
        cli_secure_boot_cmd(s);

    autoboot_command(s);

    cli_loop();
    panic("No CLI available");
}

CONFIG_BOOTDELAY is the number of seconds the u-boot should wait for user interrupt before it takes its default action.

As we have explained each U-boot command is an object of type struct cmd_tbl_s. The command name and function to be executed will be stored in this structure. All commands structures are kept in memory at perticular memroy,  __u_boot_cmd_start and __u_boot_cmd_end contain start and end address of this memory section called command table.

run_command() function takes the command name and finds the data structure belonging to this command in the command table and calls the corresponding command function. Lets assume that bootcmd  is not configured and U-boot provided the prompt to enter commands. And also assume that the linux image is loaded into the ram in a perticular address using tftpboot command or using some ymodem command and bootm command is given at the U-boot prompt.

u-boot# bootm 0x80008000

The function do_bootm() that is responsible for executing bootm command. bootm  loads kernel image.
kernel image (zImage) decompression
arch/arm/boot/compressed/head.S: start (108)
    First code executed, jumped to by the bootloader, at label "start" (108)
    save contents of registers r1 and r2 in r7 and r8 to save off architecture ID and atags pointer passed in by bootloader (118)
    execute arch-specific code (inserted at 146)
        arch/arm/boot/compressed/head-xscale.S or other arch-specific code file
        added to build in arch/arm/boot/compressed/Makefile
        linked into head.S by linker section declaration:  .section “start”
        flush cache, turn off cache and MMU
    load registers with stored parameters (152)
        sp = stack pointer for decompression code (152)
        r4 = zreladdr = kernel entry point physical address
    check if running at link address, and fix up global offset table if not (196)
    zero decompression bss (205)
    call cache_on to turn on cache (218)
        defined at arch/arm/boot/compressed/head.S (320)
        call call_cache_fn to turn on cache as appropriate for processor variant
            defined at arch/arm/boot/compressed/head.S (505)
            walk through proc_types list (530) until find corresponding processor
            call cache-on function in list item corresponding to processor (511)
                for ARMv5tej core, cache_on function is __armv4_mmu_cache_on (417)
                    call setup_mmu to set up initial page tables since MMU must be on for cache to be on (419)
                    turn on cache and MMU (426)
    check to make sure won't overwrite image during decompression; assume not for this trace (232)
    call decompress_kernel to decompress kernel to RAM (277)
    branch to call_kernel (278)
        call cache_clean_flush to flush cache contents to RAM (484)
        call cache_off to turn cache off as expected by kernel initialization routines (485)
        jump to start of kernel in RAM (489)
            jump to address in r4 = zreladdr from previous load
                zreladdr = ZRELADDR = zreladdr-y
                zreladdr-y specified in arch/arm/mach-vx115/Makefile.boot
ARM-specific kernel code
arch/arm/kernel/head.S: stext (72)
    call __lookup_processor_type (76)
        defined in arch/arm/kernel/head-common.S (146)
        search list of supported processor types __proc_info_begin (176)
            kernel may be built to support more than one processor type
            list of proc_info_list structs
                defined in arch/arm/mm/proc-arm926.S (467) and other corresponding proc-*.S files
                linked into list by section declaration:  .section ".proc.info.init"
        return pointer to proc_info_list struct corresponding to processor if found, or loop in error if not
    call __lookup_machine_type (79)
        defined in arch/arm/kernel/head-common.S (194)
        search list of supported machines (boards)
            kernel may be built to support more than one board
            list of machine_desc structs
                machine_desc struct for boards defined in board-specific file vx115_vep.c
                linked into list by section declaration that's part of MACHINE_DESC macro
        return pointer to machine_desc struct corresponding to machine (board)
    call __create_page_tables to set up initial MMU tables (82)
    set lr to __enable_mmu, r13 to address of __switch_data (91, 93)
        lr and r13 used for jumps after the following calls
        __switch_data defined in arch/arm/kernel/head-common.S (15)
    call the __cpu_flush function pointer in the previously returned proc_info_list struct (94)
        offset is #PROCINFO_INITFUNC into struct
        this function is __armv7_setup for the ARM 926EJ-S, defined in arch/arm/mm/proc-armv7.S (392)
            initialize caches, writebuffer
            jump to lr, previously set to address of __enable_mmu
    __enable_mmu (147)
        set page table pointer (TTB) in MMU hardware so it knows where to start page-table walks (167)
        enable MMU so running with virtual addresses (185)
        jump to r13, previously set to address of __switch_data, whose first field is address of __mmap_switched
            __switch_data defined in arch/arm/kernel/head-common.S (15)
arch/arm/kernel/head-common.S: __mmap_switched (35)
    copy data segment to RAM (39)
    zero BSS (45)
    branch to start_kernel (55)
Processor-independent kernel code

init/main.c: start_kernel (456)