cmdline(二):uboot cmdline怎么传?cmdline kernel怎么用?

news2025/1/20 16:53:50

前面我们知道了cmdline是什么,已经在哪里添加cmdline?现在我们来看看在哪里传输cmdline,以及传输收到后怎么用?

参考内容来自前辈,感激:
https://blog.csdn.net/weixin_42031299/article/details/121239507
https://blog.csdn.net/weixin_42135087/article/details/107957684

1、uboot以tag方式给内核传参–怎么传?

我们知道cmdline是由bootloader传给kernel的。
怎么传输的?
uboot是通过在启动内核时,向内核传递tag参数,其中就包括cmdline

1、tag方式传参

  • (1)struct tag,tag是一个数据结构,在uboot和linux kernel中都有定义tag数据机构,而且定义是一样的。

  • (2)tag_header和tag_xxx。tag_header中有这个tag的size和类型编码,kernel拿到一个tag后先分析tag_header得到tag的类型和大小,然后将tag中剩余部分当作一个tag_xxx来处理。

  • (3)tag_start与tag_end。kernel接收到的传参是若干个tag构成的,这些tag由ATAG_CORE类型的tag起始,到ATAG_NONE类型的tag结束。

  • (4)tag传参的方式是由linux kernel发明的,kernel定义了这种向自身传参的方式,uboot只是实现了这种传参方式从而可以支持给kernel传参。(接口的依赖倒置)

2、内核如何接收tag参数

启动内核的代码:theKernel (0, machid, bd->bi_boot_params);

其中bd->bi_boot_params就是所有tag结构体所在的首地址,这个地址是保存在全局变量gd->bd中的,

在uboot启动的前期会指定内存地址用于存放tag结构体,然后在启动内核的时候传给内核,

内核拿到地址就会从该地址去遍历tag结构体,内核会判断tag的类型,如果是ATAG_CORE类型的tag则是起始的tag

如果是ATAG_NONE则是最后一个tag结构体,不用再往后遍历。

3、tag结构体

struct tag_header {
		u32 size;	//结构体的大小
		u32 tag;	//结构体的类型
	};

struct tag {
       struct tag_header hdr;
		union { 	//此枚举体包含了uboot传给内核参数的所有类型
				struct tag_core         core;
				struct tag_mem32        mem;
				struct tag_videotext    videotext;
				struct tag_ramdisk      ramdisk;
				struct tag_initrd       initrd;
				struct tag_serialnr     serialnr;
				struct tag_revision     revision;
				struct tag_videolfb     videolfb;
				struct tag_cmdline      cmdline;
				
				/*
				* Acorn specific
				*/
				struct tag_acorn        acorn;
				
				/*
				 * DC21285 specific
				 */
				struct tag_memclk       memclk;
				
				struct tag_mtdpart      mtdpart_info;
		} u;
};

4、构建tag结构体

	/* The list must start with an ATAG_CORE node */
	#define ATAG_CORE	0x54410001
	
	/* The list ends with an ATAG_NONE node. */
	#define ATAG_NONE	0x00000000
	
	#define tag_size(type)	((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
	#define tag_next(t)	((struct tag *)((u32 *)(t) + (t)->hdr.size))
	
	static struct tag *params;
	
	static void setup_start_tag (bd_t *bd)
	{
		params = (struct tag *) bd->bi_boot_params;//bd->bi_boot_params是专门用于保存tag结构体的内存首地址

		params->hdr.tag = ATAG_CORE;	//ATAG_CORE类型是tag结构体的开始
		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);	//将指针偏移params->hdr.size个字节,让params指向下一个可用的内存地址
	}
	
	`````````中间省略掉其他类型tag结构体的构建
	
	static void setup_end_tag (bd_t *bd)
	{
		params->hdr.tag = ATAG_NONE;
		params->hdr.size = 0;
	}

当你想实现相应的参数在uboot传输到kernel,比如uboot在设备树解析后,把里面的参数传输到kernel。那么你要在uboot和kernel都相应的添加,你参考已经存在的tag结构体添加解析,uboot中传递,内核中解析。(但是推荐设备树传递,比较方便。)
因此参数的传递其实也是通过地址的方式:uboot把内核复制到SDRAM之后,需要跳转到内核的入口函数执行。在跳转之前,还要给内核传递启动参数。传递方式是uboot把启动参数按一定的格式放在指定的地址(位于SDRAM),启动内核之后,内核再去这个地址上读取启动参数。

在uboot中,将cmdline统一放置在FDT中。FDT,flatted device tree,扁平设备树。熟悉linux的人对这个概念应该不陌生。
简单理解为将部分设备信息结构存放到device tree文件中。

2、Kernel怎么解析这个传过来的参数呢?

(1)、跳转linux kernel之前-准备cmdline

在跳转linux kernel之前(如uboot中),将cmdline数据放到了FDT中,然后将FDT的地址写入到了X0中。然后再跳转linux kernel.

请看kernel-4.14/Documentation/arm64/booting.txt

Before jumping into the kernel, the following conditions must be met:

- Quiesce all DMA capable devices so that memory does not get
  corrupted by bogus network packets or disk data.  This will save
  you many hours of debug.

- Primary CPU general-purpose register settings
  x0 = physical address of device tree blob (dtb) in system RAM.
  x1 = 0 (reserved for future use)
  x2 = 0 (reserved for future use)
  x3 = 0 (reserved for future use)

(2)、kernel启动-解析cmdline

linux kernel从stext开始启动,整个流程大概就是读取X0(FDT地址)保存到X21中,又将X21保存到__fdt_pointer全局变量中
然后再将__fdt_pointer解析处cmdline数据到boot_command_line全局变量中。

在这里插入图片描述

	/*
	 * The following callee saved general purpose registers are used on the
	 * primary lowlevel boot path:
	 *
	 *  Register   Scope                      Purpose
	 *  x21        stext() .. start_kernel()  FDT pointer passed at boot in x0
	 *  x23        stext() .. start_kernel()  physical misalignment/KASLR offset
	 *  x28        __create_page_tables()     callee preserved temp register
	 *  x19/x20    __primary_switch()         callee preserved temp registers
	 */
ENTRY(stext)
	bl	preserve_boot_args
	bl	el2_setup			// Drop to EL1, w0=cpu_boot_mode
	adrp	x23, __PHYS_OFFSET
	and	x23, x23, MIN_KIMG_ALIGN - 1	// KASLR offset, defaults to 0
	bl	set_cpu_boot_mode_flag
	bl	__create_page_tables
	/*
	 * The following calls CPU setup code, see arch/arm64/mm/proc.S for
	 * details.
	 * On return, the CPU will be ready for the MMU to be turned on and
	 * the TCR will have been set.
	 */
	bl	__cpu_setup			// initialise processor
	b	__primary_switch
ENDPROC(stext)

这里调用了:

  • preserve_boot_args
  • __primary_switch

在preserve_boot_args将X0(fdt地址)暂时先保存到了X21中

preserve_boot_args:
	mov	x21, x0				// x21=FDT

	adr_l	x0, boot_args			// record the contents of
	stp	x21, x1, [x0]			// x0 .. x3 at kernel entry
	stp	x2, x3, [x0, #16]

	dmb	sy				// needed before dc ivac with
						// MMU off

	mov	x1, #0x20			// 4 x 8 bytes
	b	__inval_dcache_area		// tail call
ENDPROC(preserve_boot_args)

__primary_switch调用了__primary_switched
__primary_switch:
#ifdef CONFIG_RANDOMIZE_BASE
	mov	x19, x0				// preserve new SCTLR_EL1 value
	mrs	x20, sctlr_el1			// preserve old SCTLR_EL1 value
#endif

	bl	__enable_mmu
#ifdef CONFIG_RELOCATABLE
	bl	__relocate_kernel
#ifdef CONFIG_RANDOMIZE_BASE
	ldr	x8, =__primary_switched
	adrp	x0, __PHYS_OFFSET
	blr	x8

__primary_switched将X21(fdt地址)保存到了__fdt_pointer全局变量中

__primary_switched:
	adrp	x4, init_thread_union
	add	sp, x4, #THREAD_SIZE
	adr_l	x5, init_task
	msr	sp_el0, x5			// Save thread_info

	adr_l	x8, vectors			// load VBAR_EL1 with virtual
	msr	vbar_el1, x8			// vector table address
	isb

	stp	xzr, x30, [sp, #-16]!
	mov	x29, sp

	str_l	x21, __fdt_pointer, x5		// Save FDT pointer

	ldr_l	x4, kimage_vaddr		// Save the offset between
	sub	x4, x4, x0			// the kernel virtual and
	str_l	x4, kimage_voffset, x5		// physical mappings

	// Clear BSS
	adr_l	x0, __bss_start
	mov	x1, xzr
	adr_l	x2, __bss_stop
	sub	x2, x2, x0
	bl	__pi_memset
	dsb	ishst				// Make zero page visible to PTW

在setup_arch()的时候,调用setup_machine_fdt将fdt解析到了boot_command_line全局变量中

void __init setup_arch(char **cmdline_p)
{
	pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());
......
	*cmdline_p = boot_command_line;
......
	setup_machine_fdt(__fdt_pointer);
......
}

setup_machine_fdt()—>early_init_dt_scan()—>early_init_dt_scan_nodes()

通过调用将fdt解析到了boot_command_line中,of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line)

static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
	void *dt_virt = fixmap_remap_fdt(dt_phys);
	const char *name;

	if (!dt_virt || !early_init_dt_scan(dt_virt)) {
		pr_crit("\n"
			"Error: invalid device tree blob at physical address %pa (virtual address 0x%p)\n"
			"The dtb must be 8-byte aligned and must not exceed 2 MB in size\n"
			"\nPlease check your bootloader.",
			&dt_phys, dt_virt);

		while (true)
			cpu_relax();
	}

	name = of_flat_dt_get_machine_name();
	if (!name)
		return;
	/* backward-compatibility for third-party applications */
	machine_desc_set(name);

	pr_info("Machine model: %s\n", name);
	dump_stack_set_arch_desc("%s (DT)", name);
}

 bool __init early_init_dt_scan(void *params)
 {
 	bool status;
 
 	status = early_init_dt_verify(params);
 	if (!status)
 		return false;
 
 	early_init_dt_scan_nodes();
 	return true;
 }

 void __init early_init_dt_scan_nodes(void)
 {
 	/* Retrieve various information from the /chosen node */
 	of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
 
 	/* Initialize {size,address}-cells info */
 	of_scan_flat_dt(early_init_dt_scan_root, NULL);
 
 	/* Setup memory, calling early_init_dt_add_memory_arch */
 	of_scan_flat_dt(early_init_dt_scan_memory, NULL);
 }

在start_kernel()打印了cmdline.

asmlinkage __visible void __init start_kernel(void)
{
…
pr_notice(“Kernel command line: %s\n”, boot_command_line);
…
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/40912.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Boot JPA EntityManager实体管理器示例

在本教程中,您将了解如何在 Spring Boot 示例中使用 JPA EntityManager(使用 CRUD 操作和查询方法)。我将向您展示: 在 Spring 引导中访问 JPA 实体管理器的方法如何使用实体管理器方法:执行SQL查询使用和CRUD操作cre…

【Android App】实现在线语音合成功能(使用云知声平台和WebSocket 超详细 附源码)

需要源码和Jar包请点赞关注收藏后评论区留下QQ~~~ 一、在线语音合成 虽然国产智能机大多集成了中文语音引擎,但是系统自带的语音工具无法满足商用要求,功能单一,所以势必引入第三方的语音引擎,依靠第三方提供的开发包统一支撑语音…

【新知实验室】——腾讯云音视频TRTC体验

腾讯实时音视频 TRTC 是什么? 腾讯实时音视频(Tencent Real-Time Communication,TRTC)将腾讯21年来在网络与音视频技术上的深度积累,以多人音视频通话和低延时互动直播两大场景化方案,通过腾讯云服务向开发…

clickHouse基础语法

clichouse数据类型 整形 lnt8 8bit,1字节 (-128-127)lnt16 16bitlnt32 32bitlnt64 64bit 无符号整型 相比于上面,就是把负数部分挪到正数部分 Ulnt8 (0-255)Ulnt16Ulnt32Ulnt64 浮点型 Float32 也就是floatFloa…

Instant Neural Graphics Primitives with a Multiresolution Hash Encoding以及源码浅析

背景 现存的一些新视图合成的训练过程和渲染速度都比较慢,其原因是因为query point需要使用MLP编码,而且在一个采样空间中,存在很多无效的query point也要计算其density和color,从而出现很多冗余计算。 作者针对这个问题&#x…

MAUI 中使用 DI 及 MVVM

MAUI 中使用 DI 及 MVVM为什么要使用 依赖注入 和 MVVM如何在 MAUI 中使用依赖注入如何使用 MVVM不使用框架或组件定义一个 BaseViewModelMainViewModel 的实现MainPage 中进行 Binding使用组件优化前面的 ViewModel 代码基项目的效果为什么要使用 依赖注入 和 MVVM MVVM 和 依…

1535_TriCore编译器Tasking使用_汇编分区、内置函数以及伪指令

全部学习汇总: GreyZhang/TriCore_Tasking_Compiler_Skills: Some skills for Tasking compiler on AURIX platform. Happy hacking! (github.com) 看了一下这个章节的内容,原本看着页数很多拆分成了两次学习。后面发现剩下的这部分内容主要并不是框架性…

项目实战——项目上线

ps : 项目要在云服务器上部署,博主自己是用的腾讯云,大家可以选择购买合适的服务器进行部署 目录 一、AC终端操 1、ssh登录服务器 2、创建新用户 3、分配用户 sudo 权限 4、配置免密登录(SSH) 5、传递祖传文件给服务器 6、安…

JetpackCompose从入门到实战学习笔记2——Modifier的简单使用

JetpackCompose从入门到实战学习笔记2——Modifier的简单使用 1.Image的使用: Composable fun Image(modifier: Modifier) {Row {Image(painterResource(id R.mipmap.iv_pic),contentDescription stringResource(R.string.description),modifier modifier.size…

阿里P8熬了一个月肝出这份32W字Java面试手册,传到Git上目前star数达到了30K+

互联网行业竞争越来越严峻,面试也是越来越难,一直以来我都想整理一套完美的面试宝典,奈何难抽出时间,这套1000道的Java面试手册我整理了整整1个月,上传到Git上目前star数达到了30K这套互联网Java工程师面试题包括了&am…

【iOS】UITableView的动态Cell高度(Masonry)

动态cell高度评论长度不同引出的问题实现评论长度不同引出的问题 对于之前写的项目的评论部分,由于评论文字字数的不同会导致label高度不同,所以需要设定不同的cell高度来展示。 一开始使用了 CGSize labelSize [label.text boundingRectWithSize:CG…

数据结构与算法_AVL平衡二叉树_四种旋转,插入和删除

1 AVL平衡二叉树的概念 平衡二叉树在BST树基础上加了平衡操作。 BST树特点 :在BST树的基础上,引入了节点“平衡”的概念,任意一个节点的左右子树高度差不超过 1 ,为了维持节点的平衡,引入了四种旋转操作,如…

MySQL的时区引起的前后端数据交互不畅的问题解决

MySQL的时区问题 一、问题起源 在使用swagger2进行代码测试时,执行完成后显示的时间与国内时间少了8个小时 强迫症的原因,就手贱了如下操作 ① 修改MySQL内的时间 set global time_zone 8:00; flush privileges;② show variables like “%time_zone%…

整数除法不用除号

给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 *、除号 / 以及求余符号 % 。 注意: 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) 8 以及 truncate(-2…

【路径规划】(2) A* 算法求解最短路,附python完整代码

大家好,今天和各位分享一下机器人路径规划中非常经典的 A* 算法,感兴趣的点个关注,文末有 python 代码,那我么开始吧。 1. 算法介绍 A* 算法是 1968 年 P.E.Hart[1]等人所提出的在全局地图环境中所有已知情形下求解最短路径问题的…

部分gcc预定义宏和函数栈帧的内存分布

本文简单基于树莓派8,linux4.4.50版本,32位arm cpu 尝试了解函数调用栈的内存分布的形态。使用gcc内置的宏 __builtin_frame_address 来打印栈帧内存上的信息,以及了解一下常用的gcc 内置的宏的输出。 针对 __builtin_frame_address 在gcc官网…

猴子也能学会的jQuery第十二期——jQuery遍历(上)

📚系列文章—目录🔥 猴子也能学会的jQuery第一期——什么是jQuery 猴子也能学会的jQuery第二期——引用jQuery 猴子也能学会的jQuery第三期——使用jQuery 猴子也能学会的jQuery第四期——jQuery选择器大全 猴子也能学会的jQuery第五期——jQuery样式操作…

PIC单片机3——外部中断

//RB2&#xff08;INT2&#xff09;作为外中断 #include <p18cxxx.h>/*18F系列单片机头文件*/ void PIC18F_High_isr(void);/*中断服务函数声明*/ void PIC18F_Low_isr(void); #pragma code high_vector_section0x8 /*高优先级中断响应时&#xff0c;会自动跳转到0x8处…

基于三相坐标系状态方程的感应电动机起动动态计算matlab程序

基于三相坐标系状态方程的感应电动机起动动态计算matlab程序 1 异步电动机动态数学模型的性质 电磁耦合是机电能量转换的必要条件&#xff0c;电流与磁通的乘积产生转矩&#xff0c;转速与磁通的乘积得到感应电动势。无论是直流电动机&#xff0c;还是交流电动机均如此。 交、直…

二十七、CANdelaStudio深入-编辑技巧(一致性检查)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio软件的一致性检查,欢迎各位朋友订阅、评论…