/* ARMv8-M Security Extensions routines. Copyright (C) 2015-2019 Free Software Foundation, Inc. Contributed by ARM Ltd. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. Under Section 7 of GPL version 3, you are granted additional permissions described in the GCC Runtime Library Exception, version 3.1, as published by the Free Software Foundation. You should have received a copy of the GNU General Public License and a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ #if __ARM_FEATURE_CMSE & 1 #include /* ARM intrinsic function to perform a permission check on a given address range. See ACLE changes for ARMv8-M. */ 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_TYPE__) 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_TYPE__) pb ^ (__UINTPTR_TYPE__) 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; } } #endif /* __ARM_FEATURE_CMSE & 1. */