ARM技术论坛
直播中

万飞

6年用户 12经验值
擅长:测量仪表 嵌入式技术 控制/MCU RF/无线 MEMS/传感技术
私信 关注
[问答]

自己写u-boot启动不了内核?请问是为什么?

仿照韦东山老师的方法自己写一个简单的u-boot,发现启动不了zImage内核,在Starting kernel...处卡住。sdram中内核的数据与nand中内核的数据以及内核本身的数据是一致的,说明内核从nand拷贝到sdram是正确的。又看了一下sdram中的环境变量,发现也是正确的。代码已经上传附件,请大神帮我分析一下,问题出在哪里?? 不胜感激!!

开发板:友善之臂Tiny 6410, 256M sdram, 2G MLC2 Nand flash
Nand型号: K9GAG08U0E, 2G MLC2, 8K Page
烧写工具:MiniTools

友善之臂提供的MiniTools在烧写uboot到nand中的时候应该是已经处理过前4页只烧写2k内容的问题的。经过测试,友善之臂的MiniTools下载uboot到nand flash时每页只用前2K的空间而后面6K的空间装载的内容和前2K是一样的。

hexdump zImage:

[root@localhost bins]# hexdump zImage | less
0000000 0000 e1a0 0000 e1a0 0000 e1a0 0000 e1a0
0000020 0002 ea00 2818 016f 0000 0000 5ac0 0039
0000030 7001 e1a0 8002 e1a0 2000 e10f 0003 e312
0000040 0001 1a00 0017 e3a0 3456 ef12 2000 e10f
0000050 20c0 e382 f002 e121 0000 0000 0000 0000
0000060 00d8 e28f 186e e890 d01c e590 47b8 e59f
0000070 0001 e050 000a 0a00 5000 e085 b000 e08b
0000080 c000 e08c 2000 e082 3000 e083 d000 e08d
0000090 1000 e59b 1000 e081 1004 e48b 000c e15b
00000a0 fffa 3aff 0000 e3a0 0004 e482 0004 e482
00000b0 0004 e482 0004 e482 0003 e152 fff9 3aff
00000c0 002e eb00 100d e1a0 2801 e28d 0002 e154
00000d0 0016 2a00 0006 e084 0005 e150 0013 9a00
00000e0 5002 e1a0 0005 e1a0 3007 e1a0 024f eb00



串口中打印的数据如下:

Copying kernel from nand to sdram...
0x1234ABCD
Data in sdram from 0x50400000:
0xE1A00000 0xE1A00000 0xE1A00000 0xE1A00000
0xE1A00000 0xE1A00000 0xE1A00000 0xE1A00000
0xEA000002 0x016F2818 0x00000000 0x00395AC0
0xE1A07001 0xE1A08002 0xE10F2000 0xE3120003
Copying finished!
Setting kernel parameters...
Kernel parameters setting finished!
Data in sdram from 0x50000100:
0x00000005 0x54410001 0x00000000 0x00000000
0x00000000 0x00000004 0x54410002 0x10000000
0x50000000 0x00000017 0x54410009 0x746F6F72
0x65642F3D 0x746D2F76 0x6F6C6264 0x20656B63
Starting kernel...

使用友善之臂提供的u-boot_nand-ram256.bin烧写可以正常启动内核。

bdinfo如下:

MINI6410 # bdinfo
arch_number = 0x000009D8
env_t       = 0x00000000
boot_params = 0x50000100
DRAM bank   = 0x00000000
-> start    = 0x50000000
-> size     = 0x10000000
ethaddr     = 08:90:90:90:90:90
ip_addr     = 192.168.1.230
baudrate    = 115200 bps

printenv如下:

MINI6410 # printenv
bootdelay=3
baudrate=115200
ethaddr=08:90:90:90:90:90
ipaddr=192.168.1.230
serverip=192.168.1.88
gatewayip=192.168.1.1
netmask=255.255.255.0
bootcmd=nand read.i c0008000 400000 500000;bootm c0008000
bootargs=root=/dev/mtdblock2 console=ttySAC0,115200
stdin=serial
stdout=serial
stderr=serial
Environment size: 292/131068 bytes

我的程序代码如下:


/**** nand.c ****/

#define MEM_SYS_CFG     (*((volatile unsigned long *)0x7E00F120))
#define NFCONF          (*((volatile unsigned long *)0x70200000))
#define NFCONT          (*((volatile unsigned long *)0x70200004))
#define NFCMMD          (*((volatile unsigned long *)0x70200008))
#define NFADDR          (*((volatile unsigned long *)0x7020000C))
#define NFDATA          (*((volatile unsigned char *)0x70200010))
#define NFSTAT          (*((volatile unsigned long *)0x70200028))


void nand_select(void) { NFCONT &= ~(1<<1); }

void nand_deselect(void) { NFCONT |= (1<<1);}

void nand_cmd(unsigned char cmd) { NFCMMD = cmd;}

void nand_addr(unsigned char addr) { NFADDR = addr; }

unsigned char nand_get_data(void) { return NFDATA; }

void nand_send_data(unsigned char data) { NFDATA = data; }

void wait_ready(void) { while ((NFSTAT & 0x1) == 0); }

void nand_reset(void)
{
nand_select();

nand_cmd(0xff);

wait_ready();

nand_deselect();
}


void nand_init(void)
{
MEM_SYS_CFG &= ~(1<<1);

#define TACLS     0
#define TWRPH0    2
#define TWRPH1    1

NFCONF &= ~((1<<30) | (7<<12) | (7<<8) | (7<<4));

NFCONF |= ((TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4));

NFCONT |= 1;

NFCONT &= ~(1<<16);

nand_reset();
}


void nand_send_addr(unsigned int addr)
{

unsigned int page = addr / 8192;
unsigned int col = addr & (8192 - 1);


/* 这两个地址表示从页内哪里开始 */

nand_addr(col & 0xff);

nand_addr((col >> 8) & 0xff);



/* 下面三个地址表示哪一页 */

nand_addr(page & 0xff);

nand_addr((page >> 8) & 0xff);

nand_addr((page >> 16) & 0xff);

}


int nand_read(unsigned int nand_start, unsigned int ddr_start, unsigned int len)
{
unsigned int addr = nand_start;
int i = nand_start % 8192;  //列地址
int left = i;    //
int count = 0;
unsigned char *dest = (unsigned char *)ddr_start;
unsigned char data = 0;

nand_select();

while (count < len)
{
  nand_cmd(0x00);

  nand_send_addr(addr);

  nand_cmd(0x30);

  wait_ready();

  for (; i < (8192-left) && count < len; i++)
  {
   data = nand_get_data();

   if(addr< (4*8192))  //前4页
   {
      if(i<(2048-left))   //小于2K
      {
    dest[count++] = data;
      }
   }

   else
   {
      dest[count++] = data;
   }

   //dest[count++] = nand_get_data();
   addr++;
  }

  i = 0;

  left = i;

}

nand_deselect();

return 0;
}




int copy2ddr(unsigned int nand_start, unsigned int ddr_start, unsigned int len)
{
int ret;

nand_init();

ret = nand_read(nand_start, ddr_start, len);

return ret;
}

/****** boot.c *******/

#include "setup.h"

#include "uart.h"

extern int copy2ddr(unsigned int nand_start, unsigned int ddr_start, unsigned int len);

extern void putstr(char * str);

extern void uart_init(void);

static struct tag *params;

void setup_start_tag (void)
{
params = (struct tag *) 0x50000100;

params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);

params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;

params = tag_next (params);
}


void setup_memory_tags (void)
{

params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);

params->u.mem.start = 0x50000000;      //RAM起始地址
params->u.mem.size =  256*1024*1024;   //RAM: 256MB

params = tag_next (params);

}

int strlen(char *str)
{
    int i = 0;
    while(str)
    {
        i++;
    }

    return i;
}

void strcpy(char *dest, char *src)
{
    char *tmp = dest;

    while ((*dest++ = *src++) != '');
}

void setup_commandline_tag (char *commandline)
{
    int len = strlen(commandline) + 1;

params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + len + 3 ) >> 2;

strcpy (params->u.cmdline.cmdline, commandline);

params = tag_next (params);
}

void setup_end_tag (void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}


int main(void)
{
int i,j;
volatile unsigned int *p_sdram_kernel = (volatile unsigned int *)0x50008000;
volatile unsigned int *p_sdram_parameters = (volatile unsigned int *)0x50000100;

uart_init();

void (*theKernel)(int zero, int arch, unsigned int params);



/* 1. 从nand flash里把内核读入sdram */

putstr("rnCopying kernel from nand to sdram...rn");

copy2ddr(0x400000, 0x50008000, 0x500000);   //将内核从nand flash拷贝到sdram,内核烧写在nand flash的 0x400000地址,拷贝到sdram的0x50008000地址,拷贝大小为0x500000

puthex(0x1234ABCD);
    putstr("nr");

putstr("Data in sdram from 0x50400000: rn");

for(j = 0; j<4;j++)
{
  for(i = 0; i<4; i++)
  {
   puthex(*(p_sdram_kernel + 4*j+i));
   putstr(" ");
  }

     putstr("nr");
}


putstr("Copying finished!rn");

/* 2. 设置内核启动参数 */

putstr("Setting kernel parameters...rn");

setup_start_tag();

setup_memory_tags();

setup_commandline_tag("root=/dev/mtdblocke rootfstype=yaffs2 init=/linuxrc console=ttySAC0,115200 lcd=S70");

setup_end_tag();

putstr("Kernel parameters setting finished!rn");
putstr("Data in sdram from 0x50000100: rn");

for(j = 0; j<4;j++)
{
  for(i = 0; i<4; i++)
  {
   puthex(*(p_sdram_parameters + 4*j+i));
   putstr(" ");
  }

     putstr("nr");
}

/* 3. 跳转到内核 */

putstr("Starting kernel...rn");

theKernel = (void (*)(int, int, unsigned int)) 0x50008000;  //内核在sdram中的地址
theKernel(0, 2520, 0x50000100);  //机器ID: 2520,  内核参数约定地址:0x50000100

putstr("Error!rn");


return 0;
}







    21.myboot.zip (2019-10-11 00:58 上传)

    78.46 KB, 下载次数: 9

回帖(1)

李存皓

2019-10-11 01:10:04

最佳答案

没看出什么问题呀,这部分看起来都很ok
你确定启动后是卡死了而不是串口没有设置为console吗?会不会没有卡死,只是串口无输出
启动参数中我看用的ttySAC0 115200,确定内核驱动中有这个口吗?

首先第一步,你已经确定了copy过程的正确,包括内核和启动参数
然后第二步,你需要检查kernel中解析启动参数的地方格式是否和你的启动参数一致
第三步,确定是否kernel中有ttySAC0口
地四步,如果上面都没有结果,那么确定下kernel程序是否运行了,比如kernel启动代码中执行一个串口打印或者io口操作(越早越好,不行就汇编控制寄存器控制io口)
第五步,如果kernel是没有启动,那么就要确认启动地址相关,主要是编译器相关,可以用readelf,来检查kernel的elf格式文件,查看复位的代码地址
11 举报
  • 万飞: 可能我描述得不够清楚。 我希望通过运行自己编写的简单u-boot来启动内核。问题是在完成内核从nand拷贝到sdram,设置环境变量后, 跳转到sdram中内核的起始地址时,内核并没有启动。也就是说运行到如下代码后,卡死了。
    /* 3. 跳转到内核 */

    putstr(&quot;Starting kernel...\r\n&quot;);

    theKernel = (void (*)(int, int, unsigned int)) 0x50008000;  //内核在sdram中的地址
    theKernel(0, 2520, 0x50000100);  //机器ID: 2520,  内核参数约定地址:0x50000100
  • 李存皓 回复 万飞: 请问下你是怎么确定卡死的?就因为没有串口输出就觉得是卡死了?
    出现这个问题,需要分析为什么,我上面提出的方法就是我个人的思路,去解决问题,我也没从你的代码中发现什么错误,所以需要深入调查,你开发板提供的uboot可能没有给你源码,你自己写的uboot是否能正常加载内核需要深入调查一下
    是否内核中需要的内核参数格式和你代码中的参数格式一致?有没有其它认证相关?
    首先我从代码看没有看出问题,现在问题是没有串口输出,那么是内核还没有启动到串口输出那里就失败了,还是内核根本没启动?我觉得你应该验证这些问题
  • 万飞 回复 李存皓: 谢谢你的回答! 现在可以明确的是内核没有启动,因为使用友善官方提供的u-boot启动内核时,最开始会执行解压操作,串口会提示“Uncompressing...”。而我的代码都没有执行内核的解压。 您的意思时很有可能问题出在内核参数上?
  • 李存皓 回复 万飞: 你用的是zImage?uBoot支持zImage启动内核吗?这个是压缩文件
    你可以从这里检查一下,是否需要用uImage或者在uboot里面解压zImage再copy给sdram?
  • 李存皓 回复 万飞: 你应该知道uboot里面有个bootm函数,可以了解一下,首先要获取zImage,然后解压,然后获取image的header info,找到第一个指令地址,这个地址才是theKernel的地址,我估计你就是这里有问题
  • 万飞 回复 李存皓: 好的,谢谢你! 我用的是友善之臂提供的linux内核的zImage映像文件,还没有看它的源文件,linux内核还没研究过,不知道从哪个地方看这个header info... 分数就先给你吧。
  • 万飞 回复 李存皓: 既然uboot启动不了zImage,那么为什么友善之臂提供的u-boot能够启动?
  • 李存皓 回复 万飞: 我的意思就是你开发板的uboot肯定是解压了zImage然后在解压后的image中找到了image的info,然后找到了入口,thekernel再定位到这个入口,现在的uboot版本里面这些都做的挺好的了,你查看下uboot源码里面的bootm.c文件就清除了
  • 万飞 回复 李存皓: 看了一下友善的u-boot源码,在do_bootm()函数中有对zImage启动的支持:

    #define LINUX_ZIMAGE_MAGIC        0x016f2818
            if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
                    printf(&quot;Boot with zImage\n&quot;);
                    addr = virt_to_phys(addr);
                    hdr-&gt;ih_os = IH_OS_LINUX;
                    hdr-&gt;ih_ep = ntohl(addr);
                    goto after_header_check;
            }
    #endif

    然后跳转到after_header_check处执行,一系列判断后,跳转到do_bootm_linux()函数执行。 检查了一下这个do_bootm_linux()函数,主要做的工作就是设置内核参数(tags),然后跳转到内核入口地址启动内核:

    setup_start_tag (bd);
    #ifdef CONFIG_SERIAL_TAG
            setup_serial_tag (&amp;params);
    #endif
    #ifdef CONFIG_REVISION_TAG
            setup_revision_tag (&amp;params);
    #endif
    #ifdef CONFIG_SETUP_MEMORY_TAGS
            setup_memory_tags (bd);
    #endif
    #ifdef CONFIG_CMDLINE_TAG
            setup_commandline_tag (bd, commandline);
    #endif
    #ifdef CONFIG_INITRD_TAG
            if (initrd_start &amp;&amp; initrd_end)
                    setup_initrd_tag (bd, initrd_start, initrd_end);
    #endif
    #if defined (CONFIG_VFD) || defined (CONFIG_LCD)
            setup_videolfb_tag ((gd_t *) gd);
    #endif
            setup_end_tag (bd);
    #endif

            /* we assume that the kernel is in place */
            printf (&quot;\nStarting kernel ...\n\n&quot;);

    #ifdef CONFIG_USB_DEVICE
            {
                    extern void udc_disconnect (void);
                    udc_disconnect ();
            }
    #endif

            cleanup_before_linux ();

            theKernel (0, bd-&gt;bi_arch_number, bd-&gt;bi_boot_params);
    }

    这个与我写的代码完成的工作是一样的啊? 为什么我的就不能启动zImage内核呢?

    网上查了一下,uImage只是比zImage多了64字节的头部,真正的内核还是zImage,而且zImage是可以完成自解压的。

    还请不吝赐教,谢谢!!
  • 李存皓 回复 万飞: 你自己写的uboot,theKernel的地址就是你copy的uImage到sdram的地址,而实际thekernel的地址应该不是,你看uBoot源码里面,theKernel的地址是怎么设置的就清除了,我估计你的uboot就是出现在theKernel地址的问题
  • 万飞 回复 李存皓: 我看韦东山自己写uboot时,theKernel的入口地址就是等于下载地址啊?而且u-boot源码里面好像也是这样的:

    if (argc &lt; 2) {
                    addr = load_addr;
            } else {
                    addr = simple_strtoul(argv[1], NULL, 16);
            }

    #ifdef CONFIG_ZIMAGE_BOOT
    #define LINUX_ZIMAGE_MAGIC        0x016f2818
            if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
                    printf(&quot;Boot with zImage\n&quot;);
                    addr = virt_to_phys(addr);
                    hdr-&gt;ih_os = IH_OS_LINUX;
                    hdr-&gt;ih_ep = ntohl(addr);
                    goto after_header_check;
            }
    #endif

    从上面的代码可以看到,hdr-&gt;ih_ep = ntohl(addr), 表示内核的入口地址就是等于内核的下载地址。

    void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
                         ulong addr, ulong *len_ptr, int verify)
    {
            ulong len = 0, checksum;
            ulong initrd_start, initrd_end;
            ulong data;
            void (*theKernel)(int zero, int arch, uint params);
            image_header_t *hdr = &amp;header;
            bd_t *bd = gd-&gt;bd;

    #ifdef CONFIG_CMDLINE_TAG
            char *commandline = getenv (&quot;bootargs&quot;);
    #endif

            theKernel = (void (*)(int, int, uint))ntohl(hdr-&gt;ih_ep);

    从上面的代码可以看到, theKernel的地址 = ntohl(hdr-&gt;ih_ep), 将hdr-&gt;ih_ep = ntohl(addr) 代入, 得到theKernel的地址 = addr = load_addr。

    这不是表明内核的入口地址就等于内核的下载地址吗?  至于为什么能相等,我也不清楚,因为对内核的启动机制没有研究...

    请赐教!谢谢!

更多回帖

发帖
×
20
完善资料,
赚取积分