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