1 /*
2 * Copyright 2025 The Hafnium Authors.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/BSD-3-Clause.
7 */
8
9 #include "hf/arch/ffa.h"
10 #include "hf/arch/other_world.h"
11
12 #include "hf/check.h"
13 #include "hf/dlog.h"
14 #include "hf/ffa/setup_and_discovery.h"
15 #include "hf/vcpu.h"
16 #include "hf/vm.h"
17
18 static bool ffa_tee_enabled = false;
19
ffa_init_is_tee_enabled(void)20 bool ffa_init_is_tee_enabled(void)
21 {
22 return ffa_tee_enabled;
23 }
24
ffa_init_set_tee_enabled(bool tee_enabled)25 void ffa_init_set_tee_enabled(bool tee_enabled)
26 {
27 ffa_tee_enabled = tee_enabled;
28 }
29
ffa_init_log(void)30 void ffa_init_log(void)
31 {
32 dlog_info("Initializing Hafnium (Hypervisor)\n");
33 }
34
ffa_init(struct mpool * ppool)35 void ffa_init(struct mpool *ppool)
36 {
37 struct vm *other_world_vm = vm_find(HF_OTHER_WORLD_ID);
38 struct ffa_value ret;
39 struct mm_stage1_locked mm_stage1_locked;
40
41 /* This is a segment from TDRAM for the NS memory in the FVP platform.
42 *
43 * TODO: We ought to provide a better way to do this, if porting the
44 * hypervisor to other platforms. One option would be to provide this
45 * via DTS.
46 */
47 const uint64_t start = 0x90000000;
48 const uint64_t len = 0x60000000;
49 const paddr_t send_addr = pa_init(start + len - PAGE_SIZE * 1);
50 const paddr_t recv_addr = pa_init(start + len - PAGE_SIZE * 2);
51
52 (void)ppool;
53
54 if (!ffa_init_is_tee_enabled()) {
55 return;
56 }
57
58 CHECK(other_world_vm != NULL);
59
60 arch_ffa_init();
61
62 /*
63 * Call FFA_VERSION so the SPMC can store the hypervisor's
64 * version. This may be useful if there is a mismatch of
65 * versions.
66 */
67 ret = arch_other_world_call((struct ffa_value){
68 .func = FFA_VERSION_32, .arg1 = FFA_VERSION_COMPILED});
69 if (ret.func == FFA_NOT_SUPPORTED) {
70 panic("Hypervisor and SPMC versions are not compatible.\n");
71 }
72
73 /*
74 * Setup TEE VM RX/TX buffers.
75 * Using the following hard-coded addresses, as they must be within the
76 * NS memory node in the SPMC manifest. From that region we should
77 * exclude the Hypervisor's address space to prevent SPs from using that
78 * memory in memory region nodes, or for the NWd to misuse that memory
79 * in runtime via memory sharing interfaces.
80 */
81
82 // NOLINTNEXTLINE(performance-no-int-to-ptr)
83 other_world_vm->mailbox.send = (void *)pa_addr(send_addr);
84 // NOLINTNEXTLINE(performance-no-int-to-ptr)
85 other_world_vm->mailbox.recv = (void *)pa_addr(recv_addr);
86
87 /*
88 * Note that send and recv are swapped around, as the send buffer from
89 * Hafnium's perspective is the recv buffer from the EL3 dispatcher's
90 * perspective and vice-versa.
91 */
92 dlog_verbose("Setting up buffers for TEE.\n");
93 ffa_setup_rxtx_map_spmc(
94 pa_from_va(va_from_ptr(other_world_vm->mailbox.recv)),
95 pa_from_va(va_from_ptr(other_world_vm->mailbox.send)),
96 HF_MAILBOX_SIZE / FFA_PAGE_SIZE);
97
98 ffa_init_set_tee_enabled(true);
99
100 /*
101 * Hypervisor will write to secure world receive buffer, and will read
102 * from the secure world send buffer.
103 *
104 * Mapping operation is necessary because the ranges are outside of the
105 * hypervisor's binary.
106 */
107 mm_stage1_locked = mm_lock_stage1();
108 CHECK(mm_identity_map(mm_stage1_locked, send_addr,
109 pa_add(send_addr, PAGE_SIZE),
110 MM_MODE_R | MM_MODE_SHARED, ppool) != NULL);
111 CHECK(mm_identity_map(
112 mm_stage1_locked, recv_addr, pa_add(recv_addr, PAGE_SIZE),
113 MM_MODE_R | MM_MODE_W | MM_MODE_SHARED, ppool) != NULL);
114 mm_unlock_stage1(&mm_stage1_locked);
115
116 dlog_verbose("TEE finished setting up buffers.\n");
117 }
118