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