很客观的说,ok6410的硬件相比mini6410强大许多(同样的价钱),但是ok6410的uboot制作用起来不太方便,需要输入很多命令才可以烧写完一个系统。我还是比较怀念在2440上方便、灵活的烧写方式。 
      下面我们就来修改出一个简单的uboot,实现快速更新系统。 
 
点击此处下载 ourdev_668367WCNJTV.rar(文件大小:10K) (原文件名:main.rar)  
点击此处下载 ourdev_668368BEUX4H.rar(文件大小:79K) (原文件名:u-boot.rar)  
 
一、首先简单的说明uboot的启动过程: 
1)、从文件层面上看主要流程是在两个文件中:cpu/xxxx/start.s,lib_arm/board.c。 
Start.s  
在flash中执行的引导代码,也就是bootloader中的stage1,负责初始化硬件环境,把u-boot从flash加载到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去执行。 
1.1.6版本的start.s流程: 
硬件环境初始化: 
进入svc模式-->关闭watch dog-->屏蔽所有IRQ掩码-->设置时钟频率FCLK、HCLK、PCLK-->清I/D cache-->禁止MMU和CACHE-->配置memory control-->重定位:如果当前代码不在连接指定的地址上(对smdk2410是0x3f000000)则需要把u-boot从当前位置拷贝到RAM指定位置中;-->建立堆栈,堆栈是进入C函数前必须初始化的。-->清.bss区。-->跳到start_armboot函数中执行。(lib_arm/board.c) 
2)、lib_arm/board.c: 
start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程: 
void start_armboot (void) 
   { 
       //全局数据变量指针gd占用r8。 
DECLARE_GLOBAL_DATA_PTR; 
          /* 给全局数据变量gd安排空间*/ 
          gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 
          memset ((void*)gd, 0, sizeof (gd_t)); 
          /* 给板子数据变量gd->bd安排空间*/ 
          gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 
          memset (gd->bd, 0, sizeof (bd_t)); 
          monitor_flash_len = _bss_start - _armboot_start;//取u-boot的长度。 
          /* 顺序执行init_sequence数组中的初始化函数 */ 
          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)  
if ((*init_fnc_ptr)() != 0) { hang ();} 
/*配置可用的Flash */ 
  size = flash_init (); 
        …… 
          /* 初始化堆空间 */ 
          mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); 
          /* 重新定位环境变量, */ 
          env_relocate (); 
          /* 从环境变量中获取IP地址 */ 
          gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); 
          /* 以太网接口MAC 地址 */ 
          …… 
          devices_init ();      /* 设备初始化 */ 
          jumptable_init ();  //跳转表初始化 
          console_init_r ();    /* 完整地初始化控制台设备 */ 
          enable_interrupts (); /* 使能中断处理 */ 
          /* 通过环境变量初始化 */ 
          if ((s = getenv ("loadaddr")) != NULL)  
                  load_addr = simple_strtoul (s, NULL, 16); 
         /* main_loop()循环不断执行 */ 
          for (;;)  
          main_loop (); /* 主循环函数处理执行用户命令 -- common/main.c */ 
   } 
其中,初始化函数序列init_sequence[] 
  init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。 
init_fnc_t *init_sequence[] = { 
   cpu_init,     /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */ 
   board_init,   /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */ 
   interrupt_init,  /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */ 
   env_init,             /* 初始化环境变量 -- common/env_flash.c */ 
   init_baudrate,        /* 初始化波特率设置 -- lib_arm/board.c */ 
   serial_init,          /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */ 
   console_init_f,       /* 控制台初始化阶段1 -- common/console.c */ 
   display_banner,       /* 打印u-boot信息 -- lib_arm/board.c */ 
   dram_init,            /* 配置可用的RAM -- board/smdk2410/smdk2410.c */ 
    display_dram_config,  /* 显示RAM的配置大小 -- lib_arm/board.c */ 
    NULL, 
}; 
整个u-boot的执行就进入等待用户输入命令,解析并执行命令的死循环中。 
二、修改main_loop()函数 
我们期望此时能进入一个菜单,通过输入一些简单的指令来更新uboot、kernel、fs等。 
1)、分析/common/main.c的main_loop函数:系统进入到main_loop 
后首先判断在3秒内是否有输入。如果有输入就进入命令行模式,我们可以在此命令行下通过输入指令来更新系统。如果没有输入则执行bootm 指令。 
首先,我自己试过几次,如果用run_command来保持环境变量“setenv bootcmd nand read 0xc0008000 0x100000 0x300000\;bootm 0xc0008000 ”,系统会直接重启。比较郁闷,想了变通的方法就是在/include/configs/smdk6410.h文件里直接修改CONFIG_BOOTCOMMAND 为nand read c0008000 100000 300000;bootm c0008000。据我分析系统启动后会从这个宏里读取bootcmd参数。(如果有不对的,请高手指出) 那么我们后面就不用再设定这个参数了。  
 
void main_loop (void) 
{ 
        ……. 
s = getenv ("bootcmd");         //获取bootcmd指令 
debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>"); 
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {  
// abortboot()主要是判断bootdelay的时间内,是否有按键输入。 
…… 
run_command (s, 0); //如果没有输入就执行bootcmd指令 
      } 
//进入一个命令行模式,循环接受用户指令。 
//我们就在这里加入一个我们自己的函数MainMenu()用来执行菜单。 
MainMenu(); 
/* 
* Main Loop for Monitor Command Processing 
*/ 
…… 
} 
2)、在该文件的开头申明一个函数MainMenu():void MainMenu() 
3)、定义我们的MainMenu()  
void main_menu_usage(void) 
{ 
    printf("\r\n##### ok6410 Bootloader #####\r\n"); 
    printf(" Download u-boot\r\n"); 
    printf("[k] Download Linux kernel\r\n"); 
    //printf("[y] Download YAFFS image\r\n"); 
    printf("[c] Download cramfs image\r\n"); 
    //printf("[d] Download to SDRAM & Run\r\n"); 
    printf(" Boot the system\r\n"); 
    printf("[f] Format the Nand Flash\r\n"); 
    printf(" Set the boot parameters\r\n"); 
    printf("[r] Reboot u-boot\r\n"); 
    printf("[q] Quit from menu\r\n"); 
    printf("Enter your selection: "); 
} 
void MainMenu() 
{ 
        char c; 
    char cmd_buf[256]; 
    char name_buf[20]; 
    char val_buf[256]; 
         while (1) 
    { 
        main_menu_usage();        //输出菜单的函数 
        c = getc();                                //获取输入的字符 
        printf("%c\n", c); 
        switch (c) 
        { 
case 'u':                                         //烧写uboot 
    printf("nand erase nand and write uboot \n"); 
    strcpy(cmd_buf, "dnw c0008000 ; nand erase 0 100000 ; nand write 0xc0008000 0 100000"); 
    printf("%s \n",cmd_buf); 
    run_command(cmd_buf, 0); 
    break; 
case 'k':                                        //烧写kernel 
//先设定环境变量 
    strcpy(cmd_buf,"setenv bootargs \"root=/dev/mtdblock2 console=ttySAC0,115200\"“); 
    run_command(cmd_buf,0); 
    run_command("saveenv",0); 
    strcpy(cmd_buf, "dnw c0008000; nand erase 100000 300000 ; nand write.e 0xc0008000 100000 300000"); 
    printf("%s \n",cmd_buf); 
    run_command(cmd_buf, 0); 
    break; 
case 'c':                //烧写cramfs文件系统 
    strcpy(cmd_buf, "dnw 0xc0008000; nand erase 400000 5000000 ; nand write.e 0xc0008000 400000 5000000"); 
        printf("%s \n",cmd_buf); 
    run_command(cmd_buf, 0); 
        strcpy(cmd_buf, "setenv bootargs \"root=/dev/mtdblock2 rootfstype=cramfs console=ttySAC0,115200\""); 
        printf("%s \n",cmd_buf); 
    run_command(cmd_buf, 0); 
        strcpy(cmd_buf, "saveenv"); 
        printf("%s \n",cmd_buf); 
    run_command(cmd_buf, 0); 
    break; 
case 'b':                //bootm 重启 
    printf("Booting Linux ...\n"); 
    strcpy(cmd_buf, "nand read 0xc0008000 0x100000 0x300000;bootm 0xc0008000"); 
        printf("%s\n",cmd_buf); 
    run_command(cmd_buf, 0); 
    break; 
case 'f':                //format flash 
    strcpy(cmd_buf, "nand scrub"); 
        printf("%s\n",cmd_buf); 
    run_command(cmd_buf, 0); 
    break; 
case 's':                //更改环境参数 
    param_menu_shell();  这部分函数需要自己写 :) 
    break; 
case 'q':                //退出菜单 
    return;     
    break; 
        }               
    } 
} 
4)、加入参数修改的菜单函数  
void param_menu_usage() 
{ 
    printf("\r\n##### Parameter Menu #####\r\n"); 
    printf("[v] View the parameters\r\n"); 
    printf(" Set parameter \r\n"); 
    printf("[d] Delete parameter \r\n"); 
    printf("[w] Write the parameters to flash memeory \r\n"); 
    printf("[q] Quit \r\n"); 
    printf("[l] load env 1 \r\n");        //设置参数1,跟新系统时用 
    printf("[m] load env 2 \r\n");        //设置参数2,更新完系统后恢复的参数 
    printf("Enter your selection: "); 
} 
 
void param_menu_shell(void) 
{ 
    char c; 
    char cmd_buf[256]; 
    char name_buf[20]; 
    char val_buf[256]; 
    while (1) 
    { 
        param_menu_usage(); 
        c = getc(); 
        printf("%c\n", c); 
        switch (c) 
        { 
            case 'v': 
            strcpy(cmd_buf, "printenv "); 
            run_command(cmd_buf, 0); 
            break; 
             
            case 's': 
            sprintf(cmd_buf, "setenv "); 
        printf("Name: "); 
            readline(NULL); 
            strcat(cmd_buf, console_buffer); 
            run_command(cmd_buf, 0); 
             break; 
             
            case 'd': 
                sprintf(cmd_buf, "setenv "); 
                printf("Name: "); 
                readline(NULL); 
                strcat(cmd_buf, console_buffer); 
                run_command(cmd_buf, 0); 
                break; 
 
            case 'w': 
            sprintf(cmd_buf, "saveenv"); 
            run_command(cmd_buf, 0); 
            break; 
        
        case 'l': 
        sprintf(cmd_buf, "setenv bootargs \"root=/dev/mtdblock2 console=ttySAC0,115200\""); 
        printf("%s\n",cmd_buf); 
        run_command(cmd_buf, 0); 
//保存参数 
        run_command("saveenv", 0); 
        break; 
            case 'm': 
        sprintf(cmd_buf, "setenv bootargs \"root=/dev/mtdblock2 rootfstype=cramfs console=ttySAC0,115200\""); 
        printf("%s\n",cmd_buf); 
        run_command(cmd_buf, 0); 
        run_command("saveenv", 0); 
        break; 
            case 'q': 
                return; 
                break; 
        } 
    } 
} 
三、重新编译uboot,烧写进入nand 
第一次烧写需要按照手册上的要求来做。烧写完后,从nand启动,在读秒时按空格就进入我们上面设定的菜单了。可以很方便的进行系统的更新。 |  
  
 
 
 
 
 |