1 /*
2 * SPDX-License-Identifier: BSD-3-Clause
3 * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4 */
5
6 #include <buffer.h>
7 #include <granule.h>
8 #include <realm.h>
9 #include <rsi-host-call.h>
10 #include <smc-rsi.h>
11 #include <status.h>
12 #include <string.h>
13
14 /*
15 * If the RIPAS of the target IPA is empty then return value is RSI_ERROR_INPUT.
16 *
17 * If the RTT walk fails then:
18 * - @rsi_walk_result.abort is true and @rsi_walk_result.rtt_level is the
19 * last level reached by the walk.
20 * - Return value is RSI_SUCCESS.
21 *
22 * If the RTT walk succeeds then:
23 * - If @rec_exit is not NULL and @rec_entry is NULL, then copy host call
24 * arguments from host call data structure (in Realm memory) to @rec_exit.
25 * - If @rec_exit is NULL and @rec_entry is not NULL, then copy host call
26 * results to host call data structure (in Realm memory).
27 * - Return value is RSI_SUCCESS.
28 */
do_host_call(struct rec * rec,struct rmi_rec_exit * rec_exit,struct rmi_rec_entry * rec_entry,struct rsi_walk_result * rsi_walk_result)29 static unsigned int do_host_call(struct rec *rec,
30 struct rmi_rec_exit *rec_exit,
31 struct rmi_rec_entry *rec_entry,
32 struct rsi_walk_result *rsi_walk_result)
33 {
34 enum s2_walk_status walk_status;
35 struct s2_walk_result walk_result;
36 unsigned long ipa = rec->regs[1];
37 unsigned long page_ipa;
38 struct rd *rd;
39 struct granule *gr;
40 unsigned char *data;
41 struct rsi_host_call *host_call;
42 unsigned int i;
43 unsigned int ret = RSI_SUCCESS;
44
45 assert(addr_in_rec_par(rec, ipa));
46
47 /* Only 'rec_entry' or 'rec_exit' should be set */
48 assert((rec_entry != NULL) ^ (rec_exit != NULL));
49
50 rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
51
52 page_ipa = ipa & GRANULE_MASK;
53 walk_status = realm_ipa_to_pa(rd, page_ipa, &walk_result);
54
55 switch (walk_status) {
56 case WALK_SUCCESS:
57 break;
58 case WALK_FAIL:
59 if (s2_walk_result_match_ripas(&walk_result, RMI_EMPTY)) {
60 ret = RSI_ERROR_INPUT;
61 } else {
62 rsi_walk_result->abort = true;
63 rsi_walk_result->rtt_level = walk_result.rtt_level;
64 }
65 goto out;
66 case WALK_INVALID_PARAMS:
67 assert(false);
68 break;
69 }
70
71 /* Map Realm data granule to RMM address space */
72 gr = find_granule(walk_result.pa);
73 data = (unsigned char *)granule_map(gr, SLOT_RSI_CALL);
74 host_call = (struct rsi_host_call *)(data + (ipa - page_ipa));
75
76 if (rec_exit != NULL) {
77 /* Copy host call arguments to REC exit data structure */
78 rec_exit->imm = host_call->imm;
79 for (i = 0U; i < RSI_HOST_CALL_NR_GPRS; i++) {
80 rec_exit->gprs[i] = host_call->gprs[i];
81 }
82 } else {
83 /* Copy host call results to host call data structure */
84 for (i = 0U; i < RSI_HOST_CALL_NR_GPRS; i++) {
85 host_call->gprs[i] = rec_entry->gprs[i];
86 }
87 }
88
89 /* Unmap Realm data granule */
90 buffer_unmap(data);
91
92 /* Unlock last level RTT */
93 granule_unlock(walk_result.llt);
94
95 out:
96 buffer_unmap(rd);
97 return ret;
98 }
99
handle_rsi_host_call(struct rec * rec,struct rmi_rec_exit * rec_exit)100 struct rsi_host_call_result handle_rsi_host_call(struct rec *rec,
101 struct rmi_rec_exit *rec_exit)
102 {
103 struct rsi_host_call_result res = { { false, 0UL } };
104 unsigned long ipa = rec->regs[1];
105
106 if (!ALIGNED(ipa, sizeof(struct rsi_host_call))) {
107 res.smc_result = RSI_ERROR_INPUT;
108 return res;
109 }
110
111 if ((ipa / GRANULE_SIZE) !=
112 ((ipa + sizeof(struct rsi_host_call) - 1UL) / GRANULE_SIZE)) {
113 res.smc_result = RSI_ERROR_INPUT;
114 return res;
115 }
116
117 if (!addr_in_rec_par(rec, ipa)) {
118 res.smc_result = RSI_ERROR_INPUT;
119 return res;
120 }
121
122 res.smc_result = do_host_call(rec, rec_exit, NULL, &res.walk_result);
123
124 return res;
125 }
126
complete_rsi_host_call(struct rec * rec,struct rmi_rec_entry * rec_entry)127 struct rsi_walk_result complete_rsi_host_call(struct rec *rec,
128 struct rmi_rec_entry *rec_entry)
129 {
130 struct rsi_walk_result res = { false, 0UL };
131
132 /*
133 * Do the necessary to walk the S2 RTTs and copy args from NS Host
134 * to the host call data structure. But it is possible for the
135 * RIPAS of the IPA to be EMPTY and hence this call can return
136 * RSI_ERROR_INPUT. In this case, we return RSI_SUCCESS to Realm
137 * and Realm may take an abort on accessing the IPA (depending on
138 * the RIPAS of IPA at that time). This is a situation which can be
139 * controlled from Realm and Realm should avoid this.
140 */
141 (void)do_host_call(rec, NULL, rec_entry, &res);
142
143 return res;
144 }
145