void * cmse_check_address_range (void *p, size_t size, int flags) { cmse_address_info_t permb, perme; char *pb = (char *) p, *pe; /* Check if the range wraps around. */ if (UINTPTR_MAX - (uintptr_t) p < size) return NULL; /* Check if an unknown flag is present. */ int known = CMSE_MPU_UNPRIV | CMSE_MPU_READWRITE | CMSE_MPU_READ; int known_secure_level = CMSE_MPU_UNPRIV; #if __ARM_FEATURE_CMSE & 2 known |= CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE; known_secure_level |= CMSE_MPU_NONSECURE; #endif if (flags & (~known)) return NULL; /* Execute the right variant of the TT instructions. */ pe = pb + size - 1; const int singleCheck = (((uintptr_t) pb ^ (uintptr_t) pe) < 32); switch (flags & known_secure_level) { case 0: permb = cmse_TT (pb); perme = singleCheck ? permb : cmse_TT (pe); break; case CMSE_MPU_UNPRIV: permb = cmse_TTT (pb); perme = singleCheck ? permb : cmse_TTT (pe); break; #if __ARM_FEATURE_CMSE & 2 case CMSE_MPU_NONSECURE: permb = cmse_TTA (pb); perme = singleCheck ? permb : cmse_TTA (pe); break; case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE: permb = cmse_TTAT (pb); perme = singleCheck ? permb : cmse_TTAT (pe); break; #endif default: /* Invalid flag, eg. CMSE_MPU_NONSECURE specified but __ARM_FEATURE_CMSE & 2 == 0. */ return NULL; } /* Check that the range does not cross MPU, SAU, or IDAU boundaries. */ if (permb.value != perme.value) return NULL; /* Check the permissions on the range. */ switch (flags & (~known_secure_level)) { #if __ARM_FEATURE_CMSE & 2 case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE: case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE: return permb.flags.nonsecure_readwrite_ok ? p : NULL; case CMSE_MPU_READ | CMSE_AU_NONSECURE: return permb.flags.nonsecure_read_ok ? p : NULL; case CMSE_AU_NONSECURE: return permb.flags.secure ? NULL : p; #endif case CMSE_MPU_READ | CMSE_MPU_READWRITE: case CMSE_MPU_READWRITE: return permb.flags.readwrite_ok ? p : NULL; case CMSE_MPU_READ: return permb.flags.read_ok ? p : NULL; default: return NULL; } }
void tfm_core_memory_permission_check_handler(uint32_t *svc_args) { uint32_t ptr = svc_args[0]; uint32_t size = svc_args[1]; int32_t access = svc_args[2]; uint32_t max_buf_size, ptr_start, range_limit, range_check = false; int32_t res; uint32_t running_partition_idx = tfm_spm_partition_get_running_partition_idx(); const struct spm_partition_runtime_data_t *curr_part_data = tfm_spm_partition_get_runtime_data(running_partition_idx); uint32_t running_partition_flags = tfm_spm_partition_get_flags(running_partition_idx); int32_t flags = 0; void *rangeptr; if (!(running_partition_flags & SPM_PART_FLAG_APP_ROT) || (size == 0)) { /* This handler should only be called from a secure partition. */ svc_args[0] = TFM_ERROR_INVALID_PARAMETER; return; } if (curr_part_data->share != TFM_BUFFER_SHARE_PRIV) { flags |= CMSE_MPU_UNPRIV; } if (access == TFM_MEMORY_ACCESS_RW) { flags |= CMSE_MPU_READWRITE; } else { flags |= CMSE_MPU_READ; } /* Check if partition access to address would fail */ rangeptr = cmse_check_address_range((void *)ptr, size, flags); /* Get regions associated with address */ cmse_address_info_t addr_info = cmse_TT((void *)ptr); if (rangeptr == NULL) { svc_args[0] = TFM_ERROR_INVALID_PARAMETER; return; } if (addr_info.flags.secure) { #if TFM_LVL == 1 /* For privileged partition execution, all secure data memory is * accessible */ max_buf_size = S_DATA_SIZE; ptr_start = S_DATA_START; range_limit = S_DATA_LIMIT; #else /* Only scratch is permitted in secure memory */ max_buf_size = (uint32_t)tfm_scratch_area_size; ptr_start = (uint32_t)tfm_scratch_area; range_limit = (uint32_t)tfm_scratch_area + tfm_scratch_area_size - 1; #endif range_check = true; } else { if (!addr_info.flags.sau_region_valid) { /* If address is NS, TF-M expects SAU to be configured */ svc_args[0] = TFM_ERROR_INVALID_PARAMETER; return; } switch (addr_info.flags.sau_region) { case TFM_NS_REGION_CODE: if (access == TFM_MEMORY_ACCESS_RW) { res = TFM_ERROR_INVALID_PARAMETER; } else { /* Currently TF-M does not support checks for NS Memory * accesses by partitions */ res = TFM_SUCCESS; } break; case TFM_NS_REGION_DATA: /* Currently TF-M does not support checks for NS Memory * accesses by partitions */ res = TFM_SUCCESS; break; default: /* Only NS data and code regions can be accessed as buffers */ res = TFM_ERROR_INVALID_PARAMETER; break; } } if (range_check == true) { if ((size <= max_buf_size) && (ptr >= ptr_start) && (ptr <= range_limit + 1 - size)) { res = TFM_SUCCESS; } else { res = TFM_ERROR_INVALID_PARAMETER; } } /* Store return value in r0 */ svc_args[0] = res; }