您的位置:首页 > 其它

【系统移植】uboot详细分析

2017-12-21 19:47 295 查看


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 (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)

(email:wwwlllll@126.com)

(qq:552158509)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐