ATF中smc指令详解

1 ATF的smc指令调用流程

在REE侧调用smc异常之后,会根据中断向量表触发cpu的同步异常sync_exception_aarch64/32,然后跳转执行到handle_sync_exception->smc_handler64/32中,最后跳转到_RT_SVC_DESCS_START_+RT_SVC_DESC_HANDLE这个工具类中执行具体的操作,最后跳转到el3_exit返回REE侧。
在这里插入图片描述

2.1 REE侧如何调用smc指令

在REE侧调用smc之前,需要对通用寄存器进行赋值传参x0-x8。然后通过 smc #0这一汇编指令进行smc调用

// 在 AArch32中只有个r0-r3才能成功的保存到寄存器中,r4-r6需要被压入到堆栈中保存
func smc
	/*
	 * For AArch32 only r0-r3 will be in the registers;
	 * rest r4-r6 will be pushed on to the stack. So here, we'll
	 * have to load them from the stack to registers r4-r6 explicitly.
	 * Clobbers: r4-r6
	 */
	ldm	sp, {r4, r5, r6}
	smc	#0
endfunc smc
//在 AArch64中则不需要
func smc
	smc	#0
endfunc smc

2.2 smc同步异常

在运行时el3采用的异常向量表是runtime_exceptions

(1)首先看两个宏定义


.macro vector_base  label	
	.section .vectors, "ax"	//指定代码段必须存放在.vectors段里, “ax”表示该段可执行并且可‘a’读和可‘x’执行
	.align 11, 0			//地址方式对齐11 其余字节用0填充
	\label:
	.endm
	
.macro vector_entry  label	//label为标号以冒号结尾
	.section .vectors, "ax"//指定代码段必须存放在.vectors段里, “ax”表示该段可执行并且可‘a’读和可‘x’执行
	.align 7, 0				//地址方式对齐7
	\label:
	.endm

.macro check_vector_size since
	  .if (. - \since) > (32 * 4)	//这个.应该是当前位置 - 段的开头地址 如果大于 32条指令
	    .error "Vector exceeds 32 instructions"		//向量超过32条指令
	  .endif
	.endm
.macro no_ret _func:req, skip_nop=0	//其实意思就是bl _func
	bl	\_func
//这里省略了其他的异常
vector_base runtime_exceptions	//定义 .vectors
vector_entry sync_exception_aarch64
	handle_sync_exception
	check_vector_size sync_exception_aarch64
vector_entry sync_exception_aarch32
	handle_sync_exception
	check_vector_size sync_exception_aarch32

这段代码等价于

.section .vectors, "ax"	//指定代码段必须存放在.vectors段里, “ax”表示该段可执行并且可‘a’读和可‘x’执行
.align 11, 0			//地址方式对齐11 其余字节用0填充
runtime_exceptions:
	.section .vectors, "ax"//指定代码段必须存放在.vectors段里, “ax”表示该段可执行并且可‘a’读和可‘x’执行
	.align 7, 0				//地址方式对齐7
	sync_exception_aarch64:
		handle_sync_exception
		.if (. - serror_aarch64) > (32 * 4)	//这个.应该是当前位置 - 段的开头地址 如果大于 32条指令
	    .error "Vector exceeds 32 instructions"		//向量超过32条指令
	    .endif
	sync_exception_aarch32
	     handle_sync_exception
		.if (. - serror_aarch64) > (32 * 4)	//这个.应该是当前位置 - 段的开头地址 如果大于 32条指令
	    .error "Vector exceeds 32 instructions"		//向量超过32条指令
	    .endif	

2.3 handle_sync_exception

这个 handle_sync_exception是一个宏定义

.macro	handle_sync_exception
	/* Enable the SError interrupt */
	msr	daifclr, #DAIF_ABT_BIT	// #define DAIF_ABT_BIT		(U(1) << 2)  将其传输到 daifclr寄存器中

	str	x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]  // CTX_GPREGS_OFFSET = 0 + #define CTX_GPREG_LR		U(0xf0) + sp 的值作为地址 存入 x30的值

#if ENABLE_RUNTIME_INSTRUMENTATION
	/*
	 * Read the timestamp value and store it in per-cpu data. The value
	 * will be extracted from per-cpu data by the C level SMC handler and
	 * saved to the PMF timestamp region.
	 */
	mrs	x30, cntpct_el0 //将cntpct_el0 存入x30
	str	x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]	//保存x29的值到堆栈
	mrs	x29, tpidr_el3	//将tpidr_el3存入x29
	str	x30, [x29, #CPU_DATA_PMF_TS0_OFFSET]	//将 cntpct_el0 指写入到tpidr_el3中
	ldr	x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X29]	//获取x29的值
#endif

	mrs	x30, esr_el3	//将esr_el3存入x30
	ubfx	x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH	//#define ESR_EC_SHIFT			U(26) #define ESR_EC_LENGTH			U(6)	相当于 保留 x30的bit[31-26]并将这几位提到bit[6-0]

	/* Handle SMC exceptions separately from other synchronous exceptions */
	cmp	x30, #EC_AARCH32_SMC	//#define EC_AARCH32_SMC			U(0x13)	对比
	b.eq	smc_handler32

	cmp	x30, #EC_AARCH64_SMC	//#define EC_AARCH64_SMC			U(0x17) 对比
	b.eq	smc_handler64

	/* Other kinds of synchronous exceptions are not handled */
	no_ret	report_unhandled_exception	//都不是的话跳转到这里report_unhandled_exception
	.endm

2.4 smc_handler32/64 以及report_unhandled_exception的执行

在2.3中有三种跳转选项其中smc_handler32/64能够正确触发异常,report_unhandled_exception则是错误的流程
这个函数主要是存储x4-x18寄存器的值,并通过x0也就是smc_id判断该指令是smc_handler32还是smc_handler64,然后进入到rt_svc_descs这个工具结构体中,执行具体的指令。

//report_unhandled_exception 这里不详细研究
func report_unhandled_exception
	prepare_crash_buf_save_x0_x1	//保存
	adr	x0, excpt_msg
	mov	sp, x0
	/* This call will not return */
	b	do_crash_reporting
endfunc report_unhandled_exception
//smc_handler32/64
smc_handler32:

	tbnz	x0, #FUNCID_CC_SHIFT, smc_prohibited	//  #define FUNCID_CC_SHIFT	U(30) 意思是 x0的第30位若不为0 跳转到smc_prohibited这个函数,x0一般用来做smc_id 第三十位为1时代表时AArch32指令
	stp	x8, x9, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8]
	stp	x10, x11, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10]
	stp	x12, x13, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12]
	stp	x14, x15, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14]
	stp	x16, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16]	//保存现场,将寄存器的值压入堆栈
smc_handler64:	//不知道为什么AArch64指令不保存上述寄存器的值
	stp	x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]	
	stp	x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]	//保存现场,将寄存器的值压入堆栈
		/* Save rest of the gpregs and sp_el0*/
	save_x18_to_x29_sp_el0	//保存x18-x29到sp栈中后面有代码
	mov	x5, xzr	//xzr存入x5
	mov	x6, sp	//将sp指针存入x6
	/* Get the unique owning entity number */ // #define FUNCID_OEN_WIDTH		U(6) #define FUNCID_OEN_SHIFT		U(24) #define FUNCID_OEN_SHIFT		U(24)  #define FUNCID_TYPE_WIDTH		U(1)
	ubfx	x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH	//x0为smd_id 指令的bit[24-30]为OEN 存入到x16中
	ubfx	x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH	//x0为smd_id  x15 存储着 0或者1 代表是AArch32指令还是AArch64指令
	orr	x16, x16, x15, lsl #FUNCID_OEN_WIDTH	//将x15左移6为存入到x16中
	adr	x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)	//读取KEEP(*(rt_svc_descs))指令的地址
	adr	x14, rt_svc_descs_indices	//读取rt_svc_descs_indices这个描述符索引
	ldrb	w15, [x14, x16]	//将地址为x14+x16的数据读入到w15,并清除w15的高24位 这里没明白啥意思
	ldr	x12, [x6, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]	//x6之前是指针的地址 在加上el3环境的偏移地址和其sp的所在地址,可以将CTX_RUNTIME_SP的sp地址给x12
	tbnz	w15, 7, smc_unknown	//如果 w15的第7位不是0,则进入到smc_unknown	
	msr	spsel, #0	//切换到SP_EL0 spsel = 0
	//* Get the descriptor using the index  x11 = (base + off), x15 = index  handler = (base + off) + (index << log2(size)) 这里主要是算出异常要跳转的c函数入口 
	lsl	w10, w15, #RT_SVC_SIZE_LOG2	//#define RT_SVC_SIZE_LOG2	5 w15左移5位给w10
	ldr	x15, [x11, w10, uxtw]//x11是KEEP(*(rt_svc_descs))指令的地址 + w10
	//保存现场
	mrs	x16, spsr_el3	//x16 = spsr_el3
	mrs	x17, elr_el3	//x17 = elr_el3	
	mrs	x18, scr_el3	//x18 = scr_el3	
	stp	x16, x17, [x6, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]	//x6为sp指针地址将数据存入对应的堆栈位置
	str	x18, [x6, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]	//取这个安全寄存器的地址
	bfi	x7, x18, #0, #1	//这个跟SCR_EL3的安位有关
	mov	sp, x12	//没看懂着啥操作
	blr	x15	//跳转执行rt_svc_descs结构体的成员地址
	b	el3_exit	//退出安全环境
//这里是__RT_SVC_DESCS_START__ 和__RT_SVC_DESCS_END__ 的作用 ld文件里的应该就是存储指针的意思吧
        __RT_SVC_DESCS_START__ = .;
        KEEP(*(rt_svc_descs))//
        __RT_SVC_DESCS_END__ = .;
.macro save_x18_to_x29_sp_el0
stp	x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18]
stp	x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20]
stp	x22, x23, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22]
stp	x24, x25, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24]
stp	x26, x27, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26]
stp	x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
mrs	x18, sp_el0
str	x18, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_SP_EL0]
.endm
//smc_unknown:
smc_unknown:
	/*
		恢复x4-x18寄存器,返回到REE侧
	 */
	mov	w0, #SMC_UNK
	b	restore_gp_registers_callee_eret

2.5 std_svc_smc_handler (这只是调用psic的smc_handler)

在2.4 中最后跳转到x15寄存器所存储的地址

ldr x15 =  [x11, w10, uxtw]//x11 是(__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)	//RT_SVC_DESC_HANDLE = 24 (也就是sizeof(rt_svc_desc_t) = 24)
adr	x11, (__RT_SVC_DESCS_START__ + RT_SVC_DESC_HANDLE)	//读取KEEP(*(rt_svc_descs))指令的地址
lsl	w10, w15, #RT_SVC_SIZE_LOG2	//#define RT_SVC_SIZE_LOG2	5 w15左移5位给w10
ldrb	w15, [x14, x16]	//将地址为x14+x16的数据读入到w15,并清除w15的高24位 这里没明白啥意思
adr	x14, rt_svc_descs_indices	//读取rt_svc_descs_indices这个描述符索引
ubfx	x16, x0, #FUNCID_OEN_SHIFT, #FUNCID_OEN_WIDTH	//x0为smd_id 指令的bit[24-30]为OEN 存入到x16中
orr	x16, x16, x15, lsl #FUNCID_OEN_WIDTH	//将x15左移6为存入到x16中
ubfx	x15, x0, #FUNCID_TYPE_SHIFT, #FUNCID_TYPE_WIDTH	//x0为smd_id  x15 存储着 0或者1 代表是AArch32指令还是AArch64指令

应该就是跳入了__RT_SVC_DESCS_这个的对应函数地址

uint8_t rt_svc_descs_indices[MAX_RT_SVCS];	//MAX_RT_SVCS = 128 代表最多有128个服务 它存储着服务id
static rt_svc_desc_t *rt_svc_descs;	

其中 rt_svc_desc_t 的成员及初始化参数为

typedef struct rt_svc_desc {
	uint8_t start_oen;	//该服务的起始编号
	uint8_t end_oen;	//该服务的末尾编号 end_oen 和 start_oen可以相同 只是在在程序上能看出这一类的服务结尾是什么编号
	uint8_t call_type;	//异常种类 分为SMC_TYPE_YIELD,SMC_TYPE_STD,SMC_TYPE_FAST
	
	const char *name;	//异常名字
	rt_svc_init_t init;	//异常函数初始化地址
	rt_svc_handle_t handle;	//异常处理函数句柄
} rt_svc_desc_t;

在其他博主的文章中搜索到是跳入了std_svc_smc_handler这个函数
首先通过上述函数了解到 只有初始的x0-x3的值没有压入到中断也就是只能传输过去smc_fid、x1、x2、x3这四个值

uintptr_t std_svc_smc_handler(uint32_t smc_fid,
			     u_register_t x1,
			     u_register_t x2,
			     u_register_t x3,
			     u_register_t x4,
			     void *cookie,
			     void *handle,	//这里的handle正好对应了之前REE侧sp堆栈的值
			     u_register_t flags)
{
	/*
	 * Dispatch PSCI calls to PSCI SMC handler and return its return
	 * value
	 */
#ifdef ENABLE_QOS_SETTING	//这里不用管
	unsigned long qos_ret = 0;
#endif

	if (is_psci_fid(smc_fid)) {	//根据smc_fid的值判断是否是psci的smv指令 smc_fid & 0xffe0 == 0 所以判断 psci的smc_fid 在 0x0000 - 0x001f
		uint64_t ret;

#if ENABLE_RUNTIME_INSTRUMENTATION	//应该是时间戳上报,没啥用,没有被使能
		PMF_WRITE_TIMESTAMP(rt_instr_svc,
		    RT_INSTR_ENTER_PSCI,
		    PMF_CACHE_MAINT,
		    get_cpu_data(cpu_data_pmf_ts[CPU_DATA_PMF_TS0_IDX]));
#endif
		ret = psci_smc_handler(smc_fid, x1, x2, x3, x4,
		    cookie, handle, flags);//这里直接调用

#if ENABLE_RUNTIME_INSTRUMENTATION	//同理时间抽,看这个handle的运行时间应该是
		PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
		    RT_INSTR_EXIT_PSCI,
		    PMF_NO_CACHE_MAINT);
#endif
		SMC_RET1(handle, ret);	//将返回值存入handle的r0中
	}

	switch (smc_fid) {	//几种其他的smc_fid指令
	case ARM_STD_SVC_CALL_COUNT:

		SMC_RET1(handle, PSCI_NUM_CALLS);	//这个就是将PSCI_NUM_CALLS的数据存储到REE侧的r0中

	case ARM_STD_SVC_UID:
		/* Return UID to the caller */
		SMC_UUID_RET(handle, arm_svc_uid);

	case ARM_STD_SVC_VERSION:
		/* Return the version of current implementation */
		SMC_RET2(handle, STD_SVC_VERSION_MAJOR, STD_SVC_VERSION_MINOR);

#ifdef ENABLE_QOS_SETTING	//没用到
	case SPRD_QOS_READ:
		qos_ret= qos_register_read(x1);
		SMC_RET1(handle, qos_ret);

	case SPRD_QOS_WRITE:
		qos_ret = qos_register_write(x1, x2);
		SMC_RET1(handle, qos_ret);
#endif

	default:	//都不是返回 SMC_UNK 0xffffffff
		WARN("Unimplemented Standard Service Call: 0x%x \n", smc_fid);
		SMC_RET1(handle, SMC_UNK);
	}
}

从这个函数中可以看出smc现在只挂载了psci相关的指令其他的都没有涉及

2.6 el3_exit

执行完std_svc_smc_handler 之后又回到原先的汇编函数接下来执行el3_exit

func el3_exit
	mov	x17, sp		//将sp的值给x17
	msr	spsel, #1	//返回非安全将spsel置1
	str	x17, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]	//把之前REE侧的sp地址给x17
	ldr	x18, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]	//读取[sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]到x18
	ldp	x16, x17, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]	//读取[sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3] 应该是elr_e13,spsr_el3 的值给	x16, x17
	msr	scr_el3, x18	//还原REE侧的scr_el3
	msr	spsr_el3, x16	//还原REE侧的spsr_el3
	msr	elr_el3, x17	//还原REE侧的elr_el3
	b	restore_gp_registers_eret	//还原REE侧的x0-x30
endfunc el3_exit
func restore_gp_registers_eret
	ldp	x0, x1, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0]
	ldp	x2, x3, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2]
	b	restore_gp_registers_callee_eret
endfunc restore_gp_registers_eret
func restore_gp_registers_callee_eret
	ldp	x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4]
	ldp	x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6]
	ldp	x8, x9, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8]
	ldp	x10, x11, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10]
	ldp	x12, x13, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12]
	ldp	x14, x15, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14]
	ldp	x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18]
	ldp	x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20]
	ldp	x22, x23, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22]
	ldp	x24, x25, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24]
	ldp	x26, x27, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26]
	ldp	x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28]
	ldp	x30, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]	//这里将链接寄存器的值给了x17
	msr	sp_el0, x17	//sp_el0 = x17 也就是之前REE侧的pc值
	ldp	x16, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16]	//还原REE侧的x16,17
	eret
endfunc	restore_gp_registers_callee_eret
//死循环?
func eret
	eret		//这里的eret 是 arm v8中的 eret指令后面讲
endfunc eret

这里是armv8手册中用于描述eret的语句
在这里插入图片描述
使用ELR和SPSR返回当前异常级别。在执行的时候,PE从SPSR恢复PSTATE,和分支到ELR里的地址。
PE检查SPSR的当前异常级别是否存在非法返回事件,
如果在EL0中执行,ERET将导致未定义指令异常。
大概意思就是回到REE侧了

3 smc指令如何书写

从之前std_svc_smc_handler函数中的handle对应REE侧sp来说,程序根据SMC_RET1、SMC_RET2等参数可以将之前堆栈的中值改变,从而影响REE侧返回的x0-x12,以起堆栈中所存储的其他寄存器。
所以 REE侧触发smc #0指令时,x0-x3的值是有用的。x0代表smc_id x1-x3代表其他需要传输的形参。返回REE侧时能都返回的数据可通过handle存储到x0-x12中。

4 bl31启动的runtime_svc_init函数

在bl31中会执行runtime_svc_init函数,该函数会调用注册到EL3中所有服务的初始化函数,其中有一个服务项就是TEE服务,该服务项的初始化函数会将TEE OS的初始化函数赋值给bl32_init变量,当所有服务项执行完初始化后,在bl31中会调用b32_init执行的函数来跳转到TEE OS中并开始执行TEE OS的启动。

void bl31_main(void)
{
	NOTICE("BL31: %s\n", version_string);
	NOTICE("BL31: %s\n", build_message);
	bl31_platform_setup();	//通用和安全时钟初始化,其他芯片相关功能初始化
	bl31_lib_init();	//空函数
	INFO("BL31: Initializing runtime services\n");
	runtime_svc_init();	//重点 下面展开分析
	if (bl32_init) {	
		INFO("BL31: Initializing BL32\n");
		(*bl32_init)();
	}
	bl31_prepare_next_image_entry();	//加载下一阶段的入口地址
	console_flush();	//控制台刷新
	bl31_plat_runtime_setup();	//空函数
}
//注册smc指令相关的服务
void runtime_svc_init(void)
{
	int rc = 0;
	unsigned int index, start_idx, end_idx;

	/* Assert the number of descriptors detected are less than maximum indices */
	assert((RT_SVC_DESCS_END >= RT_SVC_DESCS_START) &&
			(RT_SVC_DECS_NUM < MAX_RT_SVCS));	//这句话表明 RT_SVC_DECS_NUM时当前加载的服务数量,define RT_SVC_DECS_NUM		((RT_SVC_DESCS_END - RT_SVC_DESCS_START)\
					/ sizeof(rt_svc_desc_t))

	if (RT_SVC_DECS_NUM == 0)	//如果没有服务要注册
		return;
	memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));	//初始化rt_svc_descs_indices

	rt_svc_descs = (rt_svc_desc_t *) 	RT_SVC_DESCS_START;	//建立一个注册表结构体
	for (index = 0; index < RT_SVC_DECS_NUM; index++) {
		rt_svc_desc_t *service = &rt_svc_descs[index];
		rc = validate_rt_svc_desc(service);	//判断每一个服务的各项参数是否正确
		if (rc) {
			ERROR("Invalid runtime service descriptor %p\n",
				(void *) service);
			panic();	//不正确
		}
		if (service->init) {	//该服务是否需要初始化
			rc = service->init();	//进行初始化
			if (rc) {	//初始化是否成功
				ERROR("Error initializing runtime service %s\n",
						service->name);
				continue;
			}
		}
		start_idx = get_unique_oen(rt_svc_descs[index].start_oen,
				service->call_type);	//八位的id号
		assert(start_idx < MAX_RT_SVCS);
		end_idx = get_unique_oen(rt_svc_descs[index].end_oen,
				service->call_type);	//八位的id号
		assert(end_idx < MAX_RT_SVCS);
		for (; start_idx <= end_idx; start_idx++)
			rt_svc_descs_indices[start_idx] = index;//证明可以根据rt_svc_descs_indices[?]的值找到其对应的rt_svc_descs[index]中index值
	}
}

具体注册宏指令

#define DECLARE_RT_SVC(_name, _start, _end, _type, _setup, _smch) \
	static const rt_svc_desc_t __svc_desc_ ## _name \
		__section("rt_svc_descs") __used = { \
			.start_oen = _start, \
			.end_oen = _end, \
			.call_type = _type, \
			.name = #_name, \
			.init = _setup, \
			.handle = _smch }
//其中__setion("rt_svc_descs")的意思就时注册到rt_svc_descs段中

然后添加服务时只需要调用这个宏指令就可以了

DECLARE_RT_SVC(
		std_svc,

		OEN_STD_START,
		OEN_STD_END,
		SMC_TYPE_FAST,
		std_svc_setup,
		std_svc_smc_handler
);
这个的意思就是注册
static const rt_svc_desc_t __svc_desc_std_svc服务。其服务id为SMC_TYPE_FAST << 6 + OEN_STD_START,结束服务的id为SMC_TYPE_FAST << 6 + OEN_STD_END
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值