1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2023 ARM Ltd. 4 */ 5 6 #include <linux/arm-smccc.h> 7 #include <linux/cc_platform.h> 8 #include <linux/kernel.h> 9 #include <linux/module.h> 10 #include <linux/smp.h> 11 #include <linux/tsm.h> 12 #include <linux/types.h> 13 14 #include <asm/rsi.h> 15 16 /** 17 * struct arm_cca_token_info - a descriptor for the token buffer. 18 * @challenge: Pointer to the challenge data 19 * @challenge_size: Size of the challenge data 20 * @granule: PA of the granule to which the token will be written 21 * @offset: Offset within granule to start of buffer in bytes 22 * @result: result of rsi_attestation_token_continue operation 23 */ 24 struct arm_cca_token_info { 25 void *challenge; 26 unsigned long challenge_size; 27 phys_addr_t granule; 28 unsigned long offset; 29 unsigned long result; 30 }; 31 32 static void arm_cca_attestation_init(void *param) 33 { 34 struct arm_cca_token_info *info; 35 36 info = (struct arm_cca_token_info *)param; 37 38 info->result = rsi_attestation_token_init(info->challenge, 39 info->challenge_size); 40 } 41 42 /** 43 * arm_cca_attestation_continue - Retrieve the attestation token data. 44 * 45 * @param: pointer to the arm_cca_token_info 46 * 47 * Attestation token generation is a long running operation and therefore 48 * the token data may not be retrieved in a single call. Moreover, the 49 * token retrieval operation must be requested on the same CPU on which the 50 * attestation token generation was initialised. 51 * This helper function is therefore scheduled on the same CPU multiple 52 * times until the entire token data is retrieved. 53 */ 54 static void arm_cca_attestation_continue(void *param) 55 { 56 unsigned long len; 57 unsigned long size; 58 struct arm_cca_token_info *info; 59 60 info = (struct arm_cca_token_info *)param; 61 62 size = RSI_GRANULE_SIZE - info->offset; 63 info->result = rsi_attestation_token_continue(info->granule, 64 info->offset, size, &len); 65 info->offset += len; 66 } 67 68 /** 69 * arm_cca_report_new - Generate a new attestation token. 70 * 71 * @report: pointer to the TSM report context information. 72 * @data: pointer to the context specific data for this module. 73 * 74 * Initialise the attestation token generation using the challenge data 75 * passed in the TSM descriptor. Allocate memory for the attestation token 76 * and schedule calls to retrieve the attestation token on the same CPU 77 * on which the attestation token generation was initialised. 78 * 79 * The challenge data must be at least 32 bytes and no more than 64 bytes. If 80 * less than 64 bytes are provided it will be zero padded to 64 bytes. 81 * 82 * Return: 83 * * %0 - Attestation token generated successfully. 84 * * %-EINVAL - A parameter was not valid. 85 * * %-ENOMEM - Out of memory. 86 * * %-EFAULT - Failed to get IPA for memory page(s). 87 * * A negative status code as returned by smp_call_function_single(). 88 */ 89 static int arm_cca_report_new(struct tsm_report *report, void *data) 90 { 91 int ret; 92 int cpu; 93 long max_size; 94 unsigned long token_size = 0; 95 struct arm_cca_token_info info; 96 void *buf; 97 u8 *token __free(kvfree) = NULL; 98 struct tsm_desc *desc = &report->desc; 99 100 if (desc->inblob_len < 32 || desc->inblob_len > 64) 101 return -EINVAL; 102 103 /* 104 * The attestation token 'init' and 'continue' calls must be 105 * performed on the same CPU. smp_call_function_single() is used 106 * instead of simply calling get_cpu() because of the need to 107 * allocate outblob based on the returned value from the 'init' 108 * call and that cannot be done in an atomic context. 109 */ 110 cpu = smp_processor_id(); 111 112 info.challenge = desc->inblob; 113 info.challenge_size = desc->inblob_len; 114 115 ret = smp_call_function_single(cpu, arm_cca_attestation_init, 116 &info, true); 117 if (ret) 118 return ret; 119 max_size = info.result; 120 121 if (max_size <= 0) 122 return -EINVAL; 123 124 /* Allocate outblob */ 125 token = kvzalloc(max_size, GFP_KERNEL); 126 if (!token) 127 return -ENOMEM; 128 129 /* 130 * Since the outblob may not be physically contiguous, use a page 131 * to bounce the buffer from RMM. 132 */ 133 buf = alloc_pages_exact(RSI_GRANULE_SIZE, GFP_KERNEL); 134 if (!buf) 135 return -ENOMEM; 136 137 /* Get the PA of the memory page(s) that were allocated */ 138 info.granule = (unsigned long)virt_to_phys(buf); 139 140 /* Loop until the token is ready or there is an error */ 141 do { 142 /* Retrieve one RSI_GRANULE_SIZE data per loop iteration */ 143 info.offset = 0; 144 do { 145 /* 146 * Schedule a call to retrieve a sub-granule chunk 147 * of data per loop iteration. 148 */ 149 ret = smp_call_function_single(cpu, 150 arm_cca_attestation_continue, 151 (void *)&info, true); 152 if (ret != 0) { 153 token_size = 0; 154 goto exit_free_granule_page; 155 } 156 } while (info.result == RSI_INCOMPLETE && 157 info.offset < RSI_GRANULE_SIZE); 158 159 if (info.result != RSI_SUCCESS) { 160 ret = -ENXIO; 161 token_size = 0; 162 goto exit_free_granule_page; 163 } 164 165 /* 166 * Copy the retrieved token data from the granule 167 * to the token buffer, ensuring that the RMM doesn't 168 * overflow the buffer. 169 */ 170 if (WARN_ON(token_size + info.offset > max_size)) 171 break; 172 memcpy(&token[token_size], buf, info.offset); 173 token_size += info.offset; 174 } while (info.result == RSI_INCOMPLETE); 175 176 report->outblob = no_free_ptr(token); 177 exit_free_granule_page: 178 report->outblob_len = token_size; 179 free_pages_exact(buf, RSI_GRANULE_SIZE); 180 return ret; 181 } 182 183 static const struct tsm_ops arm_cca_tsm_ops = { 184 .name = KBUILD_MODNAME, 185 .report_new = arm_cca_report_new, 186 }; 187 188 /** 189 * arm_cca_guest_init - Register with the Trusted Security Module (TSM) 190 * interface. 191 * 192 * Return: 193 * * %0 - Registered successfully with the TSM interface. 194 * * %-ENODEV - The execution context is not an Arm Realm. 195 * * %-EBUSY - Already registered. 196 */ 197 static int __init arm_cca_guest_init(void) 198 { 199 int ret; 200 201 if (!is_realm_world()) 202 return -ENODEV; 203 204 ret = tsm_register(&arm_cca_tsm_ops, NULL); 205 if (ret < 0) 206 pr_err("Error %d registering with TSM\n", ret); 207 208 return ret; 209 } 210 module_init(arm_cca_guest_init); 211 212 /** 213 * arm_cca_guest_exit - unregister with the Trusted Security Module (TSM) 214 * interface. 215 */ 216 static void __exit arm_cca_guest_exit(void) 217 { 218 tsm_unregister(&arm_cca_tsm_ops); 219 } 220 module_exit(arm_cca_guest_exit); 221 222 MODULE_AUTHOR("Sami Mujawar <sami.mujawar@arm.com>"); 223 MODULE_DESCRIPTION("Arm CCA Guest TSM Driver"); 224 MODULE_LICENSE("GPL"); 225