Ejemplo n.º 1
0
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;
    }
}
Ejemplo n.º 2
0
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;
}