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设置和解析的根源终于弄懂了

浙公网安备 33010602011771号