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
10 /**
11 * Translate a realm granule IPA to PA.
12 *
13 * Parameters:
14 * [in] rd Pointer to realm descriptor granule.
15 * [in] ipa The intermediate physical address of the realm granule.
16 * [in] s2_walk Address of s2_walk_result structure to return:
17 * [out] s2_walk.pa The physical address of the realm granule.
18 * [out] s2_walk.rtt_level The last level reached by the table walk.
19 * [out] s2_walk.ripas RIPAS of s2tte.
20 * [out] s2_walk.destroyed 'true', if s2tte has HIPAS=DESTROYED.
21 * [out] s2_walk.llt Pointer to the last level page table which contains
22 * the mapping of the granule. If function returns with
23 * WALK_SUCCESS then 'llt' must be unlocked by the caller.
24 * Lock avoids to destoy the realm granule while RMM
25 * accessing to it.
26 * Returns:
27 * WALK_SUCCESS Translation succeeded.
28 * WALK_INVALID_PARAMS Parameter 'ipa' is unaligned or is not a Protected IPA.
29 * WALK_FAIL Mapping is not in the page table. NS Host needs to fix.
30 */
realm_ipa_to_pa(struct rd * rd,unsigned long ipa,struct s2_walk_result * s2_walk)31 enum s2_walk_status realm_ipa_to_pa(struct rd *rd,
32 unsigned long ipa,
33 struct s2_walk_result *s2_walk)
34 {
35 struct granule *g_table_root;
36 struct rtt_walk wi;
37 unsigned long s2tte, *ll_table, offset;
38 enum s2_walk_status walk_status;
39
40 if (!GRANULE_ALIGNED(ipa) || !addr_in_par(rd, ipa)) {
41 return WALK_INVALID_PARAMS;
42 }
43
44 /*
45 * SW table walk to find corresponding PA. It handles cases when buffer
46 * is mapped on page level or on block level.
47 *
48 * Todo:
49 * - Page mapping is assumed.
50 */
51 g_table_root = rd->s2_ctx.g_rtt;
52 granule_lock(g_table_root, GRANULE_STATE_RTT);
53 rtt_walk_lock_unlock(g_table_root,
54 realm_rtt_starting_level(rd),
55 realm_ipa_bits(rd),
56 ipa,
57 RTT_PAGE_LEVEL,
58 &wi);
59
60 ll_table = granule_map(wi.g_llt, SLOT_RTT);
61
62 /* Must be unlocked by caller */
63 s2_walk->llt = wi.g_llt;
64 s2tte = s2tte_read(&ll_table[wi.index]);
65
66 if (!s2tte_is_valid(s2tte, wi.last_level)) {
67 /*
68 * This 'tte' is still not been made valid by the Host.
69 * Depending on the RIPAS value, the caller needs to
70 * emulate a Data Abort back to the Host or return error
71 * back to Realm.
72 */
73 s2_walk->rtt_level = wi.last_level;
74 if (s2tte_is_destroyed(s2tte)) {
75 s2_walk->destroyed = true;
76 } else {
77 s2_walk->ripas = s2tte_get_ripas(s2tte);
78 }
79 granule_unlock(wi.g_llt);
80 walk_status = WALK_FAIL;
81 goto out_unmap_table;
82 }
83
84 s2_walk->pa = s2tte_pa(s2tte, wi.last_level);
85 offset = ipa & (s2tte_map_size(wi.last_level) - 1UL);
86 s2_walk->pa += offset;
87 s2_walk->ripas = RMI_RAM;
88
89 walk_status = WALK_SUCCESS;
90
91 out_unmap_table:
92 buffer_unmap(ll_table);
93 return walk_status;
94 }
95
96 /*
97 * Get RIPAS of IPA
98 *
99 * Parameters:
100 * [in] @rec: Pointer to the rec
101 * [in] @ipa: IPA for which RIPAS is queried.
102 * [out] @ripas_ptr: RIPAS value returned for the IPA. This is set in
103 * case of WALK_SUCCESS is returned.
104 * [out] @rtt_level: Value of last level reached by table walk. This
105 * is set in case of WALK_FAIL is returned.
106 * Returns:
107 * WALK_SUCCESS: RIPAS of IPA found
108 * WALK_FAIL: RIPAS of IPA not found. s2tte has HIPAS=DESTROYED
109 */
realm_ipa_get_ripas(struct rec * rec,unsigned long ipa,enum ripas * ripas_ptr,unsigned long * rtt_level)110 enum s2_walk_status realm_ipa_get_ripas(struct rec *rec, unsigned long ipa,
111 enum ripas *ripas_ptr,
112 unsigned long *rtt_level)
113 {
114 unsigned long s2tte, *ll_table;
115 struct rtt_walk wi;
116 enum s2_walk_status ws;
117
118 assert(ripas_ptr != NULL);
119 assert(rtt_level != NULL);
120 assert(GRANULE_ALIGNED(ipa));
121 assert(addr_in_rec_par(rec, ipa));
122
123 granule_lock(rec->realm_info.g_rtt, GRANULE_STATE_RTT);
124
125 rtt_walk_lock_unlock(rec->realm_info.g_rtt,
126 rec->realm_info.s2_starting_level,
127 rec->realm_info.ipa_bits,
128 ipa, RTT_PAGE_LEVEL, &wi);
129
130 ll_table = granule_map(wi.g_llt, SLOT_RTT);
131 s2tte = s2tte_read(&ll_table[wi.index]);
132
133 if (s2tte_is_destroyed(s2tte)) {
134 *rtt_level = wi.last_level;
135 /*
136 * The IPA has been destroyed by NS Host. Return data_abort back
137 * to NS Host and there is no recovery possible of this Rec
138 * after this.
139 */
140 ws = WALK_FAIL;
141 } else {
142 *ripas_ptr = s2tte_get_ripas(s2tte);
143 ws = WALK_SUCCESS;
144 }
145
146 buffer_unmap(ll_table);
147 granule_unlock(wi.g_llt);
148
149 return ws;
150 }
151