blob: 3f701468e7a52ca2c32bd183f39a9117888c39e1 [file] [log] [blame]
Soby Mathewb4c6df42022-11-09 11:13:29 +00001/*
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 @exit is not NULL and @entry is NULL, then copy host call arguments
24 * from host call data structure (in Realm memory) to @exit.
25 * - If @exit is NULL and @entry is not NULL, then copy host call results to
26 * host call data structure (in Realm memory).
27 * - Return value is RSI_SUCCESS.
28 */
29static 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 assert(((unsigned long)rec_entry | (unsigned long)rec_exit) != 0UL);
47
48 rd = granule_map(rec->realm_info.g_rd, SLOT_RD);
49
50 page_ipa = ipa & GRANULE_MASK;
51 walk_status = realm_ipa_to_pa(rd, page_ipa, &walk_result);
52
53 switch (walk_status) {
54 case WALK_SUCCESS:
55 break;
56 case WALK_FAIL:
57 if (s2_walk_result_match_ripas(&walk_result, RMI_EMPTY)) {
58 ret = RSI_ERROR_INPUT;
59 } else {
60 rsi_walk_result->abort = true;
61 rsi_walk_result->rtt_level = walk_result.rtt_level;
62 }
63 goto out;
64 case WALK_INVALID_PARAMS:
65 assert(false);
66 break;
67 }
68
69 /* Map Realm data granule to RMM address space */
70 gr = find_granule(walk_result.pa);
71 data = (unsigned char *)granule_map(gr, SLOT_RSI_CALL);
72 host_call = (struct rsi_host_call *)(data + (ipa - page_ipa));
73
74 if (rec_exit != NULL) {
75 /* Copy host call arguments to REC exit data structure */
76 rec_exit->imm = host_call->imm;
77 for (i = 0U; i < RSI_HOST_CALL_NR_GPRS; i++) {
78 rec_exit->gprs[i] = host_call->gprs[i];
79 }
80 }
81
82 if (rec_entry != NULL) {
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
95out:
96 buffer_unmap(rd);
97 return ret;
98}
99
100struct 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
127struct 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}