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