【系統移植】uboot詳細分析

uboot使用

    uboot控制檯,倒計時
    命令: 調試,操做一些硬件linux

setenv printenv saveenv 
 nand erase 
 nand write 
 tftp 20008000 zImage
 help : uboot能夠提供哪些命令
 setenv == set == sete == seten

    環境變量: 爲命令提供參數
     serverip : tftp命令提供tftp服務器的地址
     ipaddr : tftp命令提供tftp客戶端(開發板)的地址shell

兩個環境變量

    uboot: 下載內核,並啓動內核
    bootcmd倒計時結束後,uboot應該自動作什麼事情vim

 set bootcmd tftp 20008000 zImage \; bootm 20008000
 set serverip 192. 168. 7. 2
 set ipaddr 192. 168. 7. 6
 set ethaddr 00 : 22 : 23 : 24 : 25 :ee

   倒計時結束的時候,uboot會執行bootcmd中的內容:
   tftp 20008000 zImage ; bootm 20008000api

   從tftp服務器(serverip)中將zImage文件(/tftpboot/)下載到開發板(ipaddr)中內存的20008000服務器

   set bootcmd tftp 20008000 zImage ; bootm 20008000網絡

  bootargs: 負責告訴內核文件系統在哪裏(uboot傳遞給內核, 內核要用)架構

  set bootargs init = /linuxrc console =ttySAC0, 115200 root = /dev /nfs nfsroot = 192. 168. 7. 2 : /opt /filesystem ip = 192. 168. 7. 6
  root =xxxx : 根文件系統目錄在哪裏
   /dev /nfs : 根文件系統目錄在網絡的遠端
  
  nfsroot =xxxx : 根文件系統目錄在哪臺機器的哪一個文件路徑
  nfsroot = 192. 168. 7. 2 : /opt /filesystem
  ip = 192. 168. 7. 6 : 系統登陸的時候,靜態分配一個ip

  若是root = /dev /nfs
  root = /dev /nfs + nfsroot =xxxx +ip =xx
  
  若是root = /dev /mtdblock2(文件系統製做的時候會講)
   root = /dev /mtdblock2 + rootfstype =cramfs
  
   console =ttySAC0, 115200 : 內核啓動過程當中,調試信息往哪裏輸出,printk
   init = /linuxrc : 指定第一個init進程的可執行代碼文件
   /opt /filesystem == > host : /etc /exports
   sudo vim /etc/exports
   /opt /filesystem          *(subtree_check,rw,no_root_squash,async)
   /opt /fs100 /rootfs                *(subtree_check,rw,no_root_squash,async)

 

啓動內核:go/bootm

官方的ubootapp

zImage
   : go
  set bootcmd tftp 20008000 zImage \; go 20008000
 
 uImage 
   : bootm(下載地址,不能是 20008000)
  set bootcmd tftp 20800000 uImage \; bootm 20800000
 
  下載地址的選用 :
  go == > 能夠是任何地址
  bootm == > 20008000 +zImage的大小以上 == > 20800000 
  綜合用法:
  set bootcmd tftp 20800000 zImage \; go 20800000
  set bootcmd tftp 20800000 uImage \; bootm 20800000
uboot1. 3. 4 :
 zImage /uImage == >bootm
 set bootcmd tftp 20800000 uImage \; bootm 20800000

 

uboot的鏈接腳本

    所在路徑:cpu/arm_cortexa8/u-boot.ldsasync

OUTPUT_FORMAT( "elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start) // 入口函數
SECTIONS
{
        . = 0x00000000;   // 當前的起始位置0x0
        . = ALIGN( 4);
        .text(目標文件)    :
        { 
                cpu /arm_cortexa8 /start.o        (.text) // 第一個文件的.text
                 *(.text)
        }
        . = ALIGN( 4); // 當前位置四字節對齊
        .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata *))) }
        . = ALIGN( 4);
        .data : { *(.data) }
        . = ALIGN( 4);
        .got : { *(.got) }
        __u_boot_cmd_start = .;   // 用__u_boot_cmd_start記錄當前的位置, 代碼會用到,全局的
        .u_boot_cmd : { *(.u_boot_cmd) } // 段數據
        __u_boot_cmd_end = .; // 結束位置
        . = ALIGN( 4);
        __bss_start = .;
        .bss : { *(.bss) }
        _end = .;
}

鏈接的基地址:
     -Ttext 0x34800000==>board/samsung/smdkc100/config.mk
     TEXT_BASE = 0x34800000
     1,TEXT_BASE指定uboot的鏈接的起始位置
     2,指定uboot重定位的位置(能夠改爲0x2ff00000)
ide

 

uboot配置的詳細說明

 

make smdkc100_config
vim Makefile
unconfig :
        @ rm -f $(obj)include /config.h $(obj)include /config.mk \
                $(obj)board / * /config.tmp $(obj)board / * / * /config.tmp \
                $(obj)include /autoconf.mk $(obj)include /autoconf.mk.dep
MKCONFIG         : = $(SRCTREE) /mkconfig == . /mkconfig  shell腳本(可執行程序)

smdkc100_config :        unconfig
        @$(MKCONFIG) $(@ :_config =) arm arm_cortexa8 smdkc100 samsung s5pc1xx
 . /mkconfig  smdkc100 arm arm_cortexa8 smdkc100 samsung s5pc1xx
  執行一個腳本: 傳遞了6個參數(控制源碼的編譯)
  arm : 架構 == > lib_arm
  smdkc100 : include /configs /smdkc100.h   / / 開發板全部的宏的配置
  arm_cortexa8 : arm名 == > cpu /arm_cortexa8
  smdkc100 samsung : 開發板名 == > board /samsung /smdkc100
  s5pc1xx :cpu == >cpu /arm_cortexa8 /s5pc1xx

$(@ :_config =) : $@ :_config = == >smdkc100_config :_config =   / / _config替換成空,去掉
  $(@ :_config =xxx) == = >smdkc100xxx

 

uboot第一階段啓動流程

 

1,創建異常向量表 :
_start : b reset
 ldr pc, _undefined_instruction
 ldr pc, _software_interrupt
 ldr pc, _prefetch_abort
 ldr pc, _data_abort
 ldr pc, _not_used
 ldr pc, _irq
 ldr pc, _fiq

2,
reset :
  /*
  * set the cpu to SVC32 mode, disable F, I
  */

 mrs r0, cpsr
 bic r0, r0, #0x1f
 orr r0, r0, #0xd3
 msr cpsr,r0

  bl cpu_init_crit
    |
    /*
    * Invalidate L1 I/D
    */

   mov r0, # 0   @ set up for MCR
   mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
   mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
    /*
    * disable MMU stuff and caches
    */

   mrc p15, 0, r0, c1, c0, 0
   bic r0, r0, #0x00002000 @ clear bits 13 ( --V -)
   bic r0, r0, #0x00000007 @ clear bits 2 : 0 ( -CAM)
   orr r0, r0, #0x00000002 @ set bit 1 ( --A -) Align
   orr r0, r0, #0x00000800 @ set bit 12 (Z -- -) BTB
   mcr p15, 0, r0, c1, c0, 0

    bl lowlevel_init   //lowlevel_init.S (board\samsung\smdkc100):lowlevel_init:
      |
      /* Disable Watchdog */
     ldr r0, =S5PC100_WATCHDOG_BASE  @0xEA200000
     orr r0, r0, #0x0
     str r5, [r0]
      /* setting SRAM */
     ldr r0, =S5PC100_SROMC_BASE
     ldr r1, =0x9
     str r1, [r0]

      /* S5PC100 has 3 groups of interrupt sources */
     ldr r0, =S5PC100_VIC0_BASE   @0xE4000000
     ldr r1, =S5PC100_VIC1_BASE   @0xE4000000
     ldr r2, =S5PC100_VIC2_BASE   @0xE4000000
      /* Disable all interrupts (VIC0, VIC1 and VIC2) */
     mvn r3, #0x0
     str r3, [r0, #0x14]    @INTENCLEAR
     str r3, [r1, #0x14]    @INTENCLEAR
     str r3, [r2, #0x14]    @INTENCLEAR
      /* Set all interrupts as IRQ */
     str r5, [r0, #0xc]    @INTSELECT
     str r5, [r1, #0xc]    @INTSELECT
     str r5, [r2, #0xc]    @INTSELECT
      /* Pending Interrupt Clear */
     str r5, [r0, #0xf00]   @INTADDRESS
     str r5, [r1, #0xf00]   @INTADDRESS
     str r5, [r2, #0xf00]   @INTADDRESS
     bl uart_asm_init // 只是設置了gpio的功能,波特率的設置在第二階段
     # if 1   // 改動的部分
      /* init system clock */
      bl system_clock_init // 基本上沒太大問題
      bl mem_ctrl_asm_init
      //mem_setup.S board\samsung\Smdkc100 
      // 內存的初始化比較複雜, 原廠會提供(1.3.4)
      // 向FAE要
      // 這部分代碼運行有問題
      1,mem_ctrl_asm_init
      2,mem_setup.S須要被編譯 < == =board\samsung\Smdkc100\Makefile
      3,內存初始化代碼應該在前 16k (反彙編)
      修改cpu /arm_cotexa8 /u -boot.lds
棧的初始化:
  /* Set up the stack */
stack_setup:
 ldr r0, _TEXT_BASE  @ upper 128 KiB : relocated uboot
 sub r0, r0, #CONFIG_SYS_MALLOC_LEN @ malloc area
 sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE @ bdinfo
# ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)
# endif
 sub sp, r0, # 12  @ leave 3 words for abort -stack
 and sp, sp, # ~ 7  @ 8 byte alinged for (ldr /str)d

uboot代碼的自我拷貝:
  /* nand src offset : 0x0*/
 mov r0, #0x0   
  /* ddr dst addr : 0x2ff00000*/
 ldr r1, =0x2ff00000
  /*size*/
 ldr r2, =0x40000
 bl copy2ddr

ddr的地址(重定位的目標地址) : 和uboot的連接的基地址要同樣
board /samsung /smdkc100 /config.mk
TEXT_BASE =xxxx
清bss端 
/* Clear BSS (if any). Is below tx (watch load addr - need space) */
clear_bss:
 ldr r0, _bss_start  @ find start of bss segment
 ldr r1, _bss_end  @ stop here
 mov r2, #0x00000000  @ clear value
clbss_l:
 str r2, [r0]  @ clear BSS location
 cmp r0, r1   @ are we at the end yet
 add r0, r0, #4  @ increment clear index pointer
 bne clbss_l   @ keep clearing till at end
跳轉到c階段
 ldr pc, _start_armboot @ jump to C code
_start_armboot: .word start_armboot

_start_armboot: .word start_armboot
    //start_armboot它的值是在編譯的時候就已經肯定:0x2ff00000+offset==> 0x2ff00980

arm: 基本全部的指令都是位置無關(指令在哪裏執行均可以)
    有些代碼是位置有關: ldr pc, _start_armboot  (pc跳轉的目標地址_start_armboot(0x2ff00980),和特定的位置相關)
    ldr自己這條指令是位置無關,整個ldr pc, _start_armboot==>成爲一個位置相關的指令

連接地址: 連接器爲全部的指令作的排序, 確定有有個基地址: 基地址+該指令的偏移量
運行地址: 指令實際加載的地址,運行時,指令存放地址
物理地址: 和硬件相關,數據手冊中的地址都是物理地址, 硬件工程師爲設備設定的值
虛擬地址: 通常和mmu相關

思路:


    1,支持一種啓動模式nand啓動
         a,  時鐘和內存的初始化
              1,mem_setup.S 被編譯
         b, 完成自拷貝的實現
              nand_ops.c(讀操做)
              nand(0x0) --> ddr(TEXT_BASE)
             board/samsung/smdkc100/config.mk
         c,第一階段的代碼必須所有在前16k
              u-boot.lds
         d, 熟悉一下第一階段的啓動流程代碼

 

 


 

uboot第二階段代碼

lib_arm/Board.c
     void start_armboot (void)

爲何老是去看smkdc100.h

# include <common.h >
  |
 # include <config.h >
   |
  # define CONFIG_BOARDDIR board /samsung /smdkc100
  # include <config_defaults.h >
  # include <configs /smdkc100.h >
  # include < asm /config.h >

先看主線流程

// 設置gd指針指向特定位置
 gd = (gd_t *)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
  // gd指針指向的空間,清零
 memset (( void *)gd, 0, sizeof (gd_t));
 gd - >bd = (bd_t *)(( char *)gd - sizeof(bd_t));
 memset (gd - >bd, 0, sizeof (bd_t));
  // 初始化序列
  for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
   if (( *init_fnc_ptr)() != 0) {
   hang ();
  }
 }
 
  // 堆的初始化
  /* armboot_start is defined in the board-specific linker script */
 mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
   CONFIG_SYS_MALLOC_LEN);
  
 
 # if defined(CONFIG_CMD_NAND)   // CONFIG_CMD_NAND沒有定義
  puts ( "NAND:  ");
  nand_init();   /* go init the NAND */
 # endif
  // 環境變量的重定位
 env_relocate ();
  // 串口的初始化
 serial_initialize();

  // 無需關心
  /* IP Address */
 gd - >bd - >bi_ip_addr = getenv_IPaddr ( "ipaddr");
 stdio_init (); /* get the devices list going. */
 jumptable_init ();
 console_init_r ();
  // 中斷的使能
  /* enable exceptions */
 enable_interrupts ();

  /* Initialize from environment */
  if ((s = getenv ( "loadaddr")) != NULL) {
  load_addr = simple_strtoul (s, NULL, 16);
 }
  if ((s = getenv ( "bootfile")) != NULL) {
  copy_filename (BootFile, s, sizeof (BootFile));
 }
  // 網卡的初始化
 eth_initialize(gd - >bd);
  //死循環
  for (;;) {
  main_loop ();
 }

模塊的方式

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
   if (( *init_fnc_ptr)() != 0) {
   hang ();
  }
 }
 arch_cpu_init,  
 board_init,   // smdkc100開發板的總體的初始化
 timer_init, // 定時器的初始化, timer4==>倒計時的間隔時間,產生一個10ms的間隔
 env_init, // 環境變量的初步初始化  /* initialize environment */
 init_baudrate, // 波特率的設置 /* initialze baudrate settings */
 serial_init, // 串口的初始化  /* serial communications setup */
  // 分水嶺, 纔可以使用printf去打印調試信息
 console_init_f,   /* stage 1 init of console */
 display_banner,   /* say that we are here */
 print_cpuinfo,   /* display cpu info (and speed) */
 checkboard,   /* display board info */
 dram_init,   /* configure available RAM banks */
 display_dram_config

環境變量處理:

COBJS -$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
 環境變量的保存到哪一個地方 : CONFIG_ENV_IS_IN_NAND
 env_init
   |
  gd - >env_addr   = (ulong) &default_environment[ 0]; //gd->env_addr指向默認的環境變量 
  gd - >env_valid = 1;
 env_relocate ();
   | //分配空間,128k
  env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);
  env_relocate_spec ();
     | // 從nand中0x40000讀取數據到malloc區域
   ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
    if (ret) //若是讀取失敗,就使用默認的環境變量
     return use_default();
    //讀取數據成功,此時還要crc校驗一下,
    if (crc32( 0, env_ptr - >data, ENV_SIZE) != env_ptr - >crc)
     //若是校驗失敗,仍然使用默認的環境變量
     return use_default();
      |
     puts ( "*** Warning - bad CRC or NAND, using default environment\n\n");
     memset(env_ptr, 0, sizeof(env_t));
      // 使用默認的環境變量
     memcpy(env_ptr - >data, default_environment, sizeof(default_environment));

//CONFIG_ENV_OFFSET是能夠控制env保存到nand中特定的位置
// 0x40000
readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);
  | //                      0x40000  塊大小 , malloc區域  
 char_ptr = &buf[amount_loaded]; 
 nand_read( &nand_info[ 0], offset, &len, char_ptr)
   | // mtd的架構
  info - >read(info, ofs, *len, (size_t *)len, buf); 
  
本身設定環境變量 : smdkc100.h
# define CONFIG_SERVERIP    192. 168. 7. 2
# define CONFIG_IPADDR    192. 168. 7. 6
# define CONFIG_ETHADDR  00 : 23 : 24 : 25 : 26 : 27
# define CONFIG_BOOTCOMMAND "tftp 20800000 zImage35 \; go 20800000"
# define CONFIG_BOOTARGS "root=/de/nfs nfsroot=192.168.7.2:/opt/filesystem  ip=192.168.7.6 console=ttySAC0,115200 init=/linuxrc"

 

插曲:函數指針

1,聲明定義
  int * fun( int a, int b);
  int ( *fun)( int a, int b);
2,初始化
  int add( int a, int b)
 {
   return a +b;
 }
  int sub( int a, int b)
 {
   return a -b;
 }
  //fun = add;
 fun = sub;
3,調用
 fun( 3, 4);
4,做用
   a,產生api
   b,用於抽象分層
   struct stud{
  int age;
  int ( *func)( int a, int b);
  }

app
查詢 :  a的名字
 printf( "age = %d\n", p - >age);
 p - >func( 3, 4);
== == == == == == == == == == == == == == == == == == == == == == == =
(核心層)鏈表 :
  struct stud *p; (全局)
 p   = 鏈表頭;
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
(特定數據層) struct stud a;
 a.age = 30000;
 a.func = add;
struct stud b;
 b.age = 3;
 b.func = sub;

nandflash的初始化;

void start_armboot ( void)
  |
  nand_init();
   |Nand.c drivers\mtd\Nand 2677 2010 - 4 - 1
  nand_init_chip( &nand_info[i], &nand_chip[i], base_address[i]);
    |( struct mtd_info *mtd, struct nand_chip *nand,ulong base_addr)
   mtd - >priv = nand;
    board_nand_init(nand) // 初始化nand_chip對象
     |
    nand - >IO_ADDR_R   = ( void __iomem *)NFDATA;
    nand - >IO_ADDR_W   = ( void __iomem *)NFDATA;
    nand - >cmd_ctrl   = s3c_nand_hwcontrol;
    nand - >dev_ready   = s3c_nand_device_ready;
    nand - >select_chip = s3c_nand_select_chip;
    nand - >options   = 0;
    nand_scan(mtd, maxchips) // 初始化struct nand_info
 readenv
   |
 nand_read(
  info - >read()   // 誰給這個read函數指針初始化
  
  - >read = xxx
初始化部分:
nand_scan
  |
  nand_scan_tail(mtd);
   | //在這個地方給初始化了
  mtd - >read = nand_read; // Nand_base.c drivers\mtd\Nand 81953 2010-4-1
     |
     struct nand_chip *chip = mtd - >priv;
     nand_do_read_ops(mtd, from, &chip - >ops);
      |
     chip - >select_chip(mtd, chipnr); //select_chip在哪裏初始化

nand_scan
  |
 nand_scan_ident(mtd, maxchips);
   | // 片選在這個初始化了
  chip - >select_chip = nand_select_chip;
      | //chip實際就是struct nand_chip,
     chip - >cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
       |
      s3c_nand_hwcontrol;

board_nand_init(nand) // 初始化nand_chip對象,
     |
    nand - >IO_ADDR_R   = ( void __iomem *)NFDATA;
    nand - >IO_ADDR_W   = ( void __iomem *)NFDATA;
    nand - >cmd_ctrl   = s3c_nand_hwcontrol;
    nand - >dev_ready   = s3c_nand_device_ready;
    nand - >select_chip = s3c_nand_select_chip;
    nand - >options   = 0;

uboot命令處理的邏輯過程

struct cmd_tbl_s {
  char   *name;   /* Command Name   */
  int  maxargs; /* maximum number of arguments */
  int  repeatable; /* autorepeat allowed?  */
      /* Implementation function */
  int  ( *cmd)( struct cmd_tbl_s *, int, int, char *[]);
  char   *usage;   /* Usage message (short) */
  char   *help;   /* Help  message (long) */
};
typedef struct cmd_tbl_s cmd_tbl_t;
# define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}

# define Struct_Section  __attribute__ ((unused,section ( ".u_boot_cmd")))

U_BOOT_CMD(
 tftpboot, 3, 1, do_tftpb,
  "boot image via network using TFTP protocol",
  "[loadAddress] [[hostIPaddr:]bootfilename]"
);
== = >展開 :
 類型               變量名                  設置屬性 
struct cmd_tbl_s  __u_boot_cmd_tftpboot __attribute__ ((unused,section ( ".u_boot_cmd"))) ={
 .name = "tftpboot",
 .maxargs = 3,
 .repeatable = 1,
 .cmd = do_tftpb,
 .usage = "boot image via network using TFTP protocol",
 .help = "[loadAddress] [[hostIPaddr:]bootfilename]"
}

== = > 在uboot中新增一條命令 :
common/cmd_hello.c
# include <common.h >
# include <command.h >
int do_mycmd(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
 printf( "in do_mycmd for uboot cmd test\n");
  return 0;
 
}

U_BOOT_CMD(
 mycmd, 3, 1, do_mycmd,
  "this is a uboot cmd test",
  "mycmd : no args"
);
common/Makefile
 COBJS -$(CONFIG_CMD_HELLO) += cmd_hello.o

smdkc100.h
# define CONFIG_CMD_HELLO 1

uboot命令解析過程

 

mainloop:(通常不須要去修改)

s = getenv ( "bootdelay"); //獲取環境變量的值(字符串)
 bootdelay = s ? ( int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
 s = getenv ( "bootcmd"); //獲取bootcmd中值==>"tftp 20008000 zImage ; go 20008000"
  if (bootdelay > = 0 && s && !abortboot (bootdelay)) // 倒計時
  run_command (s, 0); 
    |
   分析字符串中命令 :  命令名  參數tftp 20008000 zImage
   argc = > 3
   argv ==argv[ 0] == "tftp"
    argv[ 1] == "20008000"
    argv[ 2] == "zImage"
   argc = parse_line (finaltoken, argv);
   cmd_tbl_t *cmdtp = find_cmd(argv[ 0]);
       |
      find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
        | // cmdtp爲指針,執行.u_boot_cmd段的起始位置
        for (cmdtp = table; cmdtp != table + table_len;cmdtp ++)
         if (strncmp (cmd, cmdtp - >name, len)
          return cmdtp;
   (cmdtp - >cmd) (cmdtp, flag, argc, argv)   // 執行命令的處理函數
   
 倒計時被打斷的時候 :
  for (;;) {
  len = readline (CONFIG_SYS_PROMPT);
  rc = run_command (lastcommand, flag);

掌握:

    1, 在uboot添加命令
    2, 已知的命令對應的處理函數
         nand 命令==>  cmd_nand.c
           do_nand

 

dm9000網卡:

void start_armboot ( void)
  |
 eth_initialize(gd - >bd); // 沒有調用全部網卡的init方法
   |
  dm9000_initialize(bis)   // 本身添加,將dm9000的對象初始化,而且設置mac地址,加入鏈表
  eth_getenv_enetaddr_by_index(eth_number, env_enetaddr);
   //從環境變量中獲取,某個網卡的mac地址,  mac從軟件上設定的
   // mac保存在env_enetaddr
 eth_init(gd - >bd); // Eth.c Net 11013 2013-9-10
   // 意味着鏈表中多有的節點都會執行其中的init方法
   |
   while循環 :
   eth_current - >init(eth_current,bis) // 執行當前的節點的init方法
   eth_try_another( 0);
     |
    eth_current = eth_current - >next;
1,dm9000 == >鏈表中
 dm9000_initialize(bis) ;
2,執行dm9000的init方法
 eth_init(gd - >bd);

COBJS -$(CONFIG_DRIVER_DM9000) += dm9000x.o

struct eth_device {
  char name[NAMESIZE]; // 網卡名字
  unsigned char enetaddr[ 6]; //網卡mac地址
  int iobase; //網卡的物理地址
  int state; //網卡狀態
  int  ( *init) ( struct eth_device *, bd_t *); // 初始化方法
  int  ( *send) ( struct eth_device *, volatile void * packet, int length); // 發送
  int  ( *recv) ( struct eth_device *); //接收
  void ( *halt) ( struct eth_device *); // 終止
  struct eth_device *next;
  void *priv;
};

int dm9000_initialize(bd_t *bis)
{
  struct eth_device *dev = &(dm9000_info.netdev);
  /* Load MAC address from EEPROM */
 dm9000_get_enetaddr(dev);
 dev - >init = dm9000_init;
 dev - >halt = dm9000_halt;
 dev - >send = dm9000_send;
 dev - >recv = dm9000_rx;
 sprintf(dev - >name, "dm9000");
 eth_register(dev);   // 將dm9000節點放到鏈表中
  return 0;
}

 

uboot是如何啓動內核

1, 在0x20000100去存放內存的信息和bootargs的內容
2, 將r1=1826,告訴內核

bootm -- >uImage
tftp 20800000 uImage \; bootm 20800000
gd - >bd - >bi_arch_number = MACH_TYPE_SMDKC100;   // 1826
gd - >bd - >bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000+0x100
bootm == >do_bootm
    |
   將zImage拷貝到0x20008000
   boot_fn = boot_os[images.os.os];
   |
  do_bootm_linux   //Bootm.c (lib_arm):int do_bootm_linux
    | //gd->bd->bi_arch_number = MACH_TYPE_SMDKC100; 1826
   bd_t *bd = gd - >bd;
    int machid = bd - >bi_arch_number;
    void ( *theKernel)( int zero, int arch, uint params);
    char *commandline = getenv ( "bootargs");
   theKernel = ( void ( *)( int, int, uint))images - >ep;   // 0x20008000
   
   setup_start_tag (bd);t
   setup_memory_tags (bd);
   setup_commandline_tag (bd, commandline); 
   setup_end_tag (bd);
   theKernel ( 0, machid, bd - >bi_boot_params);

全局的數據:gd

DECLARE_GLOBAL_DATA_PTR;
# define DECLARE_GLOBAL_DATA_PTR      register volatile gd_t *gd asm ( "r8")
某個.c中想使用gd變量 : 加上這句話
DECLARE_GLOBAL_DATA_PTR;
gd - >flags |= GD_FLG_RELOC;
board_init
  |
 gd - >bd - >bi_arch_number = MACH_TYPE_SMDKC100; // 1826
 gd - >bd - >bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000 + 0x100
env_init
  |
 gd - >env_addr   = (ulong) &default_environment[ 0];
 gd - >env_valid = 1;
init_baudrate
  |
 gd - >bd - >bi_baudrate = gd - >baudrate = 115200
serial_init == >drivers /serial /serial_s5pc1xx.c   // 通常uboot對於串口的部分,都基本上是ok
   int serial_init_dev( const int dev_index)
  {
    struct s5pc1xx_uart * const uart = s5pc1xx_get_base_uart(dev_index);
    /* reset and enable FIFOs, set triggers to the maximum */
   writel( 0, &uart - >ufcon);
   writel( 0, &uart - >umcon);
    /* 8N1 */
   writel(0x3, &uart - >ulcon);
    /* No interrupts, no DMA, pure polling */
   writel(0x245, &uart - >ucon);
   serial_setbrg_dev(dev_index);
    return 0;
  }
 gd - >have_console = 1;

dram_init :
 gd - >bd - >bi_dram[ 0].start = PHYS_SDRAM_1; //起始位置
 gd - >bd - >bi_dram[ 0].size = get_ram_size(( long *)PHYS_SDRAM_1,PHYS_SDRAM_1_SIZE); // 計算內存大小
     = 256 * 1024 * 1024
   

彙總:
gd - >bd - >bi_arch_number = MACH_TYPE_SMDKC100; // 1826, 機器id, uboot和內核達成的一個協議
gd - >bd - >bi_boot_params = PHYS_SDRAM_1 + 0x100; // 0x20000000 + 0x100
gd - >bd - >bi_baudrate = gd - >baudrate = 115200
gd - >bd - >bi_dram[ 0].start = PHYS_SDRAM_1; 
gd - >bd - >bi_dram[ 0].size = 256 * 1024 * 1024;
gd - >have_console = 1;
gd - >flags |= GD_FLG_RELOC;
gd - >bd - >bi_ip_addr = getenv_IPaddr ( "ipaddr");

串口的初始化

//串口的初始化, 將全部的串口設備作成對象 struct serial_device, 用鏈表鏈接起來
serial_initialize();
//將全部的外圍設備所有作成對象 struct stdio_dev, 若是想要研究uboot中有lcd,研究這塊
stdio_init (); /* get the devices list going. */
// 跳轉表
jumptable_init ();
// 將stdin, out, err==> serial
console_init_r (); /* fully init console as a device */
  /* enable exceptions */
enable_interrupts ();

 

smdkc100全部平臺數據的註冊流程

static int __init customize_machine( void)
{
  /* customizes platform devices, or adds new ones */
  if (init_machine)
  init_machine();
  return 0;
}
arch_initcall(customize_machine);
smdkc100_machine_init
  |
 platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices));
   |
  platform_device_register(devs[i]);
 
init /main.c
start_kernel
  |
 printk(KERN_NOTICE "%s", linux_banner);
 setup_arch( &command_line); // 創建平臺相關的數據,會到0x200000100去uboot存放數據
    |
   mdesc = setup_machine(machine_arch_type); // 獲取machine描述
     //struct machine_desc *mdesc===>mach-smdkc100.c==>MACHINE_START
   mdesc - >boot_params; //獲取0x20000100
   tags = phys_to_virt(mdesc - >boot_params);
   parse_tags(tags); // 獲取bootargs, from就在這裏初始化
   strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
 printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line);

 

內核是如何去處理bootargs中的全部參數:

parse_args( "Booting kernel", static_command_line, __start___param,__stop___param - __start___param,
      &unknown_bootoption);
用途 :
uboot想傳遞一個自定義的值給咱們內核,內核如何處理 :
 set bootargs myval = 56 init = /linuxrc console =ttySAC0, 115200 root = /dev /nfs nfsroot = 192. 168. 7. 2 : /opt /filesystem ip = 192. 168. 7. 6  

在內核的任何地方 :
static int __init parse_myval( char *str)
{
  int val = simple_strtoul(str, NULL, 10);
 printk( "myval = %d\n", val);
  return 0
}
__setup( "myval=", parse_myval);

 

uboot上電完整內存使用

 

 @成鵬致遠

(blogs:http://lcw.cnblogs.com

(emailwwwlllll@126.com)

(qq552158509



相關文章
相關標籤/搜索