Home | History | Annotate | Line # | Download | only in arm
      1 /* $NetBSD: smccc.c,v 1.4 2023/11/07 13:31:26 rin Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2021 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: smccc.c,v 1.4 2023/11/07 13:31:26 rin Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/kernel.h>
     34 
     35 #include <arm/arm/psci.h>
     36 #include <arm/arm/smccc.h>
     37 
     38 #if defined(__arm__)
     39 #  if defined(__clang__)
     40 #define	SMCCC_ARCH_ATTRIBUTE  __attribute__ ((target("armv7ve")))
     41 #  else /* gcc */
     42 #define	SMCCC_ARCH_ATTRIBUTE  __attribute__ ((target("arch=armv7ve")))
     43 #  endif
     44 #else
     45 #define	SMCCC_ARCH_ATTRIBUTE
     46 #endif
     47 
     48 /* Minimum supported PSCI version for SMCCC discovery */
     49 #define	PSCI_VERSION_1_0	0x10000
     50 
     51 /* Retrieve the implemented version of the SMC Calling Convention. */
     52 #define	SMCCC_VERSION		0x80000000
     53 
     54 /* True if SMCCC is detected. */
     55 static bool			smccc_present;
     56 
     57 /* SMCCC conduit (SMC or HVC) */
     58 static enum psci_conduit	smccc_conduit = PSCI_CONDUIT_NONE;
     59 
     60 /*
     61  * smccc_probe --
     62  *
     63  *	Returns true if SMCCC is supported by platform firmware.
     64  */
     65 bool
     66 smccc_probe(void)
     67 {
     68 	if (cold && !smccc_present) {
     69 		if (!psci_available() || psci_version() < PSCI_VERSION_1_0) {
     70 			return false;
     71 		}
     72 
     73 		smccc_present = psci_features(SMCCC_VERSION) == PSCI_SUCCESS;
     74 		if (smccc_present) {
     75 			smccc_conduit = psci_conduit();
     76 
     77 			aprint_debug("SMCCC: Version %#x (%s)\n",
     78 			    smccc_version(),
     79 			    smccc_conduit == PSCI_CONDUIT_SMC ? "SMC" : "HVC");
     80 		}
     81 	}
     82 	return smccc_present;
     83 }
     84 
     85 /*
     86  * smccc_version --
     87  *
     88  *	Return the implemented SMCCC version, or a negative error code on failure.
     89  */
     90 int
     91 smccc_version(void)
     92 {
     93 	return smccc_call(SMCCC_VERSION, 0, 0, 0, 0,
     94 			  NULL, NULL, NULL, NULL);
     95 }
     96 
     97 /*
     98  * smccc_call --
     99  *
    100  *	Generic call interface for SMC/HVC calls.
    101  */
    102 SMCCC_ARCH_ATTRIBUTE int
    103 smccc_call(uint32_t fid,
    104     register_t arg1, register_t arg2, register_t arg3, register_t arg4,
    105     register_t *res0, register_t *res1, register_t *res2, register_t *res3)
    106 {
    107 	register_t args[5] = { fid, arg1, arg2, arg3, arg4 };
    108 
    109 	register register_t r0 asm ("r0");
    110 	register register_t r1 asm ("r1");
    111 	register register_t r2 asm ("r2");
    112 	register register_t r3 asm ("r3");
    113 	register register_t r4 asm ("r4");
    114 
    115 	if (!smccc_present) {
    116 		return SMCCC_NOT_SUPPORTED;
    117 	}
    118 
    119 	KASSERT(smccc_conduit != PSCI_CONDUIT_NONE);
    120 
    121 	r0 = args[0];
    122 	r1 = args[1];
    123 	r2 = args[2];
    124 	r3 = args[3];
    125 	r4 = args[4];
    126 
    127 	if (smccc_conduit == PSCI_CONDUIT_SMC) {
    128 		asm volatile ("smc #0" :
    129 			      "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) :
    130 			      "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4) :
    131 			      "memory");
    132 	} else {
    133 		asm volatile ("hvc #0" :
    134 			      "=r" (r0), "=r" (r1), "=r" (r2), "=r" (r3) :
    135 			      "r" (r0), "r" (r1), "r" (r2), "r" (r3), "r" (r4) :
    136 			      "memory");
    137 	}
    138 
    139 	if (res0) {
    140 		*res0 = r0;
    141 	}
    142 	if (res1) {
    143 		*res1 = r1;
    144 	}
    145 	if (res2) {
    146 		*res2 = r2;
    147 	}
    148 	if (res3) {
    149 		*res3 = r3;
    150 	}
    151 
    152 	return r0;
    153 }
    154