PXE导致bootargs未生效-stm32mp157 uboot
看过我文章的应该知道上次遇到了一个问题
stm32mp157c-100ask-512d-v1_extlinux.conf 这个extlinux的配置文件内容修改了我的bootargs导致我自己设定的内核参数并未成功生效
也就是我的自定义参数被覆盖了
经过一番查找,发现原因在STM32MP157在启动时使用PXE(预启动执行环境)从SD卡启动,而像i.MX6ULL这样的处理器却没有使用PXE。PXE的全称是Preboot Execution Environment,PXE通常用于网络引导,允许计算机通过网络下载启动镜像,而不是从本地存储设备启动。
可以看到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 never333 * 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 location339 * given by the 'ramdisk_addr_r' environment variable.340 *341 * If the label specifies an 'append' line, its contents will overwrite that342 * 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 know721 * 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;728729 t->type = T_INVALID;730731 /* eat non EOL whitespace */732 while (isblank(*c))733 c++;734735 /*736 * eat comments. note that string literals can't begin with #, but737 * can contain a # after their first character.738 */739 if (*c == '#') {740 while (*c && *c != '\n')741 c++;742 }743744 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 string755 * token delimited by whitespace, and then check if it756 * matches a keyword in our keyword list. if it does, it's757 * converted to a keyword token of the appropriate type, and758 * if not, it remains a string token.759 */760 get_string(&c, t, ' ', 1);761 get_keyword(t);762 }763764 *p = c;765 }
其中get_string函数原型如下
634 /*635 * get_string retrieves a string from *p and stores it as a token in636 * *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 to641 * delim is found, or a NUL byte is reached. If delim has the special value of642 * ' ', any whitespace character will be used as a delimiter.643 *644 * If lower is unequal to 0, uppercase characters will be converted to645 * lowercase in the result. This is useful to make keywords case646 * insensitive.647 *648 * The location of *p is updated to point to the first character after the end649 * of the token - the ending delimiter.650 *651 * On success, the new value of t->val is returned. Memory for t->val is652 * allocated using malloc and must be free()'d to reclaim it. If insufficient653 * 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;659660 /*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 byte664 * is reached. Then, we take e - b to find the length of the token.665 */666 b = *p;667 e = *p;668669 while (*e) {670 if ((delim == ' ' && isspace(*e)) || delim == *e)671 break;672 e++;673 }674675 len = e - b;676677 /*678 * Allocate memory to hold the string, and copy it in, converting679 * characters to lowercase if lower is != 0.680 */681 t->val = malloc(len + 1);682 if (!t->val)683 return NULL;684685 for (i = 0; i < len; i++, b++) {686 if (lower)687 t->val[i] = tolower(*b);688 else689 t->val[i] = *b;690 }691692 t->val[len] = '\0';693694 /*695 * Update *p so the caller knows where to continue scanning.696 */697 *p = e;698699 t->type = T_STRING;700701 return t->val;702 }
可以看到当lower参数为1,也就是解析L_KEYWORD
关键字的时候(例如APPEND KERNEL)会转换为小写,若解析L_SLITERAL
,输入的字符串(如 /uImage
、/UIMAGE
)保留原始大小写,区分大小写。
到这里,bootargs设置和解析的根源终于弄懂了