zoukankan      html  css  js  c++  java
  • 【系统移植】uboot详细分析

    uboot使用

        uboot控制台,倒计时
        命令: 调试,操作一些硬件

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

        环境变量: 为命令提供参数
         serverip : tftp命令提供tftp服务器的地址
         ipaddr : tftp命令提供tftp客户端(开发板)的地址

    两个环境变量

        uboot: 下载内核,并启动内核
        bootcmd倒计时结束后,uboot应该自动做什么事情

     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 20008000

       从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

    官方的uboot

    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.lds

    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)

    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 (boardsamsungsmdkc100):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 boardsamsungSmdkc100 
         // 内存的初始化比较复杂, 原厂会提供(1.3.4)
         // 向FAE要
         // 这部分代码运行有问题
         1,mem_ctrl_asm_init
         2,mem_setup.S需要被编译<===boardsamsungSmdkc100Makefile
         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 ");
         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 ", 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 driversmtdNand 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 driversmtdNand 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 ");
     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 ", 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 ", val);
     return 0
    }
    __setup("myval=", parse_myval);

     

    uboot上电完整内存使用

     @成鹏致远

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

    (emailwwwlllll@126.com)

    (qq552158509





  • 相关阅读:
    Reference Counting GC (Part two :Partial Mark & Sweep)
    Reference Counting GC (Part one)
    Union File System
    Linux Cgroups
    Mark Sweep GC
    取模运算
    负数取模怎么算
    牛客【2021寒假集训营第一场】J-一群小青蛙呱蹦呱蹦呱
    记codeforces一个简单模拟题
    筛法求素数
  • 原文地址:https://www.cnblogs.com/lcw/p/3802644.html
Copyright © 2011-2022 走看看