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