bl31_el3_entrypoint_common_note

Linux Swap 子系统:架构、内存管理与虚拟化 Swap Linux Swap机制通过将不活跃内存页移至磁盘或压缩内存,扩展系统有效内存容量,缓解内存压力。传统架构中,逻辑Swap槽与物理存储绑定,导致资源浪费,尤其在使用Zswap等压缩技术时。为此,Vswap与Xswap提出虚拟化方案:前者以XArray实现灵活映射,支持多后端迁移;后者采用稀疏映射,简化管理但容量固定。二者在架构设计、资源记账与动态扩展上各有权衡,反映了内核开发中对性能、可维护性与灵活性的深层考量。 阅读详情

无论是bl1还是bl31都会走到el3_entrypoint_common, 只是传递过来的参数不一致。

 #if !RESET_TO_BL31
	/* ---------------------------------------------------------------------
	 * For !RESET_TO_BL31 systems, only the primary CPU ever reaches
	 * bl31_entrypoint() during the cold boot flow, so the cold/warm boot
	 * and primary/secondary CPU logic should not be executed in this case.
	 *
	 * Also, assume that the previous bootloader has already initialised the
	 * SCTLR_EL3, including the endianness, and has initialised the memory.
	 * ---------------------------------------------------------------------
	 */
	el3_entrypoint_common					\
		_init_sctlr=0					\
		_warm_boot_mailbox=0				\
		_secondary_cold_boot=0				\
		_init_memory=0					\
		_init_c_runtime=1				\
		_exception_vectors=runtime_exceptions		\
		_pie_fixup_size=BL31_LIMIT - BL31_BASE
#else

	/* ---------------------------------------------------------------------
	 * For RESET_TO_BL31 systems which have a programmable reset address,
	 * bl31_entrypoint() is executed only on the cold boot path so we can
	 * skip the warm boot mailbox mechanism.
	 * ---------------------------------------------------------------------
	 */
	el3_entrypoint_common					\
		_init_sctlr=1					\
		_warm_boot_mailbox=!PROGRAMMABLE_RESET_ADDRESS	\
		_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU	\
		_init_memory=1					\
		_init_c_runtime=1				\
		_exception_vectors=runtime_exceptions		\
		_pie_fixup_size=BL31_LIMIT - BL31_BASE

	/* ---------------------------------------------------------------------
	 * For RESET_TO_BL31 systems, BL31 is the first bootloader to run so
	 * there's no argument to relay from a previous bootloader. Zero the
	 * arguments passed to the platform layer to reflect that.
	 * ---------------------------------------------------------------------
	 */
	mov	x20, 0
	mov	x21, 0
	mov	x22, 0
	mov	x23, 0
#endif /* RESET_TO_BL31 */

A. 功能分析
bl31_entrypoint.S
el3_entrypoint_common, 这个宏对于整个系统的启动,起着承上启下的作用,在这个阶段,处理器处于安全模式。
针对这个宏的参数说明:
1. _init_sctlr:指定是否初始化寄存器SCTLR(System Control Register)。
2. _warm_boot_mailbox: 冷启动和热启动分支处理,PROGRAMMABLE_RESET_ADDRESS 宏表示系统的复位地址,为0可能是冷启动(运行程序中会进一步判断),不为0则是热启动。
3. _secondary_cold_boot:指定是否为单个 CPU 进行冷启动,如果是 COLD_BOOT_SINGLE_CPU 则表示是单个 CPU 进行冷启动。
4. _warm_boot_mailbox_init_memory:指定是否初始化内存。
5. _init_c_runtime:指定是否初始化 C 运行时。
6. _exception_vectors:指定异常向量的入口点,runtime_exceptions。
7. _pie_fixup_size:指定 PIE 修复大小,在本例中是 0。

	下面针对每一个参数执行的代码进行具体的分析,分析顺序为代码工作流程:
		1. _init_sctlr
			.if \_init_sctlr
					/* -------------------------------------------------------------
					 * This is the initialisation of SCTLR_EL3 and so must ensure
					 * that all fields are explicitly set rather than relying on hw.
					 * Some fields reset to an IMPLEMENTATION DEFINED value and
					 * others are architecturally UNKNOWN on reset.
					 *
					 * SCTLR.EE: Set the CPU endianness before doing anything that
					 *  might involve memory reads or writes. Set to zero to select
					 *  Little Endian.
					 *
					 * SCTLR_EL3.WXN: For the EL3 translation regime, this field can
					 *  force all memory regions that are writeable to be treated as
					 *  XN (Execute-never). Set to zero so that this control has no
					 *  effect on memory access permissions.
					 *
					 * SCTLR_EL3.SA: Set to zero to disable Stack Alignment check.
					 *
					 * SCTLR_EL3.A: Set to zero to disable Alignment fault checking.
					 *
					 * SCTLR.DSSBS: Set to zero to disable speculation store bypass
					 *  safe behaviour upon exception entry to EL3.
					 * -------------------------------------------------------------
					 */
					mov_imm	x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT \
							| SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT))
					msr	sctlr_el3, x0
					isb
			.endif /* _init_sctlr */		
	
			mov_imm x0, (SCTLR_RESET_VAL & ~(SCTLR_EE_BIT | SCTLR_WXN_BIT | SCTLR_SA_BIT | SCTLR_A_BIT | SCTLR_DSSBS_BIT)):
				计算 SCTLR_EL3 寄存器的新值。通过对 SCTLR_RESET_VAL 应用位掩码,我们清除了 SCTLR_EE_BIT(字节序)、SCTLR_WXN_BIT(写时不执行)、SCTLR_SA_BIT(堆栈对齐检查)、SCTLR_A_BIT(对齐错误检查)
				和 SCTLR_DSSBS_BIT(投机存储器旁路安全行为)位。结果值存储在寄存器 x0 中。
				投机存储操作是指CPU在不等待先前内存操作完成的情况下投机存储数据到内存的操作。这样做是为了通过重叠内存操作来提高性能。然而,如果存在安全漏洞,投机存储操作可能会绕过安全检查,导致信息泄露或其他安全问题。
				为了防止这种情况,在最新的CPU架构中引入了“投机存储器旁路安全行为”位。当SCTLR_DSSBS_BIT被设置时,CPU将在执行投机存储操作之前等待先前的内存操作完成,以确保投机存储操作以安全的方式执行。
				当这个位被清除时,CPU可能会在不等待的情况下执行投机存储操作,从而导致安全漏洞。
				还需要注意的是上电时,SCTLR_RESET_VAL 在arch.h中被赋了初始值。
				
			msr sctlr_el3, x0: 
				将新值(寄存器 x0 中的值)写入 SCTLR_EL3 寄存器。这将更新处理器在 EL3 异常级别的配置,如前面提到的字节序、写时不执行、堆栈对齐检查、对齐错误检查和投机存储器旁路安全行为。
				
			isb: 指令同步屏障 (Instruction Synchronization Barrier)。
				这个指令确保在执行此屏障之前的所有指令已完成,以及在执行此屏障之后的所有指令还未开始执行。
				这里使用 ISB 的目的是确保对 SCTLR_EL3 寄存器的更改在继续执行后续指令之前生效。这样可以防止在新配置生效之前执行与之相关的指令。

		2. _warm_boot_mailbox
				.if \_warm_boot_mailbox
					/* -------------------------------------------------------------
					 * This code will be executed for both warm and cold resets.
					 * Now is the time to distinguish between the two.
					 * Query the platform entrypoint address and if it is not zero
					 * then it means it is a warm boot so jump to this address.
					 * -------------------------------------------------------------
					 */
					bl	plat_get_my_entrypoint  
					cbz (Compare and Branch on Zero)一种条件分支指令。它用于在寄存器上进行比较,如果该寄存器的值为零,则跳转到指定的标签或地址。
					cbz	x0, do_cold_boot   -- 如果x0为0,那就跳到 do_cold_boot
					br	x0                 -- 如果x0不为0,那就跳到x0的地址

				do_cold_boot:
				.endif /* _warm_boot_mailbox */

				_warm_boot_mailbox =!PROGRAMMABLE_RESET_ADDRESS, PROGRAMMABLE_RESET_ADDRESS表示系统的复位地址,
				如果该值为零,则表示系统可能是冷启动,执行_warm_boot_mailbox的代码;如果不为零,则表示系统是热启动。
				如果 PROGRAMMABLE_RESET_ADDRESS =0,_warm_boot_mailbox 宏被定义,系统有可能是冷启动,但还需进一步判断,即执行以下操作:
					调用 plat_get_my_entrypoint 函数以查询平台的入口点地址, 这个函数有各自厂家实现。
					如果平台入口点地址不为零,则表示系统是热启动,因此控制将转移到该地址,开始热启动的处理。
					如果平台入口点地址为零,则表示系统是冷启动,因此控制将转移到 do_cold_boot 标签,开始冷启动的处理。






		
		3. _pie_fixup_size: 修复全局描述符表
				当系统进入冷启动后,如果启用了多处理器支持,则有可能需要修复一次全局描述符表。
				通常情况下,Arm 系统是不启用 PIE的。PIE 是一种特殊的内存分配方式,用于在程序运行时为可执行文件分配随机的内存位置。
				这对于防止恶意软件的攻击非常有用,但是它带来了一些性能开销。因此,通常情况下,Arm 系统不会启用 PIE。
				在安全性要求较高的场景,例如嵌入式系统或者涉及到支付或私密信息的系统中,PIE 可能会被启用。在这种情况下,下面这段代码就有用了。
				.if \_pie_fixup_size
					#if ENABLE_PIE  -- f1上面没有enable
						/*
						 * ------------------------------------------------------------
						 * If PIE is enabled fixup the Global descriptor Table only
						 * once during primary core cold boot path.
						 *
						 * Compile time base address, required for fixup, is calculated
						 * using "pie_fixup" label present within first page.
						 * ------------------------------------------------------------
						 */
						pie_fixup:  定义 pie_fixup 标签,该标签在编译时用于计算基址
							ldr	x0, =pie_fixup   --- ldr 指令将 X0 寄存器加载为 pie_fixup 标签的地址
							and	x0, x0, #~(PAGE_SIZE_MASK)  --- and 指令对 X0 寄存器的值进行掩码操作,以便将其与 PAGE_SIZE_MASK 取按位与
							mov_imm	x1, \_pie_fixup_size   --- mov_imm 指令将_pie_fixup_size加载到 X1 寄存器中。
							add	x1, x1, x0      --- add 指令将 X1 寄存器的值与 X0 寄存器的值相加,得到修复 GDT 的结束地址
							bl	fixup_gdt_reloc  --- 调用 fixup_gdt_reloc 函数,该函数将修复 GDT
					#endif /* ENABLE_PIE */
				.endif /* _pie_fixup_size */
				
				如果启用了多处理器支持,则 COLD_BOOT_SINGLE_CPU 的值为 0,如果没有启用多处理器支持,则 COLD_BOOT_SINGLE_CPU 的值为 1。
				如果是热启动,则不需要修复全局描述符表(GDT),因为它已经被修复过一次了。在热启动中,内存布局和系统状态都是保持不变的,因此不需要再次修复。
				相反,在冷启动中,内存布局和系统状态都是全新的,因此需要修复一次 GDT。

			
		4. 设置异常向量
				/* ---------------------------------------------------------------------
				 * Set the exception vectors.
				 * ---------------------------------------------------------------------
				 */
				 参数传进来的 runtime_exceptions, bl1、bl2、bl31都有自己的函数。
				 
				adr	x0, \_exception_vectors   -- 将异常向量表的地址存储到寄存器 x0 中
				msr	vbar_el3, x0  -- 将寄存器 x0 的值存储到 vbar_el3 寄存器中,该寄存器是 EL3 的异常向量表寄存器(Exception Vector Base Address Register)
				isb

				/* ---------------------------------------------------------------------
				 * It is a cold boot.
				 * Perform any processor specific actions upon reset e.g. cache, TLB
				 * invalidations etc.
				 * ---------------------------------------------------------------------
				 */
				bl	reset_handler

				补充初始化EL3寄存器(_init_sctlr=1时已经设置过一部分)
				el3_arch_init_common --- el3_arch_init_common宏与el3_entrypoint_common宏定义在同一个文件

		5. 主CPU或辅助CPU的冷启动(参数:_secondary_cold_boot=!COLD_BOOT_SINGLE_CPU)
			f1上不走这里,_secondary_cold_boot为0
			.if \_secondary_cold_boot
				/* -------------------------------------------------------------
				 * Check if this is a primary or secondary CPU cold boot.
				 * The primary CPU will set up the platform while the
				 * secondaries are placed in a platform-specific state until the
				 * primary CPU performs the necessary actions to bring them out
				 * of that state and allows entry into the OS.
				 * -------------------------------------------------------------
				 */
				bl	plat_is_my_cpu_primary   -- 需要的话要厂商自己实现来确定是否是主core
				cbnz	w0, do_primary_cold_boot

				/* This is a cold boot on a secondary CPU */
				bl	plat_secondary_cold_boot_setup
				/* plat_secondary_cold_boot_setup() is not supposed to return */
				bl	el3_panic

			do_primary_cold_boot:
			.endif /* _secondary_cold_boot */
		
			首先使用 plat_is_my_cpu_primary 函数来检查处理器是否为主处理器。
			如果是主处理器(返回非0),则跳转到 do_primary_cold_boot 标号,程序继续执行。
			否则,表示它是次要处理器的冷启动,将调用 plat_secondary_cold_boot_setup 函数来初始化辅助cpu,
			如果这个函数执行不出现错误,会执行辅助cpu后期操作,不再返回。
			如果返回,则说明系统出现错误,调用 el3_panic 函数抛出异常错误。

		6. 主CPU初始化内存(参数:_init_memory=1)
			/* ---------------------------------------------------------------------
			 * Initialize memory now. Secondary CPU initialization won't get to this
			 * point.
			 * ---------------------------------------------------------------------
			 */

			.if \_init_memory
				bl	platform_mem_init
			.endif /* _init_memory */
			platform_mem_init函数的实现与具体的硬件平台有关。
			每个硬件平台的内存构造和资源可能不同,因此 platform_mem_init函数的实现也可能不同。
			例如,在一个平台上,platform_mem_init 函数可能需要配置内存分配池,并将某些内存区域映射到系统存储器中,
			而在另一个平台上,platform_mem_init 函数可能仅需要配置缓存和数据换出。

		
		7. 初始化C运行时环境(参数:_init_c_runtime=1)
	
				/* ---------------------------------------------------------------------
				 * Init C runtime environment:
				 *   - Zero-initialise the NOBITS sections. There are 2 of them:
				 *       - the .bss section;
				 *       - the coherent memory section (if any).
				 *   - Relocate the data section from ROM to RAM, if required.
				 * ---------------------------------------------------------------------
				 */
				.if \_init_c_runtime
			#if defined(IMAGE_BL31) || (defined(IMAGE_BL2) && BL2_AT_EL3 && BL2_INV_DCACHE)
					/* -------------------------------------------------------------
					 * Invalidate the RW memory used by the BL31 image. This
					 * includes the data and NOBITS sections. This is done to
					 * safeguard against possible corruption of this memory by
					 * dirty cache lines in a system cache as a result of use by
					 * an earlier boot loader stage.
					 * -------------------------------------------------------------
					 */
					adrp	x0, __RW_START__
					add	x0, x0, :lo12:__RW_START__
					adrp	x1, __RW_END__
					add	x1, x1, :lo12:__RW_END__
					sub	x1, x1, x0
					bl	inv_dcache_range
			#if defined(IMAGE_BL31) && SEPARATE_NOBITS_REGION
					adrp	x0, __NOBITS_START__
					add	x0, x0, :lo12:__NOBITS_START__
					adrp	x1, __NOBITS_END__
					add	x1, x1, :lo12:__NOBITS_END__
					sub	x1, x1, x0
					bl	inv_dcache_range
			#endif
			#endif
					adrp	x0, __BSS_START__
					add	x0, x0, :lo12:__BSS_START__

					adrp	x1, __BSS_END__
					add	x1, x1, :lo12:__BSS_END__
					sub	x1, x1, x0
					bl	zeromem

			#if USE_COHERENT_MEM
					adrp	x0, __COHERENT_RAM_START__
					add	x0, x0, :lo12:__COHERENT_RAM_START__
					adrp	x1, __COHERENT_RAM_END_UNALIGNED__
					add	x1, x1, :lo12: __COHERENT_RAM_END_UNALIGNED__
					sub	x1, x1, x0
					bl	zeromem
			#endif

			#if defined(IMAGE_BL1) || (defined(IMAGE_BL2) && BL2_AT_EL3 && BL2_IN_XIP_MEM)
					adrp	x0, __DATA_RAM_START__
					add	x0, x0, :lo12:__DATA_RAM_START__
					adrp	x1, __DATA_ROM_START__
					add	x1, x1, :lo12:__DATA_ROM_START__
					adrp	x2, __DATA_RAM_END__
					add	x2, x2, :lo12:__DATA_RAM_END__
					sub	x2, x2, x0
					bl	memcpy16
			#endif
				.endif /* _init_c_runtime */	

				这段代码主要负责初始化 C 运行时环境,具体包括以下操作:
					零初始化 NOBITS 区段:这里有两个这样的区段,一个是 .bss 区段,另一个是一致性内存区段(如果有的话,我们这里没有)。
					如果需要(我们不需要),将数据区段从 ROM 拷贝到 RAM。
					我们再次逐段分析代码:
						首先,根据条件编译,代码会检查是否需要执行这些操作,例如在 BL31 镜像或者 BL2 镜像的特定条件下。
						接下来,代码会检查是否启用了 PIE (Position Independent Executable)。
						PIE 是一种可执行文件格式,可以让代码在内存中的任何位置运行,而不需要重新编译。
						根据 PIE 的启用情况,代码会计算数据区段的起始和结束地址。
						然后,代码会调用 inv_dcache_range 函数,以确保在系统缓存中没有残留的脏缓存行,防止之前的引导阶段对当前阶段的内存造成污染。
						接下来,代码会计算 .bss 区段的起始和结束地址,然后调用 zeromem 函数将 .bss 区段的内容置零。.bss 区段通常包含了程序中未初始化的全局变量。
						如果启用了一致性内存,代码会计算一致性内存区段的起始和结束地址,并调用 zeromem 函数将这部分内存置零。
						接着,对于 BL1 映像或满足特定条件的 BL2 映像,代码会计算数据区段在 RAM 和 ROM 中的起始和结束地址。然后调用 memcpy16 函数,将数据区段从 ROM 拷贝到 RAM。
						这个过程是为了确保在 RAM 中可以访问并修改数据区段的内容。
				这段代码在启动过程的早期执行,为 TF-A 的后续阶段提供了一个正确初始化的 C 运行时环境。这有助于确保程序在运行时可以正确地访问全局变量和一致性内存。
								

		8.  设置堆栈指针寄存器SP_EL0
				/* ---------------------------------------------------------------------
				 * Use SP_EL0 for the C runtime stack.
				 * ---------------------------------------------------------------------
				 */
				msr	spsel, #0
				执行msr spsel, #0指令的目的是将SPSel(栈指针选择器)寄存器的值初始化为0。
				在ARMv8-A架构中,SPSel寄存器用于选择当前异常级别下要使用的栈指针寄存器。
				当SPSel的值为0时,处理器使用SP_ELx寄存器(如SP_EL1、SP_EL2、SP_EL3)作为栈指针;
				当SPSel的值为1时,处理器使用SP_EL0寄存器作为栈指针。
				需要注意的是,SPSel寄存器仅在EL1下有效,用于在EL1和EL0之间切换栈指针。在EL2和EL3中,SPSel寄存器始终为0。
				通过执行msr spsel, #0指令,确保了处理器在EL1下使用SP_EL1寄存器作为栈指针,而不是SP_EL0寄存器。
				在EL3下(例如,在TF-A中),由于SPSel寄存器始终为0,处理器会使用SP_EL3寄存器作为栈指针。
				msr spsel, #0确保了在TF-A的执行过程中,栈空间与当前的异常级别和特权级别相匹配。


		9.
				/* ---------------------------------------------------------------------
				 * Allocate a stack whose memory will be marked as Normal-IS-WBWA when
				 * the MMU is enabled. There is no risk of reading stale stack memory
				 * after enabling the MMU as only the primary CPU is running at the
				 * moment.
				 * ---------------------------------------------------------------------
				 */
				 目的是为当前CPU分配一个栈内存。栈内存在启用MMU(内存管理单元)后将被标记为Normal-IS-WBWA。
				 这里的Normal-IS-WBWA表示内存类型是Normal,具有Inner Shareable属性(IS,内部可共享),并采用写回(Write-Back)和写分配(Write-Allocate)的缓存策略
				bl	plat_set_my_stack  这个由厂商实现,./open_atf/plat/common/aarch64/platform_mp_stack.S
					1.
							func plat_set_my_stack       
								mov	x9, x30   -- x30即LR,是返回地址寄存器,保存LR到x9中,为后续的返回做准备。
								bl 	plat_get_my_stack
								mov	sp, x0
								ret	x9   -- 返回函数调用
							endfunc plat_set_my_stack
					2.
							/* -----------------------------------------------------
							 * uintptr_t plat_get_my_stack ()
							 *
							 * For the current CPU, this function returns the stack
							 * pointer for a stack allocated in device memory.
							 * -----------------------------------------------------
							 */ 从section
						func plat_get_my_stack
							mov	x10, x30   --  保存LR到x9中,为下面的返回做准备
							get_my_mp_stack platform_normal_stacks, PLATFORM_STACK_SIZE    ---  PLATFORM_STACK_SIZE定义在platform_def.h中为0x4000 
							ret	x10
						endfunc plat_get_my_stack	
						
						【问题:】 platform_normal_stacks 是什么东西,哪里来的呢?
							declare_stack platform_normal_stacks, tzfw_normal_stacks, PLATFORM_STACK_SIZE, PLATFORM_CORE_COUNT, CACHE_WRITEBACK_GRANULE
							通过上面的宏汇编,会在编译的时候在section:tzfw_normal_stacks 中预留一段内存,用来作为各个cpu的堆栈空间。
							declare_stack:
								.macro declare_stack _name, _section, _size, _count, _align=DEFAULT_STACK_ALIGN   -- ./open_atf/include/common/asm_macros_common.S
									count_tz \_align, 0
									.if (\_align - (1 << TZ_COUNT))
									  .error "Incorrect stack alignment specified (Must be a power of 2)."
									.endif
									
									.if ((\_size & ((1 << TZ_COUNT) - 1)) <> 0)
									  .error "Stack size not correctly aligned"
									.endif
									
									.section    \_section, "aw", %nobits
									.align TZ_COUNT
										\_name:     -- 根据前面传进来的名字,定义一个变量,并给它分配size * count的内存空间,放在tzfw_normal_stacks section里面。
										.space ((\_count) * (\_size)), 0
								.endm	
							1.     
								tzfw_normal_stacks: 是一个section的名字,也就是在编译的时候会预留一段地址,专门用于堆栈中,size为PLATFORM_STACK_SIZE * core的数量,因为每个core的sp是独立的。
										#if !(defined(IMAGE_BL31) && RECLAIM_INIT_CODE)
										#define STACK_SECTION					\
											stacks (NOLOAD) : {				\
												__STACKS_START__ = .;			\
												*(tzfw_normal_stacks)			\
												__STACKS_END__ = .;			\
											}
										#endif
							2.
								PLATFORM_STACK_SIZE: 每个堆栈的大小
								PLATFORM_CORE_COUNT: cpu的个数
							
							总结:
								从上面的分析看出,创建了一个变量 platform_normal_stacks,给它申请了 PLATFORM_STACK_SIZE * PLATFORM_CORE_COUNT大小的内存空间,并放在section:tzfw_normal_stacks里面。
								专门给各个cpu做堆栈空间使用。	
							
					3. 函数 get_my_mp_stack  ---  ./open_atf/include/arch/aarch64/asm_macros.S
							/*
							 * This macro calculates the base address of the current CPU's MP stack
							 * using the plat_my_core_pos() index, the name of the stack storage
							 * and the size of each stack
							 * Out: X0 = physical address of stack base
							 * Clobber: X30, X1, X2
							 */ -- 通过调用 plat_my_core_pos 函数获取当前CPU的索引,然后使用该索引、存储堆栈的名称和每个堆栈的大小计算堆栈的基地址
							.macro get_my_mp_stack _name, _size   
								bl	plat_my_core_pos           -- 获取当前CPU的索引,应该是0、1、2、3、4、5、6....
								adrp	x2, (\_name + \_size)  --  使用adrp指令计算指向堆栈存储区域的高位地址部分,并将其加载到寄存器x2中。
								add	x2, x2, :lo12:(\_name + \_size)  -- 使用add指令合并高位地址和低位地址,以获得完整的地址。
								mov x1, #\_size  -- 将堆栈的大小存储到寄存器x1中。
								madd x0, x0, x1, x2  -- 使用madd指令计算当前CPU的MP堆栈的基地址,并将其存储到寄存器x0中, x0 = x0 * x1 + x2
										在ARMv8架构中,madd指令是一种乘加指令,用于执行两个数的乘法操作,并将结果与第三个数相加。
										madd指令是“Multiply-Add”的缩写,其一般形式和作用如下:
											madd Xd, Xn, Xm, Xa
										
											Xd:目的寄存器,用于存储操作的最终结果。
											Xn, Xm:参与乘法操作的两个源寄存器。
											Xa:在乘法结果基础上进行累加的源寄存器。
											
											指令的操作可以表示为:
												Xd = (Xn * Xm) + Xa
							.endm						
					
					4. 函数 plat_my_core_pos   ---   atf/plat/vendor/franklin/aarch64/plat_helpers.S
						func plat_my_core_pos
							mrs	x0, mpidr_el1      --- 从MPIDR_EL1(多处理器仿真器ID寄存器)中读取当前CPU的ID,并将其存储在寄存器x0中
							lsr x0, x0, #MPIDR_AFF1_SHIFT
							and x0, x0, #MPIDR_AFFLVL_MASK
							ret
						endfunc plat_my_core_pos
FPGA 学习系列(2):Verilog 语法基础 Verilog 由门级建模数据流建模行为建模门级建模(Gate Level Modeling):使用基本逻辑门描述电路,接近硬件物理结构。数据流建模(Data Flow Modeling):使用assign语句进行组合逻辑建模,类似数学方程式。行为建模(Behavioral Modeling):使用always代码块和if-elsecase等控制语句建模,抽象程度较高。Verilog 代码一般由模块(module)组成,每个模块描述一个独立的硬件功能。 阅读详情

相关推荐

Visual Studio Git 使用指南

Visual Studio 2022深度集成了Git功能,提供了直观的图形界面管理源代码。本文详细介绍了VS中Git的使用方法:从环境准备、克隆/创建仓库开始,重点讲解了"Git更改"窗口的日常操作,包括查看修改、暂存文件、提交推送等。同时涵盖了分支管理、查看历史、解决冲突等核心功能,以及储藏、重置等高级操作。文章还提供了实用技巧,如提交规范、频繁提交等最佳实践。VS的Git集成覆盖了日常开发需求,图形界面与命令行可互补使用,适合各层次开发者高效管理代码版本。

guigenyi的专栏 1569

理解 Linux 动态库加载:LD_LIBRARY_PATH

flyfish

二分掌柜的 347

用 SQL 分析不同用户群组留存率

一、前言 用户增长对企业非常重要,主要包括: 新增、留存、召回 。本文介绍一个实用的分析留存率的方法: Cohort Analysis (群组分析),APP的下载量越高并不是说产品就越好,产品若没有很好的留存,花费大量时间和资源来挖掘新客户也将是竹篮打水一场空。群组分析有利于衡量用户在每个漏斗的用户流失节点、用户流失数据,从而帮助你发现产品的用户的行为模式和用户增长点。从本质上讲,群组分析Cohort Analysis为你提供了一种数据驱动的方法,用群组分析Cohort Analysis方法,可以来帮助提

qq_39984050的博客 3172

Linux-iostat命令详解

Linux命令学习记录

m0xin的博客 120

利用树莓派制作智能音箱

利用树莓派制作智能音箱

qq_61803022的博客 1777

AI与Agent赋能下的软件测试变革:面向未来测试工程师的工作方法论

AI Agent 软件测试 测试 software testing test

测试技师的自我修养 1234

USB 技术系列 11 · USB Linux 驱动框架

本文系统解析Linux USB驱动框架,涵盖USB Core、HCD(xhci-hcd/dwc3)、类驱动(CDC/HID/Mass Storage)分层架构,详解usb_device_id匹配、probe/disconnect生命周期及Device Tree配置。结合RISC-V N300实战,揭示Host/Device双模式驱动开发核心逻辑,助力嵌入式BSP开发者掌握USB子系统全栈能力。

peter_pan_study的博客 254

别再为H5扫码头疼了!用Vue3 + vue-qrcode-reader,5分钟搞定移动端摄像头调用(附完整代码)

本文详细介绍了如何使用Vue3和vue-qrcode-reader快速实现移动端扫码功能,解决H5开发中常见的摄像头调用和兼容性问题。通过完整的代码示例和优化技巧,帮助开发者高效集成二维码识别功能,提升用户体验和开发效率。

weixin_33737134的博客 513

Linux 服务器性能排查手册(2026):CPU、内存、磁盘IO、网络四大瓶颈定位

iptables 四表五链模型、firewalld zone 信任级别、云安全组与主机防火墙的关系与优先级,避坑改了防火墙把自己挡在服务器外面的翻车事故。

2502_91473995的博客 321

解决Home Assistant中安装的Advanced SSH & Web Terminal插件打开提示“502 Bad Gateway” 且弹窗提示“加载项似乎尚未准备就绪,它可能仍在启动中“问题

  在HomeAssistant的应用商店安装了【AdvancedSSH&WebTerminal】插件,但是打开这个终端连接到HomeAssistant的时候却提示“502BadGateway”且弹窗提示“加载项似乎尚未准备就绪,它可能仍在启动中,您想重试吗?”一直无法进入HomeAssistant的系统中

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 2832

基于加速科技ST2500的SN74HC00N芯片测试计划

实际上随着工艺的进步,器件内部和引脚间的绝缘氧化膜越来越薄,导致漏电流发生的概率更大,另外制造过程中的工艺缺陷导致的桥接,异物或封装过程中造成的芯片划伤、引裂,都会造成IC的漏电流偏大,有部分产品可能会表现出漏电流偏大,但功能正常,此类产品具有潜在的可靠性问题,漏电流测试的目的就是把具有此类缺陷的产品筛选出来,避免其流到最终产品造成更大的损失。漏电流的测试方法相对简单,就是根据产品手册或测试规范对被测引脚施加额定的电压,然后测量其输入或流出的电流是否符合相应的设计规范。74HC00引脚功能如表所示。

weixin_68288412的博客 1437

Linux rm 误删文件恢复实操教程

Linux使用rm删除文件时,并不会直接抹除磁盘上的数据,只是移除文件索引,让系统不再识别这块空间。只要没有写入新数据,原有扇区内容就会保留。新文件写入才有可能覆盖这些区域,一旦覆盖就很难找回。恢复的本质就是扫描磁盘,找到这些还存在、但索引丢失的数据条目,再把它们导出保存到其他分区。越早停止写入,恢复成功的概率越高。

海天数据恢复-winhex数据恢复教程 232

基于BenchmarkSQL的达梦数据库TPCC性能测试

中的用户和密码在达梦数据库中创建测试用户,并保证其表空间足够容纳生成的测试数据。如果开启了归档,还要考虑归档日志占用的空间。BenchmarkSQL默认不支持达梦数据库。这里我们让BenchmarkSQL把达梦数据库当作Oracle即可。Oracle数据库对应的测试配置文件为。是使用BenchmarkSQL进行性能测试的主要配置文件。是生成的测试报告,png文件是报告中包含的图片。拷贝达梦数据库驱动到BenchmarkSQL的。测试结束后,会在当前路径下生成一个以。规则命名的结果目录。

Sebastien23的博客 2635

Windows/Linux下强实时运动控制内核(二):基于Qt的Delta机械手的开发应用

强实时运动控制内核MotionRT750下Qt开发Delta机械手的完整开发流程。

weixin_57350300的博客 241

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain&#39;s apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath(&#39;DeepLearnToolbox&#39;));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand(&#39;state&#39;,0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W&#39;); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand(&#39;state&#39;,0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = &#39;sigm&#39;;%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, &#39;Too big error&#39;);```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand(&#39;state&#39;,0)sae = saesetup([784 100]);sae.ae{1}.activation_function = &#39;sigm&#39;;sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)&#39;)% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;;nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, &#39;Too big error&#39;);```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x&#39;,28,28,60000))/255;test_x = double(reshape(test_x&#39;,28,28,10000))/255;train_y = double(train_y&#39;);test_y = double(test_y&#39;);%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you&#39;ll get around 1.2% errorrand(&#39;state&#39;,0)cnn.layers = { struct(&#39;type&#39;, &#39;i&#39;) %input layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 6, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %sub sampling layer struct(&#39;type&#39;, &#39;c&#39;, &#39;outputmaps&#39;, 12, &#39;kernelsize&#39;, 5) %convolution layer struct(&#39;type&#39;, &#39;s&#39;, &#39;scale&#39;, 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, &#39;Too big error&#39;);```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, &#39;Too big error&#39;);%% ex2 neural net with L2 weight decayrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex3 neural net with dropoutrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex4 neural net with sigmoid activation functionrand(&#39;state&#39;,0)nn = nnsetup([784 100 10]);nn.activation_function = &#39;sigm&#39;; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex5 plotting functionalityrand(&#39;state&#39;,0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = &#39;softmax&#39;; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand(&#39;state&#39;,0)nn = nnsetup([784 20 10]); nn.output = &#39;softmax&#39;; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, &#39;Too big error&#39;);```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Linux epoll 一篇讲透:从 select/poll 到红黑树、LT/ET,再到完整 TCP 服务器

一万个客户端已经连上服务器,眼下只有十个客户端发来了数据。服务器怎样把时间花在这十个连接上?理解这个问题,就抓住了 epoll 的主线。接下来,我们沿着“为什么需要它 → 接口怎么用 → 内核如何实现 → LT/ET 怎么写 → 完整服务器怎么运行”的顺序,把知识连起来。**阅读基础:**了解socketbindlistenaccept的基本用途即可。本文示例面向 Linux,服务器使用 C++17,实验脚本使用 Python 3。

2601_94861874的博客 183

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Linux 网络基础详解:IP、子网、路由与 ip、ss 命令实战

建库建表、增删改查、连接查询(内连左连右连)、聚合函数、权限控制 GRANT/REVOKE,每条命令配可运行示例,零基础也能跟着敲会 SQL 基础。

2502_91473995的博客 617

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Linux、Shell、MySQL 测试卷逐题细讲(下篇:MySQL)

本文为Linux、Shell、MySQL测试卷下半段MySQL部分逐题讲解,包含填空、选择与SQL分析三类题型。填空题覆盖COUNT、HAVING、DISTINCT、事务、约束、索引等核心语法;选择题辨析存储过程、索引类型、ACID、存储引擎、用户管理等考点。SQL大题围绕员工、部门、职位、地点四张关联表,考察多表连接、分组聚合、自连接、相关子查询、NULL处理等高频写法,标注LEFT JOIN统计计数、NULL判断、子查询别名等易错坑点,同时梳理试卷OCR识别带来的干扰问题,重点提示SQL实操中的常见失误

dongziwen1的博客 184

深入浅出聊聊 Linux 的 PageCache 与系统极致性能

本文深入浅出解析了 Linux PageCache 的核心机制,揭示其如何通过“延迟写、预读读”提升系统性能。以监控告警为切入点,阐明 free -h 中 buff/cache 并非真正占用,真正可用内存由 available 决定。结合 Kafka、Elasticsearch、MySQL 等实战场景,说明中间件如何依赖 PageCache 实现极致性能。强调不可手动清理缓存,并建议合理调优脏页参数,避免卡顿。最终指出:内存满≠危机,而是系统高效运行的体现。

小小默:进无止境 482

Linux 下 OpenCode 接入自定义大模型 API 教程:以 DeepSeek-V4.1-Flash-1017 为例

本文介绍在Linux下通过Python脚本配置OpenCode接入自定义大模型API(以DeepSeek-V4.1-Flash-1017为例),实现永久化、非侵入式配置。通过修改~/.config/opencode/opencode.json文件,添加OpenAI兼容服务商BLSC,注册模型并设置APIKey。支持命令行测试、默认模型设定及安全权限控制,操作简单且不影响原有配置。适用于各类支持OpenAI接口的自定义模型服务,提升开发灵活性与安全性。

qq_15060477的博客 128

【Linux 命令实战(06):Linux 文件权限管理,掌握 chmod、chown、chgrp】

chmod:修改文件和目录权限。chown:修改文件所有者,也可以修改所属用户组。chgrp:修改文件所属用户组。r:读取。w:写入。x:执行,对于目录则表示进入和访问能力。ugo:分别代表所有者、所属组和其他用户。我认为,权限管理最重要的不是记住755644这些数字,而是先弄清楚谁需要访问文件、需要执行什么操作,再给出恰当的权限。尤其是在测试服务器和生产服务器上,不要为了图方便直接使用chmod 777。权限开放得越大,并不代表问题解决得越好。

一些微不足道的见解、希望能帮助各位 335

【Linux 命令实战(02):文件与目录操作,掌握 pwd、ls、cd 三个核心命令】

命令作用cd /进入根目录cd ~进入当前用户家目录cd返回当前用户家目录cd …返回上一级目录cd …/…返回上两级目录cd -返回上一次所在目录进入指定目录命令核心用途常用参数pwd查看当前目录-L、-Pls查看文件和目录cd切换目录…、~、-Linux 文件系统的基本结构。绝对路径与相对路径的区别。Tab 自动补全。Shell 通配符。使用&&组合命令。这篇文章主要解决的是如何在 Linux 文件系统中快速定位目录。

一些微不足道的见解、希望能帮助各位 199

Linux篇(五)

本文系统梳理了Linux常用终端快捷键、软件包管理(yum/apt)、服务管理(systemctl)、软链接(ln)、时间与IP配置、域名解析、固定IP设置及网络命令(ping/wget/curl),涵盖日常运维核心操作,助力高效管理Linux环境。

wykyzdq的博客 202

【Linux】进程间通信的基础概念及基于匿名管道的进程池

前面我们介绍了进程的创建、退出和等待,接下来看看进程之间怎样传递数据。这篇从匿名管道入手,再用管道把父子进程联系起来,写一个简单的进程池小demo。而且我把发行版从CentOS切换成Ubuntu了,主要是CentOS已经不维护了,可惜了。。。

qeen87的博客 324

Linux的渲染与显示系统链路

Linux渲染与显示链路核心为DRM/KMS + EGL直连模式,绕过传统X11/Wayland中间层,实现低延迟、高效率的直接帧提交。相比X11的粗粒度权限控制与Wayland的合成开销,该方案链路短、资源消耗低,但仅支持单进程独占显示,缺乏窗口管理与系统级兜底。EGL作为跨平台桥梁,连接OpenGL ES与原生显示系统,通过Display、Surface、Context三要素构建渲染环境。内核层DRM管理GPU资源,KMS配置显示模式,用户态GDM负责登录会话,共同构成从硬件到应用的完整图形栈。

微小的鱼的博客 411

Linux 启动流程与故障修复:GRUB、systemd 与救援模式实战

密钥登录配置替代密码、sshd_config 禁 root 禁密码改端口、fail2ban 自动封爆破 IP、端口改不改的权衡,服务器被扫描爆破时的完整加固方案。

2502_91473995的博客 461

linux 压缩文件时间 PG TDE下 tar cfz 压缩到20% pg_basebackup 基本没有压缩

数据,这个文件的精确压缩时长为。

jnrjian的博客 154
上一篇: OpenSBI简介-4
Runner_Linux
博客等级 码龄6年 17粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值