PXE导致bootargs未生效-stm32mp157 uboot

PXE导致bootargs未生效-stm32mp157 uboot

看过我文章的应该知道上次遇到了一个问题

stm32mp157c-100ask-512d-v1_extlinux.conf 这个extlinux的配置文件内容修改了我的bootargs导致我自己设定的内核参数并未成功生效
也就是我的自定义参数被覆盖了

经过一番查找,发现原因在STM32MP157在启动时使用PXE(预启动执行环境)从SD卡启动,而像i.MX6ULL这样的处理器却没有使用PXE。PXE的全称是Preboot Execution Environment,PXE通常用于网络引导,允许计算机通过网络下载启动镜像,而不是从本地存储设备启动。

但是mp157的uboot就是选择了pxe从sd卡或者emmc启动,原因未知

可以看到uboot的配置里确实用到了PXE,于是我就打开了uboot/cmd/pxe_utils.c,可以看到以下代码:

 329 /*
 330  * Boot according to the contents of a pxe_label.
 331  *
 332  * If we can't boot for any reason, we return.  A successful boot never
 333  * returns.
 334  *
 335  * The kernel will be stored in the location given by the 'kernel_addr_r'
 336  * environment variable.
 337  *
 338  * If the label specifies an initrd file, it will be stored in the location
 339  * given by the 'ramdisk_addr_r' environment variable.
 340  *
 341  * If the label specifies an 'append' line, its contents will overwrite that
 342  * of the 'bootargs' environment variable.
 343  */
 344 static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
 345 {
 346     char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
 347     char initrd_str[28];
 348     char mac_str[29] = "";
 349     char ip_str[68] = "";
 350     char *fit_addr = NULL;
 351     int bootm_argc = 2;
 352     int len = 0;
 353     ulong kernel_addr;
 354     void *buf;
     ...

这一句解释了原因,如果conf文件中存在APPEND字段,其内容会强制覆盖bootargs

If the label specifies an 'append' line, its contents will overwrite that of the 'bootargs' environment variable.

到此已经水落石出,但是还有一个问题,它是如何解析APPEND字段的,使用grep搜索”APPEND“得到如下代码段

 597 /*
 598  * Keywords recognized.
 599  */
 600 static const struct token keywords[] = {
 601     {"menu", T_MENU},
 602     {"title", T_TITLE},
 603     {"timeout", T_TIMEOUT},
 604     {"default", T_DEFAULT},
 605     {"prompt", T_PROMPT},
 606     {"label", T_LABEL},
 607     {"kernel", T_KERNEL},
 608     {"linux", T_LINUX},
 609     {"localboot", T_LOCALBOOT},
 610     {"append", T_APPEND},
 611     {"initrd", T_INITRD},
 612     {"include", T_INCLUDE},
 613     {"devicetree", T_FDT},
 614     {"fdt", T_FDT},
 615     {"devicetreedir", T_FDTDIR},
 616     {"fdtdir", T_FDTDIR},
 617     {"ontimeout", T_ONTIMEOUT,},
 618     {"ipappend", T_IPAPPEND,},
 619     {"background", T_BACKGROUND,},
 620     {NULL, T_INVALID}
 621 };

其中关键字为token类型,但是这里是小写,实际上的conf文件为了格式工整和关键字区分使用全大写,那大小写转换的代码又在哪?
继续寻找,找到720行

 719 /*
 720  * Get the next token.  We have to keep track of which state we're in to know
 721  * if we're looking to get a string literal or a keyword.
 722  *
 723  * *p is updated to point at the first character after the current token.
 724  */
 725 static void get_token(char **p, struct token *t, enum lex_state state)
 726 {
 727     char *c = *p;
 728
 729     t->type = T_INVALID;
 730
 731     /* eat non EOL whitespace */
 732     while (isblank(*c))
 733         c++;
 734
 735     /*
 736      * eat comments. note that string literals can't begin with #, but
 737      * can contain a # after their first character.
 738      */
 739     if (*c == '#') {
 740         while (*c && *c != '\n')
 741             c++;
 742     }
 743
 744     if (*c == '\n') {
 745         t->type = T_EOL;
 746         c++;
 747     } else if (*c == '\0') {
 748         t->type = T_EOF;
 749         c++;
 750     } else if (state == L_SLITERAL) {
 751         get_string(&c, t, '\n', 0);
 752     } else if (state == L_KEYWORD) {
 753         /*
 754          * when we expect a keyword, we first get the next string
 755          * token delimited by whitespace, and then check if it
 756          * matches a keyword in our keyword list. if it does, it's
 757          * converted to a keyword token of the appropriate type, and
 758          * if not, it remains a string token.
 759          */
 760         get_string(&c, t, ' ', 1);
 761         get_keyword(t);
 762     }
 763
 764     *p = c;
 765 }

其中get_string函数原型如下

  634 /*
 635  * get_string retrieves a string from *p and stores it as a token in
 636  * *t.
 637  *
 638  * get_string used for scanning both string literals and keywords.
 639  *
 640  * Characters from *p are copied into t-val until a character equal to
 641  * delim is found, or a NUL byte is reached. If delim has the special value of
 642  * ' ', any whitespace character will be used as a delimiter.
 643  *
 644  * If lower is unequal to 0, uppercase characters will be converted to
 645  * lowercase in the result. This is useful to make keywords case
 646  * insensitive.
 647  *
 648  * The location of *p is updated to point to the first character after the end
 649  * of the token - the ending delimiter.
 650  *
 651  * On success, the new value of t->val is returned. Memory for t->val is
 652  * allocated using malloc and must be free()'d to reclaim it.  If insufficient
 653  * memory is available, NULL is returned.
 654  */
 655 static char *get_string(char **p, struct token *t, char delim, int lower)
 656 {
 657     char *b, *e;
 658     size_t len, i;
 659
 660     /*
 661      * b and e both start at the beginning of the input stream.
 662      *
 663      * e is incremented until we find the ending delimiter, or a NUL byte
 664      * is reached. Then, we take e - b to find the length of the token.
 665      */
 666     b = *p;
 667     e = *p;
 668
 669     while (*e) {
 670         if ((delim == ' ' && isspace(*e)) || delim == *e)
 671             break;
 672         e++;
 673     }
 674
 675     len = e - b;
 676
 677     /*
 678      * Allocate memory to hold the string, and copy it in, converting
 679      * characters to lowercase if lower is != 0.
 680      */
 681     t->val = malloc(len + 1);
 682     if (!t->val)
 683         return NULL;
 684
 685     for (i = 0; i < len; i++, b++) {
 686         if (lower)
 687             t->val[i] = tolower(*b);
 688         else
 689             t->val[i] = *b;
 690     }
 691
 692     t->val[len] = '\0';
 693
 694     /*
 695      * Update *p so the caller knows where to continue scanning.
 696      */
 697     *p = e;
 698
 699     t->type = T_STRING;
 700
 701     return t->val;
 702 }

可以看到当lower参数为1,也就是解析L_KEYWORD 关键字的时候(例如APPEND KERNEL)会转换为小写,若解析L_SLITERAL ,输入的字符串(如 /uImage/UIMAGE)保留原始大小写,区分大小写。

到这里,bootargs设置和解析的根源终于弄懂了

posted @ 2025-03-03 18:20  月舞纱雨日  阅读(31)  评论(0)    收藏  举报