⚠️ The code is based on: https://gitlab.com/riseproject/riscv-optee/optee_os/-/tree/dev-optee-mpxy

Commit ID: 75df9ba41a404aec897399ead0ff0aebcbff48ca

  • ldelf_hex.c is generated by scripts/gen_ldelf_hex.py from the input file: ldelf.elf and is included by OP-TEE, i.e. ldelf_data[], ldelf_code_size, ldelf_data_size, and ldelf_entry.
    • ldelf sources: <optee-src>/ldelf/
    • ldelf is running in U-mode.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// core/kernel/ldelf_loader.c

/*
 * This function may leave a few mappings behind on error, but that's taken
 * care of by tee_ta_init_user_ta_session() since the entire context is
 * removed then.
 */
TEE_Result ldelf_load_ldelf(struct user_mode_ctx *uctx)
{
	TEE_Result res = TEE_SUCCESS;
	vaddr_t stack_addr = 0;
	vaddr_t code_addr = 0;
	vaddr_t rw_addr = 0;
	vaddr_t bb_addr = 0;
	uint32_t prot = 0;

	uctx->is_32bit = is_32bit;

	// Allocate memory for bounce buffer.
	res = alloc_and_map_fobj(uctx, BOUNCE_BUFFER_SIZE, TEE_MATTR_PRW, 0,
				 &bb_addr);
	if (res)
		return res;
	uctx->bbuf = (void *)bb_addr;
	uctx->bbuf_size = BOUNCE_BUFFER_SIZE;

	// Allocate stack memory for ldelf.
	res = alloc_and_map_fobj(uctx, LDELF_STACK_SIZE,
				 TEE_MATTR_URW | TEE_MATTR_PRW, VM_FLAG_LDELF,
				 &stack_addr);
	if (res)
		return res;
	uctx->ldelf_stack_ptr = stack_addr + LDELF_STACK_SIZE;

  // Allocate code section memory for ldelf.
	res = alloc_and_map_fobj(uctx, ldelf_code_size, TEE_MATTR_PRW,
				 VM_FLAG_LDELF, &code_addr);
	if (res)
		return res;
	// Assign uctx->entry_func to ldelf's entry point, i.e. [_ldelf_start()](/posts/optee-ldelf/).
	uctx->entry_func = code_addr + ldelf_entry;

	// Allocate data section memory for ldelf.
	rw_addr = ROUNDUP(code_addr + ldelf_code_size, SMALL_PAGE_SIZE);
	res = alloc_and_map_fobj(uctx, ldelf_data_size,
				 TEE_MATTR_URW | TEE_MATTR_PRW, VM_FLAG_LDELF,
				 &rw_addr);
	if (res)
		return res;

	vm_set_ctx(uctx->ts_ctx);

	// Copy ldelf codes.
	// ldelf_data[] includes both ldelf's codes and data.
	// ldelf_data[] is generated by script/gen_ldelf_hex.py.
	memcpy((void *)code_addr, ldelf_data, ldelf_code_size);

	// Copy ldelf data.
	res = copy_to_user((void *)rw_addr, ldelf_data + ldelf_code_size,
			   ldelf_data_size);
	if (res)
		return res;

	prot = TEE_MATTR_URX;
	if (IS_ENABLED(CFG_CORE_BTI))
		prot |= TEE_MATTR_GUARDED;

	res = vm_set_prot(uctx, code_addr,
			  ROUNDUP(ldelf_code_size, SMALL_PAGE_SIZE), prot);
	if (res)
		return res;

	DMSG("ldelf load address %#"PRIxVA, code_addr);

	return TEE_SUCCESS;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// core/kernel/ldelf_loader.c

TEE_Result ldelf_init_with_ldelf(struct ts_session *sess,
				 struct user_mode_ctx *uctx)
{
	TEE_Result res = TEE_SUCCESS;
	struct ldelf_arg *arg = NULL;
	uint32_t panic_code = 0;
	uint32_t panicked = 0;
	uaddr_t usr_stack = 0;
	struct ldelf_arg *arg_bbuf = NULL;

	// Set user stack.
	usr_stack = uctx->ldelf_stack_ptr;
	usr_stack -= ROUNDUP(sizeof(*arg), STACK_ALIGNMENT);
	arg = (struct ldelf_arg *)usr_stack;
	// Temporay overwrites handle_scall() with [scall_handle_ldelf()](/posts/optee-ldelf/).
	sess->handle_scall = scall_handle_ldelf;

	res = clear_user(arg, sizeof(*arg));
	if (res)
		return res;

	// Copy UUID to &arg->uuid.
	res = PUT_USER_SCALAR(uctx->ts_ctx->uuid, &arg->uuid);
	if (res)
		return res;

	// uctx->entry_func is set to ldelf's entry point, i.e. [_ldelf_start()](/posts/optee-ldelf/),
	// in [ldelf_load_ldelf()](/posts/optee-ldelf/).
	// Switch to U-mode to execute ldelf.
	// ldelf will fill in arg parameter of the TA ELF.
	// E.g. arg->is_32bit, arg->entry_func, arg->load_addr, arg->stack_ptr... etc.
	res = [thread_enter_user_mode](/posts/optee-threads/)((vaddr_t)arg, 0, 0, 0,
				     usr_stack, uctx->entry_func,
				     is_32bit, &panicked, &panic_code);

	// Restore handle_scall().
	sess->handle_scall = sess->ctx->ops->handle_scall;
	thread_user_clear_vfp(uctx);
	ldelf_sess_cleanup(sess);

	if (panicked) {
		abort_print_current_ts();
		EMSG("ldelf panicked");
		return TEE_ERROR_GENERIC;
	}
	if (res) {
		EMSG("ldelf failed with res: %#"PRIx32, res);
		return res;
	}

	res = BB_MEMDUP_USER(arg, sizeof(*arg), &arg_bbuf);
	if (res)
		return res;

	if (is_user_ta_ctx(uctx->ts_ctx)) {
		/*
		 * This is already checked by the elf loader, but since it runs
		 * in user mode we're not trusting it entirely.
		 */
		if (arg_bbuf->flags & ~TA_FLAGS_MASK)
			return TEE_ERROR_BAD_FORMAT;

		to_user_ta_ctx(uctx->ts_ctx)->ta_ctx.flags = arg_bbuf->flags;
	}

	// Copy TA ELF's information to struct user_mode_ctx.
	uctx->is_32bit = arg_bbuf->is_32bit;
	uctx->entry_func = arg_bbuf->entry_func;
	uctx->load_addr = arg_bbuf->load_addr;
	uctx->stack_ptr = arg_bbuf->stack_ptr;
	uctx->dump_entry_func = arg_bbuf->dump_entry;
#ifdef CFG_FTRACE_SUPPORT
	uctx->ftrace_entry_func = arg_bbuf->ftrace_entry;
	sess->fbuf = arg_bbuf->fbuf;
#endif
	uctx->dl_entry_func = arg_bbuf->dl_entry;

	bb_free(arg_bbuf, sizeof(*arg));

	return TEE_SUCCESS;
}

  • scall_handle_ldelf()

    • Handle syscall according to ldelf_syscall_table:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    // core/kernel/syscall.c
    
    /*
     * The ldelf return, log, panic syscalls have the same functionality and syscall
     * number as the user TAs'. To avoid unnecessary code duplication, the ldelf SVC
     * handler doesn't implement separate functions for these.
     */
    static const struct syscall_entry ldelf_syscall_table[] = {
    	SYSCALL_ENTRY(syscall_sys_return),
    	SYSCALL_ENTRY(syscall_log),
    	SYSCALL_ENTRY(syscall_panic),
    	SYSCALL_ENTRY(ldelf_syscall_map_zi),
    	SYSCALL_ENTRY(ldelf_syscall_unmap),
    	SYSCALL_ENTRY(ldelf_syscall_open_bin),
    	SYSCALL_ENTRY(ldelf_syscall_close_bin),
    	SYSCALL_ENTRY(ldelf_syscall_map_bin),
    	SYSCALL_ENTRY(ldelf_syscall_copy_from_bin),
    	SYSCALL_ENTRY(ldelf_syscall_set_prot),
    	SYSCALL_ENTRY(ldelf_syscall_remap),
    	SYSCALL_ENTRY(ldelf_syscall_gen_rnd_num),
    };
    

  • _ldelf_start()
    • ldelf()
      • ta_elf_load_main()
        • load_main()
          • init_elf()
    • _ldelf_return()
      • Issue LDELF_RETURN syscall, which will eventually call syscall_sys_return()

  • ldelf_syscall_open_bin()
    • If the session is for user TAs, look up user TA ELF by UUID. Currently, there are three ways (TA storages) to load user TA in OP-TEE:


      • Each TA storage is registered by REGISTER_TA_STORE() and is stored into ta_stores scattered array in the priority order, e.g.
        • Early TA:

           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
          
          // core/kernel/early_ta.c
          
          REGISTER_TA_STORE(2) = {
          	.description = "early TA",
          	.open = early_ta_open,
          	.get_size = emb_ts_get_size,
          	.get_tag = emb_ts_get_tag,
          	.read = emb_ts_read,
          	.close = emb_ts_close,
          };
          
        • Secure Storage TA:

           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
          
          // core/kernel/secstor_ta.c
          
          REGISTER_TA_STORE(4) = {
          	.description = "Secure Storage TA",
          	.open = secstor_ta_open,
          	.get_size = secstor_ta_get_size,
          	.get_tag = secstor_ta_get_tag,
          	.read = secstor_ta_read,
          	.close = secstor_ta_close,
          };
          
        • REE filesystem TA:

           1
           2
           3
           4
           5
           6
           7
           8
           9
          10
          
          // core/kernel/ree_fs_ta.c
          
          REGISTER_TA_STORE(9) = {
          	.description = "REE",
          	.open = ree_fs_ta_open,
          	.get_size = ree_fs_ta_get_size,
          	.get_tag = ree_fs_ta_get_tag,
          	.read = ree_fs_ta_read,
          	.close = ree_fs_ta_close,
          };
          
    • Iterates each TA storage, call their op->open() callback, e.g. ree_fs_ta_open() .

      • Each TA storage’s op->open() is responsible for comparing UUID (either by itself or through thread RPC) to determine whether user TA for UUID can be found or not.
        • Returns TEE_ERROR_ITEM_NOT_FOUND if user TA for UUID cannot be found.