Google TEE环境trusty分析
Google TEE环境trusty分析
- 概要
- LK初始化钩子
- 初始化过程
- 初始化cpu
- 钩子描述符
- 初始化方法
- Trusty App
- 数据结构
- 链接
- 加载
- 执行
- 线程间通信
- trusty app局部存储
- trusty app的消息处理
- 轮询事件
- 轮询事件绑定到trusty app
- uctx
- IPC
- 系统调用
- 异常框架
- 系统调用声明
- 用户空间处理
- 与Monitor通信
- SMC服务类型描述
- 服务处理
- 服务注册
- TOS与服务接口
- fastcall处理
- stdcall处理
概要
源码获取
repo init -u https://android.googlesource.com/trusty/manifest
repo sync
编译
# 工具链设置
export ARCH_arm64_TOOLCHAIN_PREFIX=`pwd`/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8/bin/aarch64-linux-android-
export ARCH_arm_TOOLCHAIN_PREFIX=`pwd`/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
# 编译
make -j24 generic-arm64
LK初始化钩子
little kernel为了方便初始化过程,创造了一种初始化方法。
具体实现位于external/lk/include/lk/init.h
、external/lk/top/init.c
中
初始化过程
系统把初始化过程分步,通过初始化等级描述,值越小越早执行。初始化等级通过枚举定于
enum lk_init_level {
LK_INIT_LEVEL_EARLIEST = 1,
LK_INIT_LEVEL_ARCH_EARLY = 0x10000,
LK_INIT_LEVEL_PLATFORM_EARLY = 0x20000,
LK_INIT_LEVEL_TARGET_EARLY = 0x30000,
LK_INIT_LEVEL_HEAP = 0x40000,
LK_INIT_LEVEL_VM = 0x50000,
LK_INIT_LEVEL_KERNEL = 0x60000,
LK_INIT_LEVEL_THREADING = 0x70000,
LK_INIT_LEVEL_ARCH = 0x80000,
LK_INIT_LEVEL_PLATFORM = 0x90000,
LK_INIT_LEVEL_TARGET = 0xa0000,
LK_INIT_LEVEL_APPS = 0xb0000,
LK_INIT_LEVEL_LAST = UINT_MAX,
};
初始化cpu
并把初始化钩子按要执行的处理器分类,类别通过枚举定义
/* 处理器标示 */
enum lk_init_flags {
LK_INIT_FLAG_PRIMARY_CPU = 0x1,/* 主处理器 */
LK_INIT_FLAG_SECONDARY_CPUS = 0x2,/* 次处理器 */
/* 主次处理器都执行 */
LK_INIT_FLAG_ALL_CPUS = LK_INIT_FLAG_PRIMARY_CPU | LK_INIT_FLAG_SECONDARY_CPUS,
LK_INIT_FLAG_CPU_SUSPEND = 0x4,/* 暂停的处理器 */
LK_INIT_FLAG_CPU_RESUME = 0x8,/* 恢复执行的处理器 */
};
钩子描述符
通过一个结构体记录钩子信息
/* 钩子描述符 */
struct lk_init_struct {
uint level; /* 初始化顺序(数字越小执行越早) */
uint flags; /* 指定此钩子函数由哪些cpu执行 */
lk_init_hook hook; /* 钩子函数 */
const char *name; /* 名字 */
};
并定义如下辅助宏,用于实现一个钩子描述符
#define LK_INIT_HOOK_FLAGS(_name, _hook, _level, _flags) \
const struct lk_init_struct _init_struct_##_name __SECTION(".lk_init") = { \
.level = _level, \
.flags = _flags, \
.hook = _hook, \
.name = #_name, \
};
#define LK_INIT_HOOK(_name, _hook, _level) \
LK_INIT_HOOK_FLAGS(_name, _hook, _level, LK_INIT_FLAG_PRIMARY_CPU)
以上宏把钩子描述符放到端**.lk_init段中,配合链接脚本定义.lk_init**段头尾符号,便于程序中访问钩子描述符
初始化方法
通过函数lk_init_level函数执行指定的初始化
/* 从链接脚本中导出的.lk_init段的头尾 */
extern const struct lk_init_struct __lk_init[];
extern const struct lk_init_struct __lk_init_end[];
void lk_init_level(enum lk_init_flags required_flag,/* 要执行钩子的cpu */
uint start_level, uint stop_level/* 初始化等级[start_level,stop_level] */)
{
LTRACEF("flags %#x, start_level %#x, stop_level %#x\n",
required_flag, start_level, stop_level);
ASSERT(start_level > 0);
uint last_called_level = start_level - 1;/* 上一次执行钩子的等级 */
const struct lk_init_struct *last = NULL;/* 记录上一次处理的钩子描述符 */
for (;;) {/* 查找符号的钩子描述符并执行 */
LTRACEF("last %p, last_called_level %#x\n", last, last_called_level);
const struct lk_init_struct *found = NULL;/* 记录查找到的节点 */
bool seen_last = false;/* 标记上一次处理的节点有无遍列过 */
/* 找出一个节点 */
for (const struct lk_init_struct *ptr = __lk_init; ptr != __lk_init_end; ptr++) {
LTRACEF("looking at %p (%s) level %#x, flags %#x, seen_last %d\n", ptr, ptr->name, ptr->level, ptr->flags, seen_last);
if (ptr == last)/* 同等级level hook遍历,此处标记上一次的节点 */
seen_last = true;
/* 确定处理器 */
if (!(ptr->flags & required_flag))
continue;
/* level必须在[last_called_level,stop_level]之间 */
if (ptr->level > stop_level)
continue;
if (ptr->level < last_called_level)
continue;
/* 优先执行低level的钩子 */
if (found && found->level <= ptr->level)
continue;
/* 找出下一个level的钩子
* 因为last_called_level初始化为start_level - 1
* 所以此处要判断level >= start_level
*/
if (ptr->level >= start_level && ptr->level > last_called_level) {
found = ptr;
continue;
}
/* 同等级level hook遍历
* last记录上一次处理的节点
* seen_last标记上一次的节点已经遍历
*/
if (ptr->level == last_called_level && ptr != last && seen_last) {
found = ptr;
break;
}
}
if (!found)/* 不能找出新的节点退出循环 */
break;
#if TRACE_INIT
if (found->level >= EARLIEST_TRACE_LEVEL) {
printf("INIT: cpu %d, calling hook %p (%s) at level %#x, flags %#x\n",
arch_curr_cpu_num(), found->hook, found->name, found->level, found->flags);
}
#endif
found->hook(found->level);/* 执行指定的hook */
/* 记录上一次少描的信息 */
last_called_level = found->level;
last = found;
}
}
Trusty App
Trusty App为TOS的应用程序,位于app目录下,一个app对应一个目录。每个app单独编译链接成一个elf,把多个elf链接在一起插入到**.data**段后。
TOS通过自己的代码加载每一个App(elf)到内存,为其创建对应的little kernel线程,此线程执行会退出异常然后执行App对应的代码。
数据结构
APP信息
通过结构体trusty_app_t描述app信息。
typedef struct trusty_app
{
vaddr_t end_bss; /* bss的结束地址 */
vaddr_t start_brk; /* 堆的起始地址 */
vaddr_t cur_brk; /* 当前堆栈的结束位置 */
vaddr_t end_brk; /* 堆的结束地址 */
/* trusty_app属性信息(栈、堆等信息) */
trusty_app_props_t props;
void *app_img; /* elf镜像的起始地址 */
uthread_t *ut; /* 用户线程标示符 */
/* app local storage */
void **als; /* app的局部存储,用于记录一些程序相关的信息 */
} trusty_app_t;
APP属性
APP属性用于配置APP的堆栈等信息,在APP开发时指定,编译链接进app的ELF文件的**.trusty_app.manifest**段。在app加载时由加载程序进行分析处理。
APP属性结构
/* 此结构体信息解析自trusty_app elf文件中的.trusty_app.manifest段
* 由load_app_config_options函数完才解析工作
*/
typedef struct
{/* trusty_app属性信息 */
uuid_t uuid;
uint32_t min_stack_size; /* 栈大小 */
uint32_t min_heap_size; /* 堆大小 */
uint32_t map_io_mem_cnt;
/* 配置信息(config_blob)有多少个字(unsigned int) */
uint32_t config_entry_cnt;
/* 指向app的elf文件中的配置信息 */
uint32_t *config_blob;
} trusty_app_props_t;
为了方便APP生成**.trusty_app.manifest段,在lib/include/trusty_app_manifest.h**中定义了一些辅助的数据结构以及宏
/* .trusty_app.manifest段的数据结构
* ---------------------------------------
* trusty app 的 uuid
* ---------------------------------------
* 标示1(一个32bit的数)
* ---------------------------------------
* 配置信息1(n个32bit的数,n根具体的标示相关)
* ---------------------------------------
* 标示2(一个32bit的数)
* ---------------------------------------
* 配置信息2(n个32bit的数,n根具体的标示相关)
* ---------------------------------------
* .
* .
* .
* ---------------------------------------
* 标示x(一个32bit的数)
* ---------------------------------------
* 配置信息x(n个32bit的数,n根具体的标示相关)
* ---------------------------------------
*/
typedef struct trusty_app_manifest {
uuid_t uuid;
uint32_t config_options[];
} trusty_app_manifest_t;
enum {
TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE = 1,/* 栈配置信息标示 */
TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE = 2, /* 堆配置信息标示 */
TRUSTY_APP_CONFIG_KEY_MAP_MEM = 3, /* 内存映射配置信息标示 */
};
/* 辅助生成栈配置信息 */
#define TRUSTY_APP_CONFIG_MIN_STACK_SIZE(sz) \
TRUSTY_APP_CONFIG_KEY_MIN_STACK_SIZE, sz
/* 辅助生成堆配置信息 */
#define TRUSTY_APP_CONFIG_MIN_HEAP_SIZE(sz) \
TRUSTY_APP_CONFIG_KEY_MIN_HEAP_SIZE, sz
/* 辅助生成内存映射配置信息 */
#define TRUSTY_APP_CONFIG_MAP_MEM(id,off,sz) \
TRUSTY_APP_CONFIG_KEY_MAP_MEM, id, off, sz
/* manifest section attributes */
/* 用于把数据放到.trusty_app.manifest段 */
#define TRUSTY_APP_MANIFEST_ATTRS \
__attribute((aligned(4))) __attribute((section(".trusty_app.manifest")))
用户线程标示
TOS(little kernel)位于S-EL1,Trusty APP位于S-EL0。TOS需要管理APP,这里通过用户线程实现。用户线程包含了little kernel的线程描述符,线程创建启动退出等都通过little kernel实现。另外实现虚拟内存管理。
typedef struct uthread
{
vaddr_t start_stack; /* 栈顶(向下增长)*/
vaddr_t entry; /* 程序入口 */
void *stack; /* 栈指针 */
struct list_node map_list; /* 虚拟内存信息 */
mutex_t mmap_lock; /* 锁,防止多个多个线程访问map_list */
vaddr_t ns_va_bottom;
/* uthread ID, unique per thread */
uint32_t id; /* 线程标示符 */
void *page_table; /* 页表 */
thread_t *thread; /* lk的线程标示 */
struct list_node uthread_list_node; /* 用户线程列表中的节点 */
void *private_data; /* 存放线程的trusty_app_t */
struct arch_uthread arch; /* 架构相关的信息 */
} uthread_t;
主要实现如下方法
/*
* name 线程名字
* entry 入口地址
* priority 执行的优先级
* stack_top 栈顶
* stack_size 栈的大小
* private_data 私有数据
*/
uthread_t *uthread_create(const char *name, vaddr_t entry, int priority,
vaddr_t stack_top, size_t stack_size, void *private_data);
/* Start the user thread */
status_t uthread_start(uthread_t *ut);
/* Exit current uthread */
void uthread_exit(int retcode) __NO_RETURN;
/* 把一段内存映射到用户线程空间 */
/*
* ut 用户线程结构体
* vaddrp 要映射到用户线程空间的虚拟内存的起始地址
* size 要映射到用户线程空间的虚拟内存的大小
* flag 要映射到用户线程空间的虚拟内存特征
* align 要映射到用户线程空间虚拟内存的对齐方式
* pfn_list
* 映射到用户线程空间虚拟内存对应的物理内存地址信息
* 一块虚拟内存可以对应多块物理内存
* 内存块数 = size/PAGE_SIZE
*/
status_t uthread_map(uthread_t *ut, vaddr_t *vaddrp, paddr_t *pfn_list,
size_t size, u_int flags, u_int align);
/* 把一块虚拟内存从用户线程空间移除 */
status_t uthread_unmap(uthread_t *ut, vaddr_t vaddr, size_t size);
/* 检测一块内存是否属于用户线程空间 */
bool uthread_is_valid_range(uthread_t *ut, vaddr_t vaddr, size_t size);
/* 拷贝内存(用户->内核) */
static inline status_t copy_from_user(void *kdest, user_addr_t usrc, size_t len)
{
return arch_copy_from_user(kdest, usrc, len);
}
/* 拷贝内存(内核->用户) */
static inline status_t copy_to_user(user_addr_t udest, const void *ksrc, size_t len)
{
return arch_copy_to_user(udest, ksrc, len);
}
/* 拷贝字符串(用户->内核) */
static inline ssize_t strncpy_from_user(char *kdest, user_addr_t usrc, size_t len)
{
/* wrapper for now, the old strncpy_from_user was closer to strlcpy than
* strncpy behaviour, but could return an unterminated string */
return arch_strlcpy_from_user(kdest, usrc, len);
}
/* 拷贝字符串(用户->内核) */
static inline ssize_t strlcpy_from_user(char *kdest, user_addr_t usrc, size_t len)
{
return arch_strlcpy_from_user(kdest, usrc, len);
}
static inline __ALWAYS_INLINE
/* 映射一段连续的物理空间到虚拟地址空间 */
status_t uthread_map_contig(uthread_t *ut, vaddr_t *vaddrp, paddr_t paddr,
size_t size, u_int flags, u_int align)
{
flags = flags | UTM_PHYS_CONTIG;
return uthread_map(ut, vaddrp, &paddr, size, flags, align);
}
/* 获取用户线程标示(通过thread_t的局部存储实现) */
static inline uthread_t *uthread_get_current(void)
{
return (uthread_t *)tls_get(TLS_ENTRY_UTHREAD);
}
/* 虚拟地址转实际地址 */
status_t uthread_virt_to_phys(uthread_t *ut, vaddr_t vaddr, paddr_t *paddr);
/* 释放虚拟内存对应的物理地址空间 */
status_t uthread_revoke_pages(uthread_t *ut, vaddr_t vaddr, size_t size);
链接
每个app通过链接脚本lk/trusty/app/trusty/user-tasks.mk
编译链接成一个elf文件,并修改段名(在名字前面加.task)
USER_TASKS_BIN := $(BUILDDIR)/user_tasks.bin
USER_TASKS_OBJ := $(BUILDDIR)/user_tasks.o
GENERATED += $(USER_TASKS_OBJ) $(USER_TASKS_BIN)
# Create a task.o from the concatenation of tasks to be made available.
# To control placement in the final LK image, the tasks .data section is
# renamed to .task.data, so it's picked up in a page-aligned part of the
# data section by the linker script (allowing tasks to run, in-place).
$(USER_TASKS_BIN): $(ALLUSER_TASK_OBJS)
@$(MKDIR)
@echo combining tasks into $@: $(ALLUSER_TASK_OBJS)
$(NOECHO)cat $(ALLUSER_TASK_OBJS) > $@
$(USER_TASKS_OBJ): $(USER_TASKS_BIN)
@$(MKDIR)
@echo generating $@
$(NOECHO)$(LD) -r -b binary -o $@ $<
$(NOECHO)$(OBJCOPY) --prefix-sections=.task $@
# Append USER_TASKS_OBJ to EXTRA_OBJ
EXTRA_OBJS += $(USER_TASKS_OBJ)
所有的app通过lk/trusty/lib/trusty/trusty_apps.ld
脚本链接到**.data段后,并以__trusty_app_start符号开始,以__trusty_app_end**结束。
SECTIONS {
.__trusty_app : {
/* pre-built secure apps get inserted here */
. = ALIGN(0x1000);
__trusty_app_start = .;
KEEP(*(.task.*))
__trusty_app_end = .;
. = ALIGN(0x1000);
}
}
INSERT AFTER .data;
加载
为了程序的加载和执行,系统通过两个初始化钩子实现
/* 主要用于加载app到内存、并为其创建对应的数据结构 */
LK_INIT_HOOK(libtrusty, trusty_init, LK_INIT_LEVEL_APPS - 1);
具体加载过程在函数trusty_app_init中实现(trusty_init直接调用了trusty_app_init)
void trusty_app_init(void)
{
trusty_app_t *trusty_app;
u_int i;
/* 通过链接脚本导出的符号获取app的起始结束地址,并计算大小 */
trusty_app_image_start = (char *)&__trusty_app_start;
trusty_app_image_end = (char *)&__trusty_app_end;
trusty_app_image_size = (trusty_app_image_end - trusty_app_image_start);
ASSERT(!((uintptr_t)trusty_app_image_start & PAGE_MASK));
/* 标记程序程序已经启动,不能再申请局部存储 */
finalize_registration();
/* 把app对应的elf文件加载到内存,并构建trusty_app数据结构记录app信息 */
trusty_app_bootloader();
for (i = 0, trusty_app = trusty_app_list; i < trusty_app_count; i++, trusty_app++) {
char name[32];
uthread_t *uthread;
int ret;
snprintf(name, sizeof(name), "trusty_app_%d_%08x-%04x-%04x",
i,
trusty_app->props.uuid.time_low,
trusty_app->props.uuid.time_mid,
trusty_app->props.uuid.time_hi_and_version);
/* entry is 0 at this point since we haven't parsed the elf hdrs
* yet */
/* 创建一个用户线程 */
Elf32_Ehdr *elf_hdr = trusty_app->app_img;
uthread = uthread_create(name, elf_hdr->e_entry,
DEFAULT_PRIORITY, TRUSTY_APP_STACK_TOP,
trusty_app->props.min_stack_size, trusty_app);
if (uthread == NULL) {
/* TODO: do better than this */
panic("allocate user thread failed\n");
}
trusty_app->ut = uthread;/* 把线程绑定到trusty app */
ret = alloc_address_map(trusty_app);/* 内存映射处理 */
if (ret != NO_ERROR) {
panic("failed to load address map\n");
}
/* attach als_cnt */
/* 给程序创建局部存储空间 */
trusty_app->als = calloc(1, als_slot_cnt * sizeof(void*));
if (!trusty_app->als) {
panic("allocate app local storage failed\n");
}
/* call all registered startup notifiers */
/* 调用初始化 */
trusty_app_notifier_t *n;
list_for_every_entry(&app_notifier_list, n, trusty_app_notifier_t, node) {
if (n->startup) {
ret = n->startup(trusty_app);
if (ret != NO_ERROR)
panic("failed (%d) to invoke startup notifier\n", ret);
}
}
}
}
执行
执行也通过初始化钩子完成
static void start_apps(uint level)
{
trusty_app_t *trusty_app;
u_int i;
int ret;
/* 此时加载操作已经完才trusty_app_list、trusty_app_count初始化已经完成 */
for (i = 0, trusty_app = trusty_app_list; i < trusty_app_count; i++, trusty_app++) {
if (trusty_app->ut->entry) {/* 确认具有入口函数 */
ret = uthread_start(trusty_app->ut);
if (ret)
panic("Cannot start Trusty app!\n");
}
}
}
/* 初始化钩子 用与启动app */
LK_INIT_HOOK(libtrusty_apps, start_apps, LK_INIT_LEVEL_APPS + 1);
执行过程与uthread实现细节相关。uthread创建时,会创建一个little kernel线程,线程入口为arch_uthread_startup,并把uthread_t绑定到线程的局部存储。arch_uthread_startup执行时会访问线程句柄存储找到uthread_t,通过uthread_t找到uthread的入口entry。在异常退出后执行对应的代码
uthread创建过程
uthread_t *uthread_create(const char *name, vaddr_t entry, int priority,
vaddr_t start_stack, size_t stack_size, void *private_data)
{
uthread_t *ut = NULL;
status_t err;
vaddr_t stack_bot;
spin_lock_saved_state_t state;
/* 为用户线程描述符申请内存空间 */
ut = (uthread_t *)calloc(1, sizeof(uthread_t));
if (!ut)
goto err_done;
/* 初始化内存映射表(链表初始化) */
list_initialize(&ut->map_list);
/* 初始化内存映射表的锁,防止多线程操作内存映射表破坏数据 */
mutex_init(&ut->mmap_lock);
ut->id = uthread_alloc_utid(); /* 为线程分配线程id */
ut->private_data = private_data;/* 初始化线程私有数据,一般为trusty_app_t */
ut->entry = entry; /* 线程入口函数 */
/* 初始化虚拟地址空间分解
大于ns_va_bottom ns(Non-Secure)内存
小于ns_va_bottom sec(Secure)内存*/
ut->ns_va_bottom = MAX_USR_VA;
/* Allocate and map in a stack region */
/* 分配栈空间 */
ut->stack = memalign(PAGE_SIZE, stack_size);
if(!ut->stack)
goto err_free_ut;
/* 把栈映射到用户线程空间 */
stack_bot = start_stack - stack_size;
err = uthread_map_contig(ut, &stack_bot, vaddr_to_paddr(ut->stack),
stack_size,
UTM_W | UTM_R | UTM_STACK | UTM_FIXED,
UT_MAP_ALIGN_4KB);
if (err)
goto err_free_ut_stack;
ut->start_stack = start_stack;
/* arch_uthread_startup在异常退出后执行uthread->entry */
ut->thread = thread_create(name,
(thread_start_routine)arch_uthread_startup,
NULL,
priority,
DEFAULT_STACK_SIZE);
if (!ut->thread)
goto err_free_ut_maps;
/* 架构相关的线程操作 */
err = arch_uthread_create(ut);
if (err)
goto err_free_ut_maps;
/* store user thread struct into TLS slot 0 */
/* lk线程局部存储中放入uthread标示 */
ut->thread->tls[TLS_ENTRY_UTHREAD] = (uintptr_t) ut;
/* Put it in global uthread list */
/* 把uthread加入到uthread_list列表 */
spin_lock_save(&uthread_lock, &state, SPIN_LOCK_FLAG_INTERRUPTS);
list_add_head(&uthread_list, &ut->uthread_list_node);
spin_unlock_restore(&uthread_lock, state, SPIN_LOCK_FLAG_INTERRUPTS);
return ut;
err_free_ut_maps:
uthread_free_maps(ut);
err_free_ut_stack:
free(ut->stack);
err_free_ut:
uthread_free_utid(ut->id);
free(ut);
err_done:
return NULL;
}
uthread执行
/* 启动线程 */
status_t uthread_start(uthread_t *ut)
{
if (!ut || !ut->thread)
return ERR_INVALID_ARGS;
return thread_resume(ut->thread);
}
/* 实际启动的线程 */
void arch_uthread_startup(void)
{
/* 通过线程局部存储找到uthread标示 */
struct uthread *ut = (struct uthread *) tls_get(TLS_ENTRY_UTHREAD);
/* 获取栈地址 */
register uint64_t sp_usr asm("x2") = ROUNDDOWN(ut->start_stack, 8);
/* 获取程序入口地址 */
register uint64_t entry asm("x3") = ut->entry;
/* 配置一些执行环境,在异常退出后执行指定的代码uthread_t->entry */
__asm__ volatile(
"mov x0, #0\n"
"mov x1, #0\n"
"mov x13, %[stack]\n" /* AArch32 SP_usr */
"mov x14, %[entry]\n" /* AArch32 LR_usr */
"mov x9, #0x10\n"
"msr spsr_el1, x9\n" /* Mode = AArch32 User */
"msr elr_el1, %[entry]\n"/* 异常退出后的入口 */
"eret\n"
:
: [stack]"r" (sp_usr), [entry]"r" (entry)
: "x0", "x1", "memory"
);
}
线程间通信
因为Trusty App为独立的ELF文件,所以TOS需要实现线程间通信。
为了实现通信,使用trusty_app_t的局部存储,绑定了一个轮询结构
trusty app局部存储
即给app绑定一些私有信息。要使用句柄存储首先需要申请,获取一个id。然后通过这个id可以读写局部存储。
局部存储申请
/* 申请一个局部存储空间,返回可用的局部存储空间编号(1-n)
* 应为句柄存储在trysty_app启动时创建,所以申请空间操作必须在app启动之前
*/
int trusty_als_alloc_slot(void)
{
int ret;
mutex_acquire(&apps_lock);
if (!apps_started)
ret = ++als_slot_cnt;
else
ret = ERR_ALREADY_STARTED;
mutex_release(&apps_lock);
return ret;
}
局部存储操作
static inline void *trusty_als_get(struct trusty_app *app, int slot_id)
{
uint slot = slot_id - 1;
ASSERT(slot < als_slot_cnt);
return app->als[slot];
}
static inline void trusty_als_set(struct trusty_app *app, int slot_id, void *ptr)
{
uint slot = slot_id - 1;
ASSERT(slot < als_slot_cnt);
app->als[slot] = ptr;
}
trusty app的消息处理
这套框架构建了一个机制,在引用trusty app发生特定事件(现在只有app启动和关闭的事件)时执行相关的操作。主要通过trusty_app_notifier_t结构描述事件处理方法,要添加一个处理只需要在app_notifier_list链表中添加一个trusty_app_notifier_t结构体。
/* app消息处理器(在app接受到特定消息时执行特定操作) */
typedef struct trusty_app_notifier
{
struct list_node node;/* 用于构成链表 */
status_t (*startup)(trusty_app_t *app); /* app启动时执行 */
status_t (*shutdown)(trusty_app_t *app); /* app关闭时执行 */
} trusty_app_notifier_t;
轮询事件
用于实现轮询(多个用户线程调用陷入函数进入内核,内核通过轮询处理多个线程的等待)
handle_ops用于描述轮询方法
struct handle_ops {
uint32_t (*poll)(handle_t *handle);/* 轮询方法 */
/* 轮询成功触发此函数、event为轮询方法返回的值 */
void (*finalize_event)(handle_t *handle, uint32_t event);
void (*shutdown)(handle_t *handle); /* 关闭时触发此方法 */
void (*destroy)(handle_t *handle); /* 引用计算为0时触发 */
};
handle_t用于描述一个轮询对象
typedef struct handle {
refcount_t refcnt; /* 引用计数 */
struct handle_ops *ops; /* 轮询相关操作 */
/* pointer to a wait queue on which threads wait for events for this
* handle.
*/
event_t *wait_event; /* 要等待的事件 */
spin_lock_t slock; /* 锁,用于保护wait_event */
struct list_node hlist_node; /* 用于构成链表 */
void *cookie;
} handle_t;
handle_list_t用于描述一组轮询对象(用于等待多个轮询事件)
typedef struct handle_list {
struct list_node handles; /* handle的链表 */
mutex_t lock; /* 锁,保护handle */
event_t *wait_event; /* 要等待的事件 */
} handle_list_t;
等待操作handle_wait 用于等待一个轮询事件
/* handle要轮询的对象 handle_event轮询对象返回值 timeout等待时间 */
int handle_wait(handle_t *handle, uint32_t *handle_event, lk_time_t timeout)
{
uint32_t event;
event_t ev;
int ret = 0;
if (!handle || !handle_event)
return ERR_INVALID_ARGS;
/* 初始化event 用于等待时让出cpu资源 */
event_init(&ev, false, EVENT_FLAG_AUTOUNSIGNAL);
/* 给handle绑定event资源 */
ret = _prepare_wait_handle(&ev, handle);
if (ret) {/* handle已经绑定event,说明已经有等待操作了 */
LTRACEF("someone is already waiting on handle %p\n", handle);
goto err_prepare_wait;
}
while (true) {
event = handle->ops->poll(handle);/* 轮询 */
if (event)/* 轮询成功退出 */
break;
ret = __do_wait(&ev, timeout);/* 等待 */
if (ret < 0)
goto finish_wait;/* 超时 */
/* 等待成功 */
}
if (handle->ops->finalize_event)/* handle有事件处理函数,调用 */
handle->ops->finalize_event(handle, event);
*handle_event = event;/* 记录等待到的事件 */
ret = NO_ERROR;
finish_wait:
_finish_wait_handle(handle);/* 取消handle与event的绑定 */
err_prepare_wait:
event_destroy(&ev);/* 销毁event事件 */
return ret;
}
等待操作handle_list_wait 用于等待一组轮询事件
/* 等待一组轮询事件
* hlist 要等待的handle list对象
* handle_ptr 返回值,轮询成功的对象
* event_ptr 返回值,轮询方法的返回值
* timeout 等待时间
*/
int handle_list_wait(handle_list_t *hlist, handle_t **handle_ptr,
uint32_t *event_ptr, lk_time_t timeout)
{
int ret;
event_t ev;
DEBUG_ASSERT(hlist);
DEBUG_ASSERT(handle_ptr);
DEBUG_ASSERT(event_ptr);
/* 初始化一个event 用于等待时让出cpu资源 */
event_init(&ev, false, EVENT_FLAG_AUTOUNSIGNAL);
*event_ptr = 0;
*handle_ptr = 0;
/* 防止多个线程同时操作handle list,破坏数据 */
mutex_acquire(&hlist->lock);
DEBUG_ASSERT(hlist->wait_event == NULL);
hlist->wait_event = &ev;
/* 对handle list中的对象轮询一次 */
ret = _hlist_do_poll_locked(hlist, handle_ptr, event_ptr, true);
if (ret < 0)
goto err_do_poll;/* handle list中有对象被等待 */
if (ret == 0) {/* 轮询不成功 */
do {
/* 等待 */
mutex_release(&hlist->lock);
ret = __do_wait(&ev, timeout);
mutex_acquire(&hlist->lock);
if (ret < 0)
break;/* 超时 */
/* event等待成功 */
/* 重新轮询一次 */
ret = _hlist_do_poll_locked(hlist, handle_ptr,
event_ptr, false);
} while (!ret);
/* handle list中的对象解邦event */
_hlist_finish_wait_locked(hlist, NULL);
}
if (ret == 1) {/* 轮询成功 */
handle_t *handle = *handle_ptr;/* 记录轮询成功的句柄 */
/* 轮询成功调用对应的消息处理函数 */
if (handle->ops->finalize_event)
handle->ops->finalize_event(handle, *event_ptr);
handle_incref(handle);
/* move list head after item we just found */
/* 把轮询成功的handle移动到链表的尾部,下次优先轮询其他事件 */
list_delete(&hlist->handles);
list_add_head(&handle->hlist_node, &hlist->handles);
ret = NO_ERROR;
}
err_do_poll:
hlist->wait_event = NULL;/* 解邦event */
mutex_release(&hlist->lock);
event_destroy(&ev);/* 销毁event事件 */
return ret;
}
轮询事件绑定到trusty app
为了把轮询事件绑定到用户线程,在应用程序创建之前为每个应用创建局部存储,并通过消息处理设置局部存储uctx
/* trusty_app启动时初始化uctx */
static status_t _uctx_startup(trusty_app_t *app);
static uint _uctx_slot_id;/* 申请到的trusty_app句柄存储的编号 */
/* 给trusty_app注册的消息处理器 */
static struct trusty_app_notifier _uctx_notifier = {
.startup = _uctx_startup,
};
static status_t _uctx_startup(trusty_app_t *app)
{
uctx_t *uctx;
int err = uctx_create(app, &uctx);/* 创建uctx */
if (err)
return err;
trusty_als_set(app, _uctx_slot_id, uctx);/* 绑定uctx到app的局部存储 */
return NO_ERROR;
}
static void uctx_init(uint level)
{
int res;
/* allocate als slot */
/* 为app申请句柄存储 用于绑定uctx */
res = trusty_als_alloc_slot();
if (res < 0)
panic("failed (%d) to alloc als slot\n", res);
_uctx_slot_id = res;
/* register notifier */
/* 注册消息处理方法 初始化uctx并绑定到局部存储 */
res = trusty_register_app_notifier(&_uctx_notifier);
if (res < 0)
panic("failed (%d) to register uctx notifier\n", res);
}
/* 初始化钩子 */
LK_INIT_HOOK(uctx, uctx_init, LK_INIT_LEVEL_APPS - 2);
uctx
uctx用于给app绑定轮询事件,数据结构如下
/* 一个app最多轮询的事件数 */
#define IPC_MAX_HANDLES 64
struct uctx {
/* 位图 标记handles中那些指针被使用了 */
unsigned long inuse[BITMAP_NUM_WORDS(IPC_MAX_HANDLES)];
handle_t *handles[IPC_MAX_HANDLES];/* 通过id速查handle */
void *priv;/* 指向对应的trusty_app */
handle_list_t handle_list;/* 记录下被等待的handle */
};
uctx创建了一些函数用于绑定handle 、解除绑定handle、通过id获取handle
/* 在uctx中添加一个handle
* handle要添加的handle
* id返回变量,添加进uctx的handle的id
*/
int uctx_handle_install(uctx_t *ctx, handle_t *handle, handle_id_t *id);
/* 根据id获取handle
* handle_id 要查找的handle的id
* handle_ptr 返回值,查找到的handle
*/
int uctx_handle_get(uctx_t *ctx, handle_id_t handle_id, handle_t **handle_ptr);
/* 从uctx中移除一个handle
* handle_id要移除的handle的id
* handle_ptr返回变量,移除的handle
*/
int uctx_handle_remove(uctx_t *ctx, handle_id_t handle_id, handle_t **handle_ptr);
IPC
线程通信主要实现类似与socket的通信框架。通信分为服务器和客户端,服务器创建端口ipc_port_t,然后等待客户端链接,客户端链接后,就可以相互发送消息。
IPC主要由端口(ipc_port)和链接(ipc_chan)组成。ipc_port、ipc_chan都是内核轮询对象,通过包含handle实现。
通信端口
服务器,用于维护所有链接的数据结构。
/* 端口,服务器创建,可以由多个客户端链接 */
typedef struct ipc_port {
/* e.g. /service/sys/crypto, /service/usr/drm/widevine */
char path[IPC_PORT_PATH_MAX];/* 端口路径(名字) */
const struct uuid *uuid;
uint32_t state; /* 端口状态,用于确定通道可用 */
uint32_t flags; /* 端口权限,用于确定那些客户端可以连接(connect) */
uint num_recv_bufs;
size_t recv_buf_size;
handle_t handle;
/* 每次connect创建一对链接ipc_chan,此链表保存这些链接的server */
struct list_node pending_list;
struct list_node node;/* 链表ipc_port_list节点 */
} ipc_port_t;
端口有状态,记录端口是否可用
/* 标记一个IPC port状态 */
enum {
IPC_PORT_STATE_INVALID = 0, /* 创建时的状态,创建中不可用 */
IPC_PORT_STATE_LISTENING = 1, /* 创建完成,等待连接 */
IPC_PORT_STATE_CLOSING = 2, /* 销毁中,标记正在关闭通道,通道不可用 */
};
端口有权限控制
/* 通道的权限 */
enum {
IPC_PORT_ALLOW_TA_CONNECT = 0x1, /* 允许trusty App连接此通道 */
IPC_PORT_ALLOW_NS_CONNECT = 0x2, /* 允许Non-Secure链接此通道 */
};
连接
一个连接对应一对ipc_chan,ipc_chan通过peer指向自己的通信对象(server指向client,client指向server)。
typedef struct ipc_chan {
obj_t refobj;
obj_ref_t peer_ref;
/* 一对链接 ipc_chan指向ipc_chan,一个server一个client*/
struct ipc_chan *peer;
const struct uuid *uuid; /* 对应程序的uuid */
uint32_t state; /* 连接的状态 */
uint32_t flags; /* 标记链接是server或client */
uint32_t aux_state; /* 附加状态,主要用在通知peer改变状态时 */
/* handle_ref is a self reference when there are
* outstanding handles out there. It is removed
* when last handle ref goes away.
*/
obj_ref_t handle_ref;
handle_t handle; /* 对应的轮询句柄 */
/* used for port's pending list. node_ref field is a
* self reference when node field is inserted in the list.
*
* TODO: consider creating generic solution by grouping
* together list_node and obj_ref_t into single struct.
*/
obj_ref_t node_ref;
struct list_node node; /* 在port->pending_list构成链表 */
ipc_msg_queue_t *msg_queue; /* 用于存放消息 */
/*
* TODO: consider changing async connect to preallocate
* not-yet-existing port object then we can get rid
* of this field.
*/
/* client链接server时如果server不存在就把要连接的server的路径保存在这儿 */
const char *path;
} ipc_chan_t;
标记连接是服务器还是客户端
/* 标记一个ipc_chans是服务器还是客户端 */
enum {
IPC_CHAN_FLAG_SERVER = 0x1,
};
连接状态通过枚举实现
/* 连接的状态 */
enum {
IPC_CHAN_STATE_INVALID = 0, /* 创建中不能用于轮询 */
IPC_CHAN_STATE_ACCEPTING = 1, /* connect执行后server的状态 */
IPC_CHAN_STATE_CONNECTING = 2, /* connect执行后client的状态 */
IPC_CHAN_STATE_CONNECTED = 3, /* accept执行后的状态 */
IPC_CHAN_STATE_DISCONNECTING = 4, /* 端口关闭时的状态 */
/* client发起链接时如果server不存在,client进入此状态 */
IPC_CHAN_STATE_WAITING_FOR_PORT = 5,
};
/* 连接的附加状态,用在peer执行指定操作后,当前ipc_chan改变状态 */
enum {
/* 发送消息时ip_chan中没有空闲的消息队列可用时 进入此状态 */
IPC_CHAN_AUX_STATE_SEND_BLOCKED = 0x1,
/* 接受消息后
* 如果peer对象是IPC_CHAN_AUX_STATE_SEND_BLOCKED状态
* 就标记为IPC_CHAN_AUX_STATE_SEND_UNBLOCKED */
IPC_CHAN_AUX_STATE_SEND_UNBLOCKED = 0x2,
/* 用于通知client server接受了链接(执行了accept) */
IPC_CHAN_AUX_STATE_CONNECTED = 0x4,
};
消息队列
消息队列由ipc_msg_queue_t描述
ipc_msg_queue_t包含三个链表(free_list、filled_list、read_list)用于记录哪些空间是空闲的、哪些空间是有数据的、哪些空间是可读的
实际的存储空间在ipc_msg_queue_t的一个buf对象中
msg_item_t用于记录存储空间信息(状态、用了多少空间)
/* item的状态 */
enum {
MSG_ITEM_STATE_FREE = 0, /* 空闲的 */
MSG_ITEM_STATE_FILLED = 1, /* 填充过 */
MSG_ITEM_STATE_READ = 2, /* 可读 */
};
typedef struct msg_item {
uint8_t id; /* 编号,对应器在ipc_msg_queue->items中的数组下标 */
uint8_t state; /* 状态信息 */
uint num_handles; /* 未用 */
handle_id_t handles[MAX_MSG_HANDLES]; /* 未用 */
size_t len; /* 使用的大小 */
struct list_node node;
} msg_item_t;
typedef struct ipc_msg_queue {
struct list_node free_list; /* 链表,记录空闲的item */
struct list_node filled_list;/* 链表,记录有数据的item */
struct list_node read_list; /* 链表,记录可以读的item */
uint num_items; /* item的个数 */
size_t item_sz; /* item空间大小 */
/* 实际的存储空间
* 大小 num_items * item_sz
* 第n个item的存储起始地址 buf + n * item_sz
*/
uint8_t *buf;
/* store the message descriptors in the queue,
* and the buffer separately. The buffer could
* eventually move to a separate area that can
* be mapped into the process directly.
*/
msg_item_t items[0];
} ipc_msg_queue_t;
主要方法
/* 创建一个端口,由服务器调用
* sid 服务器程序的uuid
* path 路径,用于标示一个端口。客户段通过此路径连接此服务
* num_recv_bufs 消息队列大小
* recv_buf_size 消息队列中一个单元的大小
* flags 端口的权限
* phandle_ptr 返回值,返回端口的轮询句柄
*/
int ipc_port_create(const uuid_t *sid, const char *path,
uint num_recv_bufs, size_t recv_buf_size,
uint32_t flags, handle_t **phandle_ptr);
/* 服务器端发布端口(建立端口和链接的关系) */
int ipc_port_publish(handle_t *phandle);
/* 接受一个链接
* phandle 为port的轮询handle
* chandle_ptr 返回值,server的轮询handle
* uuid_ptr 返回值,client的uuid
*/
int ipc_port_accept(handle_t *phandle, handle_t **chandle_ptr,
const uuid_t **uuid_ptr);
/* 客户端链接到服务器
* cid 客户段的uuid
* path 要连接的服务器的路径
* max_path 用于限制path的长度
* flags 用于标记是否可以在port创建之前调用此函数,如果可以
* 此创建的客户端连接ipc_chan将会加入一个空闲队列waiting_for_port_chan_list
* chandle_ptr 返回值,返回创建的链接的轮询句柄
*/
int ipc_port_connect_async(const uuid_t *cid, const char *path, size_t max_path,
uint flags, handle_t **chandle_ptr);
/* 把读过的消息从filled_list删除插入到read_list */
int ipc_get_msg(handle_t *chandle, ipc_msg_info_t *msg_info);
/* 读取一条消息 */
int ipc_read_msg(handle_t *chandle, uint32_t msg_id, uint32_t offset,
ipc_msg_kern_t *msg);
/* 把读过的消息从read_list删除插入到free_list */
int ipc_put_msg(handle_t *chandle, uint32_t msg_id);
/* 发送一条消息 */
int ipc_send_msg(handle_t *chandle, ipc_msg_kern_t *msg);
系统调用
异常框架
分析系统调用需要分析异常处理代码
以下是异常入口代码,可以看出系统调用在app和OS中都可以执行(regsave_long保存现场、arm64_sync_exc_lower_el_32恢复现场并退出异常)。arm64_sync_exception为同步异常处理函数,接受现场保护的堆栈地址作为参数。
/* exceptions from current EL, using SPx */
.org 0x200
LOCAL_FUNCTION(arm64_sync_exc_current_el_SPx)
regsave_long
mov x0, sp
bl arm64_sync_exception
b arm64_exc_shared_restore_long
/* exceptions from lower EL, running arm32 */
.org 0x600
LOCAL_FUNCTION(arm64_sync_exc_lower_el_32)
regsave_long
mov x0, sp
bl arm64_sync_exception
b arm64_exc_shared_restore_long
同步异常处理函数。主要处理系统调用和浮点异常
void arm64_sync_exception(struct arm64_iframe_long *iframe)
{
struct fault_handler_table_entry *fault_handler;
uint32_t esr = ARM64_READ_SYSREG(esr_el1);/* 获取esr_el1的值 */
/* 获取异常原因 */
uint32_t ec = esr >> 26;
uint32_t il = (esr >> 25) & 0x1;
uint32_t iss = esr & ((1<<24) - 1);
#ifdef WITH_LIB_SYSCALL
if (ec == 0x15 || ec == 0x11) { // syscall 64/32
/* 判断是系统调用 */
void arm64_syscall(struct arm64_iframe_long *iframe);
arch_enable_fiqs();/* 使能fiq */
arm64_syscall(iframe);/* 系统调用实际函数 */
arch_disable_fiqs();/* 失能fiq */
return;
}
#endif
/* floating point */
if (ec == 0x07) {/* 浮点异常 */
arm64_fpu_exception(iframe);
return;
}
for (fault_handler = __fault_handler_table_start; fault_handler < __fault_handler_table_end; fault_handler++) {
if (fault_handler->pc == iframe->elr) {
iframe->elr = fault_handler->fault_handler;
return;
}
}
printf("sync_exception\n");
dump_iframe(iframe);
printf("ESR 0x%x: ec 0x%x, il 0x%x, iss 0x%x\n", esr, ec, il, iss);
if (ec == 0x15) { // syscall
printf("syscall\n");
return;
}
/* 出错 */
panic("die\n");
}
系统调用实际函数。通过分析,W12指定异常调用的编号,X0-X3为系统调用参数,X0为返回值,nr_syscalls为系统调用的个数,syscall_table是一个数组存放系统调用的编号。
FUNCTION (arm64_syscall)
push x0, x30 /* 保存x0 x30 */
ldr w12, [x0, #(12 << 3)] /* 从堆栈中获取w12(系统调用号) */
msr daifclr, #DAIF_MASK_IAF/* 禁止中断 */
ldr x14, =nr_syscalls
ldr x14, [x14] /* 获取系统调用个数 */
cmp x12, x14
b.hs .Lundefined /* 系统调用号必须小于系统调用个数 */
ldr x14, =syscall_table /* 获取系统调用函数的起始地址 */
ldr x14, [x14, x12, lsl#3] /* 获取系统调用句柄 */
cbnz x14, .Ldefined
.Lundefined:
ldr x14,=sys_undefined /* 系统调用句柄为0,执行sys_undefined */
.Ldefined:
ldp x2, x3, [x0, #16] /* 从堆栈中获取系统调用参数 x2 x3 */
ldp x0, x1, [x0] /* 从堆栈中获取系统调用参数 x0 x1 */
blr x14 /* 执行系统调用 */
msr daifset, #DAIF_MASK_IAF/* 使能中断 */
pop x1, x30
str x0, [x1, 0] /* 返回值写入堆栈 */
ret
系统调用声明
声明通过lk/trusty/lib/trusty/include/syscall_table.h文件声明
生成变量nr_syscalls、syscall_table通过文件lk/trusty/lib/syscall/syscall.c实现
/* 用于处理不存在的系统调用 */
long sys_undefined(int num)
{
dprintf(SPEW, "%p invalid syscall %d requested\n", get_current_thread(), num);
return ERR_NOT_SUPPORTED;
}
#ifdef WITH_SYSCALL_TABLE
/* Generate fake function prototypes */
/* 函数声明 */
#define DEF_SYSCALL(nr, fn, rtype, nr_args, ...) rtype sys_##fn (void);
#include <syscall_table.h>
#undef DEF_SYSCALL
#endif
/* 系统调用函数入口地址表(数组 下标为系统调用编号,值为函数入口地址) */
#define DEF_SYSCALL(nr, fn, rtype, nr_args, ...) [(nr)] = (unsigned long) (sys_##fn),
const unsigned long syscall_table [] = {
#ifdef WITH_SYSCALL_TABLE
#include <syscall_table.h>
#endif
};
#undef DEF_SYSCALL
/* 系统调用的个数 */
unsigned long nr_syscalls = countof(syscall_table);
用户空间处理
因为有明确的系统调用规范,用户空间处理比较简单
声明位于lib/include/trusty_syscalls.h,声明系统调用的编号以及函数声明
/* 系统调用编号声明 */
#define __NR_write 0x1
#define __NR_brk 0x2
#define __NR_exit_group 0x3
#define __NR_read 0x4
#define __NR_ioctl 0x5
#define __NR_nanosleep 0x6
#define __NR_gettime 0x7
#define __NR_mmap 0x8
#define __NR_munmap 0x9
#define __NR_prepare_dma 0xa
#define __NR_finish_dma 0xb
#define __NR_port_create 0x10
#define __NR_connect 0x11
#define __NR_accept 0x12
#define __NR_close 0x13
#define __NR_set_cookie 0x14
#define __NR_wait 0x18
#define __NR_wait_any 0x19
#define __NR_get_msg 0x20
#define __NR_read_msg 0x21
#define __NR_put_msg 0x22
#define __NR_send_msg 0x23
#ifndef ASSEMBLY
__BEGIN_CDECLS
/* 函数声明 */
long write (uint32_t fd, void* msg, uint32_t size);
long brk (uint32_t brk);
long exit_group (void);
long read (uint32_t fd, void* msg, uint32_t size);
long ioctl (uint32_t fd, uint32_t req, void* buf);
long nanosleep (uint32_t clock_id, uint32_t flags, uint64_t sleep_time);
long gettime (uint32_t clock_id, uint32_t flags, int64_t *time);
long mmap (void* uaddr, uint32_t size, uint32_t flags, uint32_t handle);
long munmap (void* uaddr, uint32_t size);
long prepare_dma (void* uaddr, uint32_t size, uint32_t flags, void* pmem);
long finish_dma (void* uaddr, uint32_t size, uint32_t flags);
long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size, uint32_t flags);
long connect (const char *path, uint flags);
long accept (uint32_t handle_id, uuid_t *peer_uuid);
long close (uint32_t handle_id);
long set_cookie (uint32_t handle, void *cookie);
long wait (uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs);
long wait_any (uevent_t *event, unsigned long timeout_msecs);
long get_msg (uint32_t handle, ipc_msg_info_t *msg_info);
long read_msg (uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t *msg);
long put_msg (uint32_t handle, uint32_t msg_id);
long send_msg (uint32_t handle, ipc_msg_t *msg);
__END_CDECLS
实现位于lib/lib/libc-trusty/arch/arm/trusty_syscall.S
.section .text.write
FUNCTION(write)
ldr r12, =__NR_write /* 系统调用编号 */
swi #0 /* 触发系统调用 */
bx lr /* 函数返回 */
.section .text.brk
FUNCTION(brk)
ldr r12, =__NR_brk
swi #0
bx lr
.section .text.exit_group
FUNCTION(exit_group)
ldr r12, =__NR_exit_group
swi #0
bx lr
.section .text.read
FUNCTION(read)
ldr r12, =__NR_read
swi #0
bx lr
.section .text.ioctl
FUNCTION(ioctl)
ldr r12, =__NR_ioctl
swi #0
bx lr
.section .text.nanosleep
FUNCTION(nanosleep)
ldr r12, =__NR_nanosleep
swi #0
bx lr
.section .text.gettime
FUNCTION(gettime)
ldr r12, =__NR_gettime
swi #0
bx lr
.section .text.mmap
FUNCTION(mmap)
ldr r12, =__NR_mmap
swi #0
bx lr
.section .text.munmap
FUNCTION(munmap)
ldr r12, =__NR_munmap
swi #0
bx lr
.section .text.prepare_dma
FUNCTION(prepare_dma)
ldr r12, =__NR_prepare_dma
swi #0
bx lr
.section .text.finish_dma
FUNCTION(finish_dma)
ldr r12, =__NR_finish_dma
swi #0
bx lr
.section .text.port_create
FUNCTION(port_create)
ldr r12, =__NR_port_create
swi #0
bx lr
.section .text.connect
FUNCTION(connect)
ldr r12, =__NR_connect
swi #0
bx lr
.section .text.accept
FUNCTION(accept)
ldr r12, =__NR_accept
swi #0
bx lr
.section .text.close
FUNCTION(close)
ldr r12, =__NR_close
swi #0
bx lr
.section .text.set_cookie
FUNCTION(set_cookie)
ldr r12, =__NR_set_cookie
swi #0
bx lr
.section .text.wait
FUNCTION(wait)
ldr r12, =__NR_wait
swi #0
bx lr
.section .text.wait_any
FUNCTION(wait_any)
ldr r12, =__NR_wait_any
swi #0
bx lr
.section .text.get_msg
FUNCTION(get_msg)
ldr r12, =__NR_get_msg
swi #0
bx lr
.section .text.read_msg
FUNCTION(read_msg)
ldr r12, =__NR_read_msg
swi #0
bx lr
.section .text.put_msg
FUNCTION(put_msg)
ldr r12, =__NR_put_msg
swi #0
bx lr
.section .text.send_msg
FUNCTION(send_msg)
ldr r12, =__NR_send_msg
swi #0
bx lr
与Monitor通信
trusty是32位程序,所以只能处理monitor交给它的32位的SMC调用(访问不了64为寄存器)。
根据SMC调用规范[1]可知:
W0用于传递SMC调用标示(即功能) W1-W3用于传递参数 W0用于返回调用结果
调用标示具体描述如下
b31 调用类型 1->Fast Call 0->Yielding Call
b30 标示32bit还是64bit调用
b29:24 服务类型
0x0 ARM架构相关
0x1 CPU服务
0x2 SiP服务
0x3 OEM服务
0x4 标准安全服务
0x5 标准Hypervisor服务
0x6 厂商定制的Hypervisor服务
0x07-0x2f 预留
0x30-0x31 Trusted Application Calls
0x32-0x3f Trusted OS Calls
b23:16 在Fast Call时这些比特位必须为0,其他值保留未用,ARMv7传统的Trusted OS在Fast Call时必须为1
b15:0 函数号
SMC服务类型描述
此不分主要用来声明常量(调用标示相关)
#define SMC_NUM_ENTITIES 64 /* 单个entry下的函数个数 */
#define SMC_NUM_ARGS 4 /* SMC调用参数个数 */
#define SMC_NUM_PARAMS (SMC_NUM_ARGS - 1)/* 去除功能描述w0后的参数个数 */
#define SMC_IS_FASTCALL(smc_nr) ((smc_nr) & 0x80000000)/* 判断一个功能号是fastcall */
#define SMC_IS_SMC64(smc_nr) ((smc_nr) & 0x40000000)/* 判断一个功能号是64位调用 */
#define SMC_ENTITY(smc_nr) (((smc_nr) & 0x3F000000) >> 24)/* 获取一个功能号的entry */
#define SMC_FUNCTION(smc_nr) ((smc_nr) & 0x0000FFFF)/* 获取一个功能号的函数编号 */
/* 宏,辅助生成一个功能号
* entry 服务类型
* fn 功能号
* fastcall 标示调用类型 1->fastcall 0->stdcall
* smc64 标示调用为64位调用
*/
#define SMC_NR(entity, fn, fastcall, smc64) ((((fastcall) & 0x1) << 31) | \
(((smc64) & 0x1) << 30) | \
(((entity) & 0x3F) << 24) | \
((fn) & 0xFFFF) \
)
/* 辅助生成一个32位fastcall的功能号 */
#define SMC_FASTCALL_NR(entity, fn) SMC_NR((entity), (fn), 1, 0)
/* 辅助生成一个32位stdcall的功能号 */
#define SMC_STDCALL_NR(entity, fn) SMC_NR((entity), (fn), 0, 0)
/* 辅助生成一个64位fastcall的功能号 */
#define SMC_FASTCALL64_NR(entity, fn) SMC_NR((entity), (fn), 1, 1)
/* 辅助生成一个64位stdcall的功能号 */
#define SMC_STDCALL64_NR(entity, fn) SMC_NR((entity), (fn), 0, 1)
/* 服务类型(entry)声明 */
#define SMC_ENTITY_ARCH 0 /* ARM Architecture calls */
#define SMC_ENTITY_CPU 1 /* CPU Service calls */
#define SMC_ENTITY_SIP 2 /* SIP Service calls */
#define SMC_ENTITY_OEM 3 /* OEM Service calls */
#define SMC_ENTITY_STD 4 /* Standard Service calls */
#define SMC_ENTITY_RESERVED 5 /* Reserved for future use */
#define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */
#define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */
#define SMC_ENTITY_LOGGING 51 /* Used for secure -> nonsecure logging */
#define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */
/* 以下是具体的系统调用号 */
/* FC = Fast call, SC = Standard call */
#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0)
#define SMC_SC_LOCKED_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1)
/**
* SMC_SC_RESTART_FIQ - Re-enter trusty after it was interrupted by an fiq
*
* No arguments, no return value.
*
* Re-enter trusty after returning to ns to process an fiq. Must be called iff
* trusty returns SM_ERR_FIQ_INTERRUPTED.
*
* Enable by selecting api version TRUSTY_API_VERSION_RESTART_FIQ (1) or later.
*/
#define SMC_SC_RESTART_FIQ SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2)
/**
* SMC_SC_NOP - Enter trusty to run pending work.
*
* No arguments.
*
* Returns SM_ERR_NOP_INTERRUPTED or SM_ERR_NOP_DONE.
* If SM_ERR_NOP_INTERRUPTED is returned, the call must be repeated.
*
* Enable by selecting api version TRUSTY_API_VERSION_SMP (2) or later.
*/
#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3)
/*
* Return from secure os to non-secure os with return value in r1
*/
#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0)
#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0)
#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1)
#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2)
#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3)
#define SMC_FC_FIQ_ENTER SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 4)
#define SMC_FC64_SET_FIQ_HANDLER SMC_FASTCALL64_NR(SMC_ENTITY_SECURE_MONITOR, 5)
#define SMC_FC64_GET_FIQ_REGS SMC_FASTCALL64_NR (SMC_ENTITY_SECURE_MONITOR, 6)
#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7)
#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8)
#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9)
#define SMC_FC_GET_VERSION_STR SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 10)
/**
* SMC_FC_API_VERSION - Find and select supported API version.
*
* @r1: Version supported by client.
*
* Returns version supported by trusty.
*
* If multiple versions are supported, the client should start by calling
* SMC_FC_API_VERSION with the largest version it supports. Trusty will then
* return a version it supports. If the client does not support the version
* returned by trusty and the version returned is less than the version
* requested, repeat the call with the largest supported version less than the
* last returned version.
*
* This call must be made before any calls that are affected by the api version.
*/
#define TRUSTY_API_VERSION_RESTART_FIQ (1)
#define TRUSTY_API_VERSION_SMP (2)
#define TRUSTY_API_VERSION_SMP_NOP (3)
#define TRUSTY_API_VERSION_CURRENT (3)
#define SMC_FC_API_VERSION SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 11)
/* TRUSTED_OS entity calls */
#define SMC_SC_VIRTIO_GET_DESCR SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 20)
#define SMC_SC_VIRTIO_START SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 21)
#define SMC_SC_VIRTIO_STOP SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 22)
#define SMC_SC_VDEV_RESET SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 23)
#define SMC_SC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 24)
#define SMC_NC_VDEV_KICK_VQ SMC_STDCALL_NR(SMC_ENTITY_TRUSTED_OS, 25)
服务处理
服务处理句柄定义如下
/* SMC调用的参数 */
typedef struct smc32_args {
uint32_t smc_nr;/* 服务号W0 */
uint32_t params[SMC_NUM_PARAMS];/* 参数W1-W3 */
} smc32_args_t;
/* 服务处理句柄 */
typedef long (*smc32_handler_t)(smc32_args_t *args);
因为trusty不处理64位调用,所有要确定一个调用只需要关心三个域(调用类型、服务类型、函数号)
把调用类型和服务类型关联到一起定义两个表
/* fast调用的entry表 表中的函数需要分析function number执行相关操作 */
smc32_handler_t sm_fastcall_table[SMC_NUM_ENTITIES] = {
[0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined,
[SMC_ENTITY_SECURE_MONITOR] = smc_fastcall_secure_monitor,
[SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] = smc_undefined
};
/* std调用的entry表 表中的函数需要分析function number执行相关操作 */
smc32_handler_t sm_stdcall_table[SMC_NUM_ENTITIES] = {
[0 ... SMC_ENTITY_SECURE_MONITOR - 1] = smc_undefined,
[SMC_ENTITY_SECURE_MONITOR] = smc_stdcall_secure_monitor,
[SMC_ENTITY_SECURE_MONITOR + 1 ... SMC_NUM_ENTITIES - 1] = smc_undefined
};
stdcall中有一个特别的调用SMC_SC_NOP,会根据第一个参数(W0)的值选择不同的功能。所以这里特别定义了如下的表
smc32_handler_t sm_nopcall_table[SMC_NUM_ENTITIES] = {
[0] = smc_nop_secure_monitor,
[1 ... SMC_NUM_ENTITIES - 1] = smc_undefined
};
表中的函数需要获取调用类型的功能号,进行相关操作。这里选取smc_stdcall_secure_monitor为列介绍
/* stdcall 函数表(通过函数编号找到对应的函数) */
static smc32_handler_t sm_stdcall_function_table[] = {
[SMC_FUNCTION(SMC_SC_RESTART_LAST)] = smc_restart_stdcall,
[SMC_FUNCTION(SMC_SC_LOCKED_NOP)] = smc_nop_stdcall,
[SMC_FUNCTION(SMC_SC_RESTART_FIQ)] = smc_restart_stdcall,
[SMC_FUNCTION(SMC_SC_NOP)] = smc_undefined, /* reserve slot in table, not called */
};
static long smc_stdcall_secure_monitor(smc32_args_t *args)
{
u_int function = SMC_FUNCTION(args->smc_nr);/* 获取函数编号 */
smc32_handler_t handler_fn = NULL;
if (function < countof(sm_stdcall_function_table))/* 防止越界访问数组 */
handler_fn = sm_stdcall_function_table[function];/* 获取函数 */
if (!handler_fn)
handler_fn = smc_undefined;/* 函数指针为空即不支持 */
return handler_fn(args);/* 执行对应的函数 */
}
服务注册
服务注册,用于添加一个服务类型的处理
status_t sm_register_entity(uint entity_nr, smc32_entity_t *entity)
{
status_t err = NO_ERROR;
if (entity_nr >= SMC_NUM_ENTITIES)
return ERR_INVALID_ARGS;/* 服务类似是否过大 */
if (entity_nr >= SMC_ENTITY_RESERVED && entity_nr < SMC_ENTITY_TRUSTED_APP)
return ERR_NOT_ALLOWED;/* 检测服务类型可用(有一段保留)*/
if (!entity)/* 用于保存函数句柄不能为空 */
return ERR_INVALID_ARGS;
if (!entity->fastcall_handler && !entity->stdcall_handler)
return ERR_NOT_VALID;/* 必须初始化一种服务 */
mutex_acquire(&smc_table_lock);
/* Check if entity is already claimed */
/* 防止重复初始乎一个服务 */
if (sm_fastcall_table[entity_nr] != smc_undefined ||
sm_nopcall_table[entity_nr] != smc_undefined ||
sm_stdcall_table[entity_nr] != smc_undefined) {
err = ERR_ALREADY_EXISTS;
goto unlock;
}
/* 登记服务 */
if (entity->fastcall_handler)
sm_fastcall_table[entity_nr] = entity->fastcall_handler;
if (entity->nopcall_handler)
sm_nopcall_table[entity_nr] = entity->nopcall_handler;
if (entity->stdcall_handler)
sm_stdcall_table[entity_nr] = entity->stdcall_handler;
unlock:
mutex_release(&smc_table_lock);
return err;
}
TOS与服务接口
通过初始化钩子在系统初始化完才后等待服务请求
/* 所有处理器初始化钩子,开启线程处理SMC调用 */
LK_INIT_HOOK_FLAGS(libsm_cpu, sm_secondary_init, LK_INIT_LEVEL_PLATFORM - 2, LK_INIT_FLAG_ALL_CPUS);
/* 主处理器初始化钩子,为stdcall服务 */
LK_INIT_HOOK(libsm, sm_init, LK_INIT_LEVEL_PLATFORM - 1);
fastcall处理
fastcall在函数sm_sched_nonsecure中处理
libsm_cpu初始化钩子为每个CPU创建了一个线程sm_wait_for_smcall,此线程最终会调用sm_sched_nonsecure。调用过程如下sm_wait_for_smcall -> sm_return_and_wait_for_next_stdcall -> sm_sched_nonsecure。
sm_sched_nonsecure实现如下。参数retval用于返回stdcall的执行结果,参数args用于获取stdcall调用的参数(W0-W3)
sm_sched_nonsecure通过SMC指令进入monitor,X0用于标记功能SMC_SC_NS_RETURN(stdcall return to Non-Secure),X1用于记录返回的值。monitor在通过退出异常进入Non-Secure。在下一次Non-Secure异常调用时,通过monitor中转返回SMC指令下一条地址。此时判定是不是32位fastcall。如果是32位fastcall,就获取系统调用功能号的Entry(服务类型),通过Entry查表sm_fastcall_table获取异常处理函数,执行函数后再次通过SMC进入Non-Secure。如果是32位stdcall,就把系统调用参数写入args,然后函数返回让调用sm_sched_nonsecure的函数处理32位stdcall。
/* void sm_sched_nonsecure(long retval, smc32_args_t *args); */
FUNCTION(sm_sched_nonsecure)
push x1, x30
.Lfastcall_complete:
mov x1, x0
.Lreturn_sm_err:
ldr x0, =SMC_SC_NS_RETURN
mov x2, xzr
mov x3, xzr
smc #0 /* 返回non-secure X0为传递给monitor的标示,X1为返回给non-secure的值 */
tbnz x0, #30, .Lsm_err_not_supported /* 第30比特用于标记64位调用 */
tbz x0, #31, .Lnot_fast_call/* 第31比特用于标记fastcall */
/* fastcall */
sub sp, sp, #(4 * SMC_NUM_ARGS) /* 在堆栈上开辟空间 存放smc32_args_t */
stp w0, w1, [sp] /* 保存系统调用参数到零时变量中 */
stp w2, w3, [sp, #4 * 2]
ubfx x0, x0, #24, #6 /* x0 = entity */ /* 获取entry的值 */
ldr x9, =sm_fastcall_table /* 加载起始地址 */
ldr x9, [x9, x0, lsl #3] /* 获取函数地址 */
mov x0, sp /* x0 = smc_args_t* args */
blr x9 /* 调用处理函数 */
add sp, sp, #(4 * SMC_NUM_ARGS)/* 恢复栈 */
b .Lfastcall_complete /* 退出到用户空间 */
/* stdcall */
.Lnot_fast_call:
pop x9, x30
stp w0, w1, [x9], #8 /* 保存系统调用参数到指针args中 */
stp w2, w3, [x9], #8
ret
.Lsm_err_not_supported: /* 不支持 退出到用户空间 */
mov x1, #SM_ERR_NOT_SUPPORTED
b .Lreturn_sm_err
.Lsm_err_busy:
mov x1, #SM_ERR_BUSY
b .Lreturn_sm_err
stdcall处理
通过代码分析,一般的stdcall在时间上不可用并发(SMC_SC_NOP可以在多个cpu上同时执行)。必须等待一个stdcall处理完后才能执行下一个stdcall。
为了实现时间上不可并发,主处理器初始化钩子libsm,创建一个线程sm_stdcall_loop用于处理实际的stdcall,所有处理器初始化钩子libsm_cpu创建一个线程sm_wait_for_smcall,用于向主处理器发送处理请求。
线程sm_stdcall_loop通过stdcallstate变量与sm_wait_for_smcall通信
stdcallstate数据类型为sm_std_call_state,定义如下
struct sm_std_call_state {
spin_lock_t lock; /* 锁用于保护此结构体不被多个线程同时访问破坏数据 */
event_t event; /* 事件,用于通知主处理器有可以处理的stdcall请求 */
smc32_args_t args; /* stdcall请求的参数 */
long ret; /* stdcall处理的结果 */
bool done; /* 标记处理完成 */
int active_cpu; /* 记录活动的cpu */
int initial_cpu; /* 记录当前请求stdcall的处理器编号 */
int last_cpu; /* 记录当前请求stdcall的处理器编号 */
int restart_count;
};
sm_stdcall_loop用于处理stdcall请求,代码如下。一般此线程处于空闲状态等待stdcall请求(等待事件stdcallstate.event),在接受到请求后通过系统调用功能号查表sm_stdcall_table找到处理函数并执行,把结果写入stdcallstate中然后继续等待新的请求。
static int __NO_RETURN sm_stdcall_loop(void *arg)
{
long ret;
spin_lock_saved_state_t state;
while (true) {
LTRACEF("cpu %d, wait for stdcall\n", arch_curr_cpu_num());
event_wait(&stdcallstate.event);/* 等待信号处理stdcall */
/* Dispatch 'standard call' handler */
LTRACEF("cpu %d, got stdcall: 0x%x, 0x%x, 0x%x, 0x%x\n",
arch_curr_cpu_num(),
stdcallstate.args.smc_nr, stdcallstate.args.params[0],
stdcallstate.args.params[1], stdcallstate.args.params[2]);
/* 查表调用stdcall处理函数 */
ret = sm_stdcall_table[SMC_ENTITY(stdcallstate.args.smc_nr)](&stdcallstate.args "SMC_ENTITY(stdcallstate.args.smc_nr)");
LTRACEF("cpu %d, stdcall(0x%x, 0x%x, 0x%x, 0x%x) returned 0x%lx (%ld)\n",
arch_curr_cpu_num(),
stdcallstate.args.smc_nr, stdcallstate.args.params[0],
stdcallstate.args.params[1], stdcallstate.args.params[2], ret, ret);
/* 防止多个线程写stdcallstate */
spin_lock_save(&stdcallstate.lock, &state, SPIN_LOCK_FLAG_IRQ);
stdcallstate.ret = ret;/* 记录stdcall的处理结果 */
stdcallstate.done = true;/* 标记stdcall处理完成 */
event_unsignal(&stdcallstate.event);
spin_unlock_restore(&stdcallstate.lock, state, SPIN_LOCK_FLAG_IRQ);
}
}
sm_wait_for_smcall用于等待新的stdcall请求并转法给sm_stdcall_loop处理,代码如下。
static void sm_wait_for_smcall(void)
{
int cpu;
long ret = 0;
LTRACEF("wait for stdcalls, on cpu %d\n", arch_curr_cpu_num());
while (true) {
/*
* Disable interrupts so stdcallstate.active_cpu does not
* change to or from this cpu after checking it below.
*/
arch_disable_ints();
/* Switch to sm-stdcall if sm_queue_stdcall woke it up */
thread_yield();/* 让出cpu资源,给主线程处理stdcall */
cpu = arch_curr_cpu_num();
if (cpu == stdcallstate.active_cpu)
ret = sm_get_stdcall_ret();/* 获取stdcall处理结果 */
else
ret = SM_ERR_NOP_DONE;
/* 等待下一个stdcall */
sm_return_and_wait_for_next_stdcall(ret, cpu);
/* Re-enable interrupts (needed for SMC_SC_NOP) */
arch_enable_ints();
}
}
static void sm_return_and_wait_for_next_stdcall(long ret, int cpu)
{
smc32_args_t args = {0};
do {
arch_disable_fiqs();
sm_sched_nonsecure(ret, &args);/* 退出到Non-secure,等待下一次stdcall请求 */
arch_enable_fiqs();
/* Allow concurrent SMC_SC_NOP calls on multiple cpus */
if (args.smc_nr == SMC_SC_NOP) {
/* 特别的stdcall可以在多个cpu上并发 */
LTRACEF_LEVEL(3, "cpu %d, got nop\n", cpu);
ret = sm_nopcall_table[SMC_ENTITY(args.params[0])](&args "SMC_ENTITY(args.params[0])");
} else {
ret = sm_queue_stdcall(&args);/* 请求sm_stdcall_loop处理 */
}
} while (ret);
}
static long sm_queue_stdcall(smc32_args_t *args)
{
long ret;
uint cpu = arch_curr_cpu_num();
spin_lock(&stdcallstate.lock);
if (stdcallstate.event.signalled || stdcallstate.done) {
/* 主线程正在处理(stdcallstate.event.signalled)
* 或其他数据还没有把数据拿走(stdcallstate.done) */
if (args->smc_nr == SMC_SC_RESTART_LAST && stdcallstate.active_cpu == -1) {
stdcallstate.restart_count++;
LTRACEF_LEVEL(3, "cpu %d, restart std call, restart_count %d\n",
cpu, stdcallstate.restart_count);
goto restart_stdcall;
}
dprintf(CRITICAL, "%s: cpu %d, std call busy\n", __func__, cpu);
ret = SM_ERR_BUSY;
goto err;
} else {
if (args->smc_nr == SMC_SC_RESTART_LAST) {
dprintf(CRITICAL, "%s: cpu %d, unexpected restart, no std call active\n",
__func__, arch_curr_cpu_num());
ret = SM_ERR_UNEXPECTED_RESTART;
goto err;
}
}
LTRACEF("cpu %d, queue std call 0x%x\n", cpu, args->smc_nr);
stdcallstate.initial_cpu = cpu; /* 记录当前主线程为那个cpu工作 */
stdcallstate.ret = SM_ERR_INTERNAL_FAILURE;
stdcallstate.args = *args; /* 把参数传递给主线程处理 */
stdcallstate.restart_count = 0;
event_signal(&stdcallstate.event, false); /* 通知主线程工作 */
restart_stdcall:
stdcallstate.active_cpu = cpu; /* 记录活动cpu */
ret = 0;
err:
spin_unlock(&stdcallstate.lock);
return ret;
}
感谢作者:wxjstz
原文链接:https://github.com/hardenedlinux/embedded-iot_profile/blob/master/docs/arm64/dragonboard410c/little-kernel%E5%88%86%E6%9E%90.md