xref: /linux/drivers/virt/coco/arm-cca-guest/arm-cca-guest.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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