SMMU: supporting unassign vspace in context banks

Providing a system call that removes an assigned vspace root from its
context bank. This operation causes the context bank being disabled
as it does not have a valid vspace root after the unassignment.

Signed-off-by: Oliver Scott <Oliver.Scott@data61.csiro.au>
This commit is contained in:
Qian Ge
2020-01-15 10:45:01 +11:00
committed by Oliver Scott
parent cb0ef83a28
commit 38ed1046e1
5 changed files with 106 additions and 60 deletions

View File

@@ -422,4 +422,5 @@ void smmu_sid_bind_cb(word_t sid, word_t cb);
void plat_smmu_init(void);
void smmu_tlb_invalidate_all(void);
void smmu_tlb_invalidate_cb(int cb, asid_t asid);
void smmu_cb_disable(word_t cb, asid_t asid);

View File

@@ -357,6 +357,12 @@
<param dir="in" name="vspace" type="seL4_CPtr"
description="The VSpace that is being assigned to a context bank. Must already has an assigned ASID."/>
</method>
<method id="ARMCBUnassignVspace" name="UnassignVspace" manual_name="UnassignVspace"
manual_label="cb_unassignvspace">
<brief>
Unassigning a vspace to a context bank.
</brief>
</method>
<method id="ARMCBTLBInvalidate" name="TLBInvalidate" manual_name="TLBInvalidate"
manual_label="cb_tlbinvalidate">
<brief>

View File

@@ -152,6 +152,12 @@ finaliseCap_ret_t Arch_finaliseCap(cap_t cap, bool_t final)
deleteASID(cap_page_global_directory_cap_get_capPGDMappedASID(cap),
VSPACE_PTR(cap_page_global_directory_cap_get_capPGDBasePtr(cap)));
}
#ifdef CONFIG_ARM_SMMU
if (cap_page_global_directory_cap_get_capPGDMappedCB(cap) != CB_INVALID) {
smmu_cb_disable(cap_page_global_directory_cap_get_capPGDMappedCB(cap),
cap_page_global_directory_cap_get_capPGDMappedASID(cap));
}
#endif
break;
case cap_page_upper_directory_cap:
@@ -160,6 +166,12 @@ finaliseCap_ret_t Arch_finaliseCap(cap_t cap, bool_t final)
deleteASID(cap_page_upper_directory_cap_get_capPUDMappedASID(cap),
PUDE_PTR(cap_page_upper_directory_cap_get_capPUDBasePtr(cap)));
}
#ifdef CONFIG_ARM_SMMU
if (cap_page_upper_directory_cap_get_capPGDMappedCB(cap) != CB_INVALID) {
smmu_cb_disable(cap_page_upper_directory_cap_get_capPGDMappedCB(cap),
cap_page_upper_directory_cap_get_capPUDMappedASID(cap));
}
#endif
#else
if (final && cap_page_upper_directory_cap_get_capPUDIsMapped(cap)) {
unmapPageUpperDirectory(cap_page_upper_directory_cap_get_capPUDMappedASID(cap),

View File

@@ -43,13 +43,13 @@ exception_t decodeARMSIDControlInvocation(word_t label, unsigned int length, cpt
depth = getSyscallArg(2, buffer);
cnodeCap = extraCaps.excaprefs[0]->cap;
if (sid >= SMMU_MAX_SID) {
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = SMMU_MAX_SID - 1;
userError("Rejecting request for SID %u. SID is greater than or equal to SMMU_MAX_SID.", (int)sid);
return EXCEPTION_SYSCALL_ERROR;
}
if (sid >= SMMU_MAX_SID) {
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = SMMU_MAX_SID - 1;
userError("Rejecting request for SID %u. SID is greater than or equal to SMMU_MAX_SID.", (int)sid);
return EXCEPTION_SYSCALL_ERROR;
}
if (smmuStateSIDTable[sid]) {
current_syscall_error.type = seL4_RevokeFirst;
userError("Rejecting request for SID %u. Already active.", (int)sid);
@@ -71,7 +71,7 @@ exception_t decodeARMSIDControlInvocation(word_t label, unsigned int length, cpt
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
smmuStateSIDTable[sid] = true;
cteInsert(cap_sid_cap_new(sid), srcSlot, destSlot);
cteInsert(cap_sid_cap_new(sid), srcSlot, destSlot);
return EXCEPTION_NONE;
}
@@ -164,13 +164,13 @@ exception_t decodeARMCBControlInvocation(word_t label, unsigned int length, cptr
depth = getSyscallArg(2, buffer);
cnodeCap = extraCaps.excaprefs[0]->cap;
if (cb >= SMMU_MAX_CB) {
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = SMMU_MAX_CB - 1;
userError("Rejecting request for CB %u. CB is greater than or equal to SMMU_MAX_CB.", (int)cb);
return EXCEPTION_SYSCALL_ERROR;
}
if (cb >= SMMU_MAX_CB) {
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = SMMU_MAX_CB - 1;
userError("Rejecting request for CB %u. CB is greater than or equal to SMMU_MAX_CB.", (int)cb);
return EXCEPTION_SYSCALL_ERROR;
}
if (smmuStateCBTable[cb]) {
current_syscall_error.type = seL4_RevokeFirst;
userError("Rejecting request for CB %u. Already active.", (int)cb);
@@ -207,54 +207,73 @@ exception_t decodeARMCBInvocation(word_t label, unsigned int length, cptr_t cptr
exception_t status;
word_t cb;
if (label == ARMCBTLBInvalidate) {
if (unlikely(checkARMCBVspace(cap) != EXCEPTION_NONE)) {
userError("ARMCBTLBInvalidate: the CB does not have a vspace root.");
switch (label) {
case ARMCBTLBInvalidate:
if (unlikely(checkARMCBVspace(cap) != EXCEPTION_NONE)) {
userError("ARMCBTLBInvalidate: the CB does not have a vspace root.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
cb = cap_cb_cap_get_capCB(cap);
cbSlot = smmuStateCBNode + cb;
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
smmu_tlb_invalidate_cb(cb, cap_vtable_root_get_mappedASID(cbSlot->cap));
return EXCEPTION_NONE;
case ARMCBAssignVspace:
if (unlikely(extraCaps.excaprefs[0] == NULL)) {
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
vspaceCapSlot = extraCaps.excaprefs[0];
vspaceCap = vspaceCapSlot->cap;
if (unlikely(!isVTableRoot(vspaceCap) || !cap_vtable_root_isMapped(vspaceCap))) {
userError("ARMCBAssignVspace: the vspace is invalid");
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
/*the cb number must be valid as it is created via the ARMCBIssueCBManager*/
cb = cap_cb_cap_get_capCB(cap);
cbSlot = smmuStateCBNode + cb;
status = ensureEmptySlot(cbSlot);
if (status != EXCEPTION_NONE) {
userError("ARMCBAssignVspace: the CB already assigned with a vspace root.");
return status;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
/*setting up vspace for the context bank in SMMU*/
smmu_cb_assign_vspace(cb, cap_vtable_root_get_basePtr(vspaceCap),
cap_vtable_root_get_mappedASID(vspaceCap));
/*Connecting vspace cap to context bank*/
cteInsert(vspaceCap, vspaceCapSlot, cbSlot);
cap_vtable_root_ptr_set_mappedCB(&(cbSlot->cap), cb);
return EXCEPTION_NONE;
case ARMCBUnassignVspace:
if (unlikely(checkARMCBVspace(cap) != EXCEPTION_NONE)) {
userError("ARMCBUnassignVspace: the CB does not have an assigned VSpace.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
cb = cap_cb_cap_get_capCB(cap);
cbSlot = smmuStateCBNode + cb;
status = cteDelete(cbSlot, true);
if (unlikely(status != EXCEPTION_NONE)) {
userError("ARMCBUnassignVspace: the Assigned VSpace cannot be deleted.");
return status;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return EXCEPTION_NONE;
default:
userError("ARMCBInvocation: Illegal operation.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
cb = cap_cb_cap_get_capCB(cap);
cbSlot = smmuStateCBNode + cb;
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
smmu_tlb_invalidate_cb(cb, cap_vtable_root_get_mappedASID(cbSlot->cap));
return EXCEPTION_NONE;
}
if (unlikely(label != ARMCBAssignVspace)) {
userError("ARMCBAssignVspace: Illegal operation.");
current_syscall_error.type = seL4_IllegalOperation;
return EXCEPTION_SYSCALL_ERROR;
}
if (unlikely(extraCaps.excaprefs[0] == NULL)) {
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
vspaceCapSlot = extraCaps.excaprefs[0];
vspaceCap = vspaceCapSlot->cap;
if (unlikely(!isVTableRoot(vspaceCap) || !cap_vtable_root_isMapped(vspaceCap))) {
userError("ARMCBAssignVspace: the vspace is invalid");
current_syscall_error.type = seL4_InvalidCapability;
current_syscall_error.invalidCapNumber = 1;
return EXCEPTION_SYSCALL_ERROR;
}
/*the cb number must be valid as it is created via the ARMCBIssueCBManager*/
cb = cap_cb_cap_get_capCB(cap);
cbSlot = smmuStateCBNode + cb;
status = ensureEmptySlot(cbSlot);
if (status != EXCEPTION_NONE) {
userError("ARMCBAssignVspace: the CB already assigned with a vspace root.");
return status;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
/*setting up cb in smmu*/
smmu_cb_assign_vspace(cb, cap_vtable_root_get_basePtr(vspaceCap),
cap_vtable_root_get_mappedASID(vspaceCap));
/*building connection between the vspace cap and cb*/
cteInsert(vspaceCap, vspaceCapSlot, cbSlot);
cap_vtable_root_ptr_set_mappedCB(&(cbSlot->cap), cb);
return EXCEPTION_NONE;
}
#endif

View File

@@ -505,6 +505,14 @@ void smmu_cb_assign_vspace(word_t cb, vspace_root_t *vspace, asid_t asid) {
smmu_write_reg32(SMMU_CBn_BASE_PPTR(cb), SMMU_CBn_SCTLR, reg);
}
void smmu_cb_disable(word_t cb, asid_t asid) {
uint32_t reg = smmu_read_reg32(SMMU_CBn_BASE_PPTR(cb), SMMU_CBn_SCTLR);
reg &= ~CBn_SCTLR_M;
smmu_write_reg32(SMMU_CBn_BASE_PPTR(cb), SMMU_CBn_SCTLR, reg);
smmu_tlb_invalidate_cb(cb, asid);
}
void smmu_sid_bind_cb(word_t sid, word_t cb) {
uint32_t reg = 0;