//===---- arm_cmse.h - Arm CMSE support -----------------------------------===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef __ARM_CMSE_H
 
#define __ARM_CMSE_H
 
 
 
#if (__ARM_FEATURE_CMSE & 0x1)
 
#include <stddef.h>
 
#include <stdint.h>
 
 
 
#define __ARM_CMSE_SECURE_MODE (__ARM_FEATURE_CMSE & 0x2)
 
#define CMSE_MPU_READWRITE 1 /* checks if readwrite_ok field is set */
 
#define CMSE_AU_NONSECURE  2 /* checks if permissions have secure field unset */
 
#define CMSE_MPU_UNPRIV    4 /* sets T flag on TT insrtuction */
 
#define CMSE_MPU_READ      8 /* checks if read_ok field is set */
 
#define CMSE_MPU_NONSECURE 16 /* sets A flag, checks if secure field unset */
 
#define CMSE_NONSECURE (CMSE_AU_NONSECURE | CMSE_MPU_NONSECURE)
 
 
 
#define cmse_check_pointed_object(p, f) \
 
  cmse_check_address_range((p), sizeof(*(p)), (f))
 
 
 
#if defined(__cplusplus)
 
extern "C" {
 
#endif
 
 
 
typedef union {
 
  struct cmse_address_info {
 
#ifdef __ARM_BIG_ENDIAN
 
    /* __ARM_BIG_ENDIAN */
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned idau_region : 8;
 
    unsigned idau_region_valid : 1;
 
    unsigned secure : 1;
 
    unsigned nonsecure_readwrite_ok : 1;
 
    unsigned nonsecure_read_ok : 1;
 
#else
 
    unsigned : 12;
 
#endif
 
    unsigned readwrite_ok : 1;
 
    unsigned read_ok : 1;
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned sau_region_valid : 1;
 
#else
 
    unsigned : 1;
 
#endif
 
    unsigned mpu_region_valid : 1;
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned sau_region : 8;
 
#else
 
    unsigned : 8;
 
#endif
 
    unsigned mpu_region : 8;
 
 
 
#else /* __ARM_LITTLE_ENDIAN */
 
    unsigned mpu_region : 8;
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned sau_region : 8;
 
#else
 
    unsigned : 8;
 
#endif
 
    unsigned mpu_region_valid : 1;
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned sau_region_valid : 1;
 
#else
 
    unsigned : 1;
 
#endif
 
    unsigned read_ok : 1;
 
    unsigned readwrite_ok : 1;
 
#if (__ARM_CMSE_SECURE_MODE)
 
    unsigned nonsecure_read_ok : 1;
 
    unsigned nonsecure_readwrite_ok : 1;
 
    unsigned secure : 1;
 
    unsigned idau_region_valid : 1;
 
    unsigned idau_region : 8;
 
#else
 
    unsigned : 12;
 
#endif
 
#endif /*__ARM_LITTLE_ENDIAN */
 
  } flags;
 
  unsigned value;
 
} cmse_address_info_t;
 
 
 
static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 
cmse_TT(void *__p) {
 
  cmse_address_info_t __u;
 
  __u.value = __builtin_arm_cmse_TT(__p);
 
  return __u;
 
}
 
static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 
cmse_TTT(void *__p) {
 
  cmse_address_info_t __u;
 
  __u.value = __builtin_arm_cmse_TTT(__p);
 
  return __u;
 
}
 
 
 
#if __ARM_CMSE_SECURE_MODE
 
static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 
cmse_TTA(void *__p) {
 
  cmse_address_info_t __u;
 
  __u.value = __builtin_arm_cmse_TTA(__p);
 
  return __u;
 
}
 
static cmse_address_info_t __attribute__((__always_inline__, __nodebug__))
 
cmse_TTAT(void *__p) {
 
  cmse_address_info_t __u;
 
  __u.value = __builtin_arm_cmse_TTAT(__p);
 
  return __u;
 
}
 
#endif
 
 
 
#define cmse_TT_fptr(p) cmse_TT(__builtin_bit_cast(void *, (p)))
 
#define cmse_TTT_fptr(p) cmse_TTT(__builtin_bit_cast(void *, (p)))
 
 
 
#if __ARM_CMSE_SECURE_MODE
 
#define cmse_TTA_fptr(p) cmse_TTA(__builtin_bit_cast(void *, (p)))
 
#define cmse_TTAT_fptr(p) cmse_TTAT(__builtin_bit_cast(void *, (p)))
 
#endif
 
 
 
static void *__attribute__((__always_inline__))
 
cmse_check_address_range(void *__pb, size_t __s, int __flags) {
 
  uintptr_t __begin = (uintptr_t)__pb;
 
  uintptr_t __end = __begin + __s - 1;
 
 
 
  if (__end < __begin)
 
    return NULL; /* wrap around check */
 
 
 
  /* Check whether the range crosses a 32-bytes aligned address */
 
  const int __single_check = (__begin ^ __end) < 0x20u;
 
 
 
  /* execute the right variant of the TT instructions */
 
  void *__pe = (void *)__end;
 
  cmse_address_info_t __permb, __perme;
 
  switch (__flags & (CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
 
  case 0:
 
    __permb = cmse_TT(__pb);
 
    __perme = __single_check ? __permb : cmse_TT(__pe);
 
    break;
 
  case CMSE_MPU_UNPRIV:
 
    __permb = cmse_TTT(__pb);
 
    __perme = __single_check ? __permb : cmse_TTT(__pe);
 
    break;
 
#if __ARM_CMSE_SECURE_MODE
 
  case CMSE_MPU_NONSECURE:
 
    __permb = cmse_TTA(__pb);
 
    __perme = __single_check ? __permb : cmse_TTA(__pe);
 
    break;
 
  case CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE:
 
    __permb = cmse_TTAT(__pb);
 
    __perme = __single_check ? __permb : cmse_TTAT(__pe);
 
    break;
 
#endif
 
  /* if CMSE_NONSECURE is specified w/o __ARM_CMSE_SECURE_MODE */
 
  default:
 
    return NULL;
 
  }
 
 
 
  /* check that the range does not cross MPU, SAU, or IDAU region boundaries */
 
  if (__permb.value != __perme.value)
 
    return NULL;
 
#if !(__ARM_CMSE_SECURE_MODE)
 
  /* CMSE_AU_NONSECURE is only supported when __ARM_FEATURE_CMSE & 0x2 */
 
  if (__flags & CMSE_AU_NONSECURE)
 
    return NULL;
 
#endif
 
 
 
  /* check the permission on the range */
 
  switch (__flags & ~(CMSE_MPU_UNPRIV | CMSE_MPU_NONSECURE)) {
 
#if (__ARM_CMSE_SECURE_MODE)
 
  case CMSE_MPU_READ | CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
 
  case CMSE_MPU_READWRITE | CMSE_AU_NONSECURE:
 
    return __permb.flags.nonsecure_readwrite_ok ? __pb : NULL;
 
 
 
  case CMSE_MPU_READ | CMSE_AU_NONSECURE:
 
    return __permb.flags.nonsecure_read_ok ? __pb : NULL;
 
 
 
  case CMSE_AU_NONSECURE:
 
    return __permb.flags.secure ? NULL : __pb;
 
#endif
 
  case CMSE_MPU_READ | CMSE_MPU_READWRITE:
 
  case CMSE_MPU_READWRITE:
 
    return __permb.flags.readwrite_ok ? __pb : NULL;
 
 
 
  case CMSE_MPU_READ:
 
    return __permb.flags.read_ok ? __pb : NULL;
 
 
 
  default:
 
    return NULL;
 
  }
 
}
 
 
 
#if __ARM_CMSE_SECURE_MODE
 
static int __attribute__((__always_inline__, __nodebug__))
 
cmse_nonsecure_caller(void) {
 
  return !((uintptr_t)__builtin_return_address(0) & 1);
 
}
 
 
 
#define cmse_nsfptr_create(p)                                                  \
 
  __builtin_bit_cast(__typeof__(p),                                            \
 
                     (__builtin_bit_cast(uintptr_t, p) & ~(uintptr_t)1))
 
 
 
#define cmse_is_nsfptr(p) ((__builtin_bit_cast(uintptr_t, p) & 1) == 0)
 
 
 
#endif /* __ARM_CMSE_SECURE_MODE */
 
 
 
void __attribute__((__noreturn__)) cmse_abort(void);
 
#if defined(__cplusplus)
 
}
 
#endif
 
 
 
#endif /* (__ARM_FEATURE_CMSE & 0x1) */
 
 
 
#endif /* __ARM_CMSE_H */