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