1 /*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6 #include <config.h>
7
8 #ifdef CONFIG_ARM_SMMU
9 #include <arch/object/smmu.h>
10
checkARMCBVspace(cap_t cap)11 static exception_t checkARMCBVspace(cap_t cap)
12 {
13 word_t cb = cap_cb_cap_get_capCB(cap);
14 cte_t *cbSlot = smmuStateCBNode + cb;
15 if (unlikely(!isVTableRoot(cbSlot->cap))) {
16 return EXCEPTION_SYSCALL_ERROR;
17 }
18 return EXCEPTION_NONE;
19 }
20
decodeARMSIDControlInvocation(word_t label,unsigned int length,cptr_t cptr,cte_t * srcSlot,cap_t cap,bool_t call,word_t * buffer)21 exception_t decodeARMSIDControlInvocation(word_t label, unsigned int length, cptr_t cptr,
22 cte_t *srcSlot, cap_t cap, bool_t call, word_t *buffer)
23 {
24
25 word_t index, depth, sid;
26 cte_t *destSlot;
27 cap_t cnodeCap;
28 lookupSlot_ret_t lu_ret;
29 exception_t status;
30 uint32_t faultStatus, faultSyndrome_0, faultSyndrome_1;
31
32 if (label == ARMSIDGetFault) {
33 smmu_read_fault_state(&faultStatus, &faultSyndrome_0, &faultSyndrome_1);
34 setRegister(NODE_STATE(ksCurThread), msgRegisters[0], faultStatus);
35 setRegister(NODE_STATE(ksCurThread), msgRegisters[1], faultSyndrome_0);
36 setRegister(NODE_STATE(ksCurThread), msgRegisters[2], faultSyndrome_1);
37 setRegister(NODE_STATE(ksCurThread), msgInfoRegister,
38 wordFromMessageInfo(seL4_MessageInfo_new(0, 0, 0, 3)));
39 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
40 return EXCEPTION_NONE;
41 }
42
43 if (label == ARMSIDClearFault) {
44 smmu_clear_fault_state();
45 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
46 return EXCEPTION_NONE;
47 }
48
49 if (label != ARMSIDIssueSIDManager) {
50 userError("SIDControl: Illegal operation.");
51 current_syscall_error.type = seL4_IllegalOperation;
52 return EXCEPTION_SYSCALL_ERROR;
53 }
54 if (length < 3 || current_extra_caps.excaprefs[0] == NULL) {
55 current_syscall_error.type = seL4_TruncatedMessage;
56 return EXCEPTION_SYSCALL_ERROR;
57 }
58
59 sid = getSyscallArg(0, buffer);
60 index = getSyscallArg(1, buffer);
61 depth = getSyscallArg(2, buffer);
62 cnodeCap = current_extra_caps.excaprefs[0]->cap;
63
64 if (sid >= SMMU_MAX_SID) {
65 current_syscall_error.type = seL4_RangeError;
66 current_syscall_error.rangeErrorMin = 0;
67 current_syscall_error.rangeErrorMax = SMMU_MAX_SID - 1;
68 userError("Rejecting request for SID %u. SID is greater than or equal to SMMU_MAX_SID.", (int)sid);
69 return EXCEPTION_SYSCALL_ERROR;
70 }
71 if (smmuStateSIDTable[sid]) {
72 current_syscall_error.type = seL4_RevokeFirst;
73 userError("Rejecting request for SID %u. Already active.", (int)sid);
74 return EXCEPTION_SYSCALL_ERROR;
75 }
76
77 lu_ret = lookupTargetSlot(cnodeCap, index, depth);
78 if (lu_ret.status != EXCEPTION_NONE) {
79 userError("Target slot for new SID Handler cap invalid: cap %lu, SID %u.",
80 getExtraCPtr(buffer, 0), (int)sid);
81 return lu_ret.status;
82 }
83 destSlot = lu_ret.slot;
84 status = ensureEmptySlot(destSlot);
85 if (status != EXCEPTION_NONE) {
86 userError("Target slot for new SID Handler cap not empty: cap %lu, SID %u.",
87 getExtraCPtr(buffer, 0), (int)sid);
88 return status;
89 }
90 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
91 smmuStateSIDTable[sid] = true;
92 cteInsert(cap_sid_cap_new(sid), srcSlot, destSlot);
93 return EXCEPTION_NONE;
94 }
95
decodeARMSIDInvocation(word_t label,unsigned int length,cptr_t cptr,cte_t * srcSlot,cap_t cap,bool_t call,word_t * buffer)96 exception_t decodeARMSIDInvocation(word_t label, unsigned int length, cptr_t cptr,
97 cte_t *srcSlot, cap_t cap, bool_t call, word_t *buffer)
98 {
99 cap_t cbCap;
100 cte_t *cbCapSlot;
101 cte_t *cbAssignSlot;
102 exception_t status;
103 word_t sid;
104
105 switch (label) {
106 case ARMSIDBindCB:
107 if (unlikely(current_extra_caps.excaprefs[0] == NULL)) {
108 userError("ARMSIDBindCB: Invalid CB cap.");
109 current_syscall_error.type = seL4_TruncatedMessage;
110 return EXCEPTION_SYSCALL_ERROR;
111 }
112 cbCapSlot = current_extra_caps.excaprefs[0];
113 cbCap = cbCapSlot->cap;
114 if (unlikely(cap_get_capType(cbCap) != cap_cb_cap)) {
115 userError("ARMSIDBindCB: Invalid CB cap.");
116 current_syscall_error.type = seL4_InvalidCapability;
117 current_syscall_error.invalidCapNumber = 1;
118 return EXCEPTION_SYSCALL_ERROR;
119 }
120 if (unlikely(checkARMCBVspace(cbCap) != EXCEPTION_NONE)) {
121 userError("ARMSIDBindCB: Invalid CB cap.");
122 current_syscall_error.type = seL4_InvalidCapability;
123 current_syscall_error.invalidCapNumber = 1;
124 return EXCEPTION_SYSCALL_ERROR;
125 }
126 sid = cap_sid_cap_get_capSID(cap);
127 cbAssignSlot = smmuStateSIDNode + sid;
128 status = ensureEmptySlot(cbAssignSlot);
129 if (status != EXCEPTION_NONE) {
130 userError("ARMSIDBindCB: The SID is already bound with a context bank.");
131 return status;
132 }
133 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
134 /*binding the sid to cb in SMMU*/
135 smmu_sid_bind_cb(sid, cap_cb_cap_get_capCB(cbCap));
136 /* Building the connection between SID and CB caps by placing a
137 * copy of the given cb cap in sid's cnode*/
138 cteInsert(cbCap, cbCapSlot, cbAssignSlot);
139 /* Recording the SID number in the copied CB cap.
140 * Deleting the copied CB cap will trigger unbinding
141 * operations. As a CB can be used (bound)
142 * by multiple SID caps, each copied CB caps resulted from
143 * binding operations keeps track of its serving SID numbers.*/
144 cap_cb_cap_ptr_set_capBindSID(&(cbAssignSlot->cap), sid);
145 return EXCEPTION_NONE;
146
147 case ARMSIDUnbindCB:
148 sid = cap_sid_cap_get_capSID(cap);
149 cbAssignSlot = smmuStateSIDNode + sid;
150 if (unlikely(cap_get_capType(cbAssignSlot->cap) != cap_cb_cap)) {
151 userError("ARMSIDUnbindCB: The SID is not assigned with a context bank.");
152 current_syscall_error.type = seL4_IllegalOperation;
153 return EXCEPTION_SYSCALL_ERROR;
154 }
155 status = cteDelete(cbAssignSlot, true);
156 if (unlikely(status != EXCEPTION_NONE)) {
157 userError("ARMSIDUnbindCB: the Assigned context bank cannot be unassigned.");
158 return status;
159 }
160 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
161 return EXCEPTION_NONE;
162 default:
163 userError("ARMSID: Illegal operation.");
164 current_syscall_error.type = seL4_IllegalOperation;
165 return EXCEPTION_SYSCALL_ERROR;
166 }
167 }
168
smmu_delete_sid(cap_t cap)169 exception_t smmu_delete_sid(cap_t cap)
170 {
171 word_t sid = cap_sid_cap_get_capSID(cap);
172 cte_t *cbAssignSlot = smmuStateSIDNode + sid;
173 exception_t status = EXCEPTION_NONE;
174 /*deleting the assigned context bank cap if exsits*/
175 if (unlikely(cap_get_capType(cbAssignSlot->cap) == cap_cb_cap)) {
176 status = cteDelete(cbAssignSlot, true);
177 }
178 smmuStateSIDTable[sid] = false;
179 return status;
180 }
181
decodeARMCBControlInvocation(word_t label,unsigned int length,cptr_t cptr,cte_t * srcSlot,cap_t cap,bool_t call,word_t * buffer)182 exception_t decodeARMCBControlInvocation(word_t label, unsigned int length, cptr_t cptr,
183 cte_t *srcSlot, cap_t cap, bool_t call, word_t *buffer)
184 {
185
186 word_t index, depth, cb;
187 cte_t *destSlot;
188 cap_t cnodeCap;
189 lookupSlot_ret_t lu_ret;
190 exception_t status;
191
192 if (label == ARMCBTLBInvalidateAll) {
193 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
194 smmu_tlb_invalidate_all();
195 return EXCEPTION_NONE;
196 }
197
198 if (label != ARMCBIssueCBManager) {
199 userError("ARMCBControl: Illegal operation.");
200 current_syscall_error.type = seL4_IllegalOperation;
201 return EXCEPTION_SYSCALL_ERROR;
202 }
203 if (length < 3 || current_extra_caps.excaprefs[0] == NULL) {
204 current_syscall_error.type = seL4_TruncatedMessage;
205 return EXCEPTION_SYSCALL_ERROR;
206 }
207
208 cb = getSyscallArg(0, buffer);
209 index = getSyscallArg(1, buffer);
210 depth = getSyscallArg(2, buffer);
211 cnodeCap = current_extra_caps.excaprefs[0]->cap;
212
213 if (cb >= SMMU_MAX_CB) {
214 current_syscall_error.type = seL4_RangeError;
215 current_syscall_error.rangeErrorMin = 0;
216 current_syscall_error.rangeErrorMax = SMMU_MAX_CB - 1;
217 userError("Rejecting request for CB %u. CB is greater than or equal to SMMU_MAX_CB.", (int)cb);
218 return EXCEPTION_SYSCALL_ERROR;
219 }
220 if (smmuStateCBTable[cb]) {
221 current_syscall_error.type = seL4_RevokeFirst;
222 userError("Rejecting request for CB %u. Already active.", (int)cb);
223 return EXCEPTION_SYSCALL_ERROR;
224 }
225
226 lu_ret = lookupTargetSlot(cnodeCap, index, depth);
227 if (lu_ret.status != EXCEPTION_NONE) {
228 userError("Target slot for new CB Handler cap invalid: cap %lu, CB %u.",
229 getExtraCPtr(buffer, 0), (int)cb);
230 return lu_ret.status;
231 }
232 destSlot = lu_ret.slot;
233 status = ensureEmptySlot(destSlot);
234 if (status != EXCEPTION_NONE) {
235 userError("Target slot for new CB Handler cap not empty: cap %lu, CB %u.",
236 getExtraCPtr(buffer, 0), (int)cb);
237 return status;
238 }
239
240 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
241 smmuStateCBTable[cb] = true;
242 cteInsert(cap_cb_cap_new(SID_INVALID, cb), srcSlot, destSlot);
243 return EXCEPTION_NONE;
244 }
245
decodeARMCBInvocation(word_t label,unsigned int length,cptr_t cptr,cte_t * srcSlot,cap_t cap,bool_t call,word_t * buffer)246 exception_t decodeARMCBInvocation(word_t label, unsigned int length, cptr_t cptr,
247 cte_t *srcSlot, cap_t cap, bool_t call, word_t *buffer)
248 {
249
250 cap_t vspaceCap;
251 cte_t *vspaceCapSlot;
252 cte_t *cbSlot;
253 exception_t status;
254 word_t cb;
255 uint32_t faultStatus;
256 word_t faultAddress;
257
258 switch (label) {
259 case ARMCBTLBInvalidate:
260 if (unlikely(checkARMCBVspace(cap) != EXCEPTION_NONE)) {
261 userError("ARMCBTLBInvalidate: the CB does not have a vspace root.");
262 current_syscall_error.type = seL4_IllegalOperation;
263 return EXCEPTION_SYSCALL_ERROR;
264 }
265 cb = cap_cb_cap_get_capCB(cap);
266 cbSlot = smmuStateCBNode + cb;
267 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
268 smmu_tlb_invalidate_cb(cb, cap_vtable_root_get_mappedASID(cbSlot->cap));
269 return EXCEPTION_NONE;
270
271 case ARMCBAssignVspace:
272 if (unlikely(current_extra_caps.excaprefs[0] == NULL)) {
273 current_syscall_error.type = seL4_TruncatedMessage;
274 return EXCEPTION_SYSCALL_ERROR;
275 }
276
277 vspaceCapSlot = current_extra_caps.excaprefs[0];
278 vspaceCap = vspaceCapSlot->cap;
279
280 if (unlikely(!isVTableRoot(vspaceCap) || !cap_vtable_root_isMapped(vspaceCap))) {
281 userError("ARMCBAssignVspace: the vspace is invalid");
282 current_syscall_error.type = seL4_InvalidCapability;
283 current_syscall_error.invalidCapNumber = 1;
284 return EXCEPTION_SYSCALL_ERROR;
285 }
286
287 /*the cb number must be valid as it is created via the ARMCBIssueCBManager*/
288 cb = cap_cb_cap_get_capCB(cap);
289 cbSlot = smmuStateCBNode + cb;
290 status = ensureEmptySlot(cbSlot);
291 if (status != EXCEPTION_NONE) {
292 userError("ARMCBAssignVspace: the CB already assigned with a vspace root.");
293 return status;
294 }
295
296 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
297 /*setting up vspace for the context bank in SMMU*/
298 smmu_cb_assign_vspace(cb, cap_vtable_root_get_basePtr(vspaceCap),
299 cap_vtable_root_get_mappedASID(vspaceCap));
300 /*Connecting vspace cap to context bank*/
301 cteInsert(vspaceCap, vspaceCapSlot, cbSlot);
302 cap_vtable_root_ptr_set_mappedCB(&(cbSlot->cap), cb);
303 /*set relationship between CB and ASID*/
304 smmuStateCBAsidTable[cb] = cap_vtable_root_get_mappedASID(vspaceCap);
305 increaseASIDBindCB(cap_vtable_root_get_mappedASID(vspaceCap));
306 return EXCEPTION_NONE;
307
308 case ARMCBUnassignVspace:
309 if (unlikely(checkARMCBVspace(cap) != EXCEPTION_NONE)) {
310 userError("ARMCBUnassignVspace: the CB does not have an assigned VSpace.");
311 current_syscall_error.type = seL4_IllegalOperation;
312 return EXCEPTION_SYSCALL_ERROR;
313 }
314 cb = cap_cb_cap_get_capCB(cap);
315 cbSlot = smmuStateCBNode + cb;
316 status = cteDelete(cbSlot, true);
317 if (unlikely(status != EXCEPTION_NONE)) {
318 userError("ARMCBUnassignVspace: the Assigned VSpace cannot be deleted.");
319 return status;
320 }
321 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
322 return EXCEPTION_NONE;
323
324 case ARMCBGetFault:
325 smmu_cb_read_fault_state(cap_cb_cap_get_capCB(cap), &faultStatus, &faultAddress);
326 setRegister(NODE_STATE(ksCurThread), msgRegisters[0], faultStatus);
327 setRegister(NODE_STATE(ksCurThread), msgRegisters[1], faultAddress);
328 setRegister(NODE_STATE(ksCurThread), msgInfoRegister,
329 wordFromMessageInfo(seL4_MessageInfo_new(0, 0, 0, 2)));
330 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
331 return EXCEPTION_NONE;
332
333 case ARMCBClearFault:
334 smmu_cb_clear_fault_state(cap_cb_cap_get_capCB(cap));
335 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
336 return EXCEPTION_NONE;
337
338 default:
339 userError("ARMCBInvocation: Illegal operation.");
340 current_syscall_error.type = seL4_IllegalOperation;
341 return EXCEPTION_SYSCALL_ERROR;
342 }
343 }
344
345
smmu_delete_cb(cap_t cap)346 exception_t smmu_delete_cb(cap_t cap)
347 {
348 word_t cb = cap_cb_cap_get_capCB(cap);
349 cte_t *cbSlot;
350 exception_t status = EXCEPTION_NONE;
351 /*deleting assigned vspace root if exists*/
352 if (unlikely(checkARMCBVspace(cap) == EXCEPTION_NONE)) {
353 cbSlot = smmuStateCBNode + cb;
354 /*the relationship between CB and ASID is reset at the vspace deletion
355 triggered by the cteDelete*/
356 status = cteDelete(cbSlot, true);
357 }
358 smmuStateCBTable[cb] = false;
359 return status;
360 }
361
smmu_cb_delete_vspace(word_t cb,asid_t asid)362 void smmu_cb_delete_vspace(word_t cb, asid_t asid)
363 {
364 /* Deleting the vsapce cap stored in context bank's CNode, causing:
365 * -reset the relationship between context bank and vspace's ASID
366 * -disabe the context bank as its vspace no longer exists*/
367 smmuStateCBAsidTable[cb] = ASID_INVALID;
368 decreaseASIDBindCB(asid);
369 smmu_cb_disable(cb, asid);
370 }
371
invalidateSMMUTLBByASID(asid_t asid,word_t bind_cb)372 void invalidateSMMUTLBByASID(asid_t asid, word_t bind_cb)
373 {
374 /* Due to the requirement of one vspace (ASID) can be shared by
375 * multiple threads and drivers, there is no obvious way to
376 * directly locate all context banks associated with a given ASID without a
377 * serch. Another possible solution is representing all context banks in
378 * bitmaps, which also requires a search. This operation can only be triggered
379 * by ASID invalidation or similar operations, hence the performance is not a major issue.*/
380 for (int cb = 0; cb < SMMU_MAX_CB && bind_cb; cb++) {
381 if (unlikely(smmuStateCBAsidTable[cb] == asid)) {
382 smmu_tlb_invalidate_cb(cb, asid);
383 bind_cb--;
384 }
385 }
386 }
387
invalidateSMMUTLBByASIDVA(asid_t asid,vptr_t vaddr,word_t bind_cb)388 void invalidateSMMUTLBByASIDVA(asid_t asid, vptr_t vaddr, word_t bind_cb)
389 {
390 /* Implemeneted in the same way as invalidateSMMUTLBByASID */
391 for (int cb = 0; cb < SMMU_MAX_CB && bind_cb; cb++) {
392 if (unlikely(smmuStateCBAsidTable[cb] == asid)) {
393 smmu_tlb_invalidate_cb_va(cb, asid, vaddr);
394 bind_cb--;
395 }
396 }
397 }
398
399 #endif
400
401