File Index
Files and directories
Sign in
FILE

arm-cca-guest.c

File preview with syntax highlighting.

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