• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include <linux/of_platform.h>
7 #include <linux/gunyah_qtvm.h>
8 #include "vm_mgr.h"
9 
10 #define PAS_VM_METADATA_SZ 8192
11 
12 static DEFINE_MUTEX(gunyah_qtvm_lock);
13 static LIST_HEAD(gunyah_qtvm_list);
14 SRCU_NOTIFIER_HEAD_STATIC(gunyah_qtvm_notifier);
15 
16 struct gunyah_qtvm {
17 	struct gunyah_vm *ghvm;
18 	struct gunyah_vm_parcel *parcel_list;
19 	struct list_head list;
20 	u64 vm_image_addr;
21 	u64 vm_image_size;
22 	u32 pas_id;
23 	u16 vmid;
24 };
25 
gunyah_qtvm_register_notifier(struct notifier_block * nb)26 int gunyah_qtvm_register_notifier(struct notifier_block *nb)
27 {
28 	return srcu_notifier_chain_register(&gunyah_qtvm_notifier, nb);
29 }
30 EXPORT_SYMBOL_GPL(gunyah_qtvm_register_notifier);
31 
gunyah_qtvm_unregister_notifier(struct notifier_block * nb)32 int gunyah_qtvm_unregister_notifier(struct notifier_block *nb)
33 {
34 	return srcu_notifier_chain_unregister(&gunyah_qtvm_notifier, nb);
35 }
36 EXPORT_SYMBOL_GPL(gunyah_qtvm_unregister_notifier);
37 
gunyah_notify_clients(struct gunyah_qtvm * vm,enum gunyah_qtvm_state state)38 static void gunyah_notify_clients(struct gunyah_qtvm *vm,
39 					enum gunyah_qtvm_state state)
40 {
41 	srcu_notifier_call_chain(&gunyah_qtvm_notifier, state, &vm->vmid);
42 }
43 
gunyah_qtvm_pre_alloc_vmid(struct gunyah_vm * ghvm)44 static u16 gunyah_qtvm_pre_alloc_vmid(struct gunyah_vm *ghvm)
45 {
46 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
47 
48 	return vm->vmid;
49 }
50 
gunyah_qtvm_pre_vm_configure(struct gunyah_vm * ghvm)51 static int gunyah_qtvm_pre_vm_configure(struct gunyah_vm *ghvm)
52 {
53 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
54 	u64 start_gfn;
55 	int ret;
56 
57 	/*
58 	 * For QTVMs, the metadata is always placed at the beginning of the
59 	 * main VM memory and will always be of fixed size decided at the
60 	 * build time while signing the VM image. The metadata contains the
61 	 * signing information needed by firmware to authenticate the VM image.
62 	 * VM image once loaded into the memory looks like this:
63 	 *
64 	 *           start |----------------------|
65 	 *                 | MDT header + hashes  |
66 	 *                 |----------------------|
67 	 *                 |       Kernel         |
68 	 *                 |----------------------|
69 	 *                 |         DTB          |
70 	 *                 |----------------------|
71 	 *                 |      CPIO/Ramdisk    |
72 	 *                 |----------------------|
73 	 */
74 
75 	ghvm->config_image.parcel.start = gunyah_gpa_to_gfn(vm->vm_image_addr);
76 	ghvm->config_image.parcel.pages = gunyah_gpa_to_gfn(vm->vm_image_size);
77 
78 	ghvm->config_image.image_offset = 0;
79 	ghvm->config_image.image_size = PAS_VM_METADATA_SZ;
80 
81 	if (ghvm->dtb.config.size > 0) {
82 		ghvm->config_image.dtb_offset = ghvm->dtb.config.guest_phys_addr -
83 					gunyah_gfn_to_gpa(ghvm->config_image.parcel.start);
84 		ghvm->config_image.dtb_size = ghvm->dtb.config.size;
85 
86 		if ((ghvm->dtb.config.guest_phys_addr + ghvm->config_image.dtb_size) >
87 		    (gunyah_gfn_to_gpa(ghvm->config_image.parcel.start) +
88 			gunyah_gfn_to_gpa(ghvm->config_image.parcel.pages))) {
89 			/*
90 			 * DTB is out of the config image bounds.
91 			 * This is should not happen!
92 			 */
93 			dev_err(ghvm->parent, "DTB is outside the image parcel\n");
94 			return -EINVAL;
95 		}
96 	}
97 
98 	/*
99 	 * RM would expect to have all the memory mentioned
100 	 * in the VM DT to be shared/lent before the VM starts.
101 	 * We will lend the primary memory parcel as
102 	 * part of the vm_configure operation. So, share the rest
103 	 * of the VM memory here.
104 	 */
105 	start_gfn = gunyah_gpa_to_gfn(vm->vm_image_addr + vm->vm_image_size);
106 	ret = gunyah_share_range_as_parcels(ghvm, start_gfn, ULONG_MAX, &vm->parcel_list);
107 	if (ret) {
108 		dev_err(ghvm->parent, "Failed to share non primary parcel(s) before VM start\n");
109 		return ret;
110 	}
111 
112 	return 0;
113 }
114 
gunyah_qtvm_authenticate(struct gunyah_vm * ghvm)115 static int gunyah_qtvm_authenticate(struct gunyah_vm *ghvm)
116 {
117 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
118 	struct gunyah_rm_vm_authenticate_param_entry entry;
119 	int ret;
120 
121 	entry.param_type = GUNYAH_VM_AUTH_PARAM_PAS_ID;
122 	entry.param = vm->pas_id;
123 
124 	ret = gunyah_rm_vm_authenticate(ghvm->rm, vm->vmid, 1, &entry);
125 	if (ret) {
126 		dev_err(ghvm->parent, "Failed to Authenticate VM: %d\n", ret);
127 		return ret;
128 	}
129 
130 	return 0;
131 }
132 
gunyah_qtvm_pre_vm_start(struct gunyah_vm * ghvm)133 static int gunyah_qtvm_pre_vm_start(struct gunyah_vm *ghvm)
134 {
135 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
136 
137 	gunyah_notify_clients(vm, GUNYAH_QTVM_BEFORE_POWERUP);
138 	return 0;
139 }
140 
gunyah_qtvm_vm_start_fail(struct gunyah_vm * ghvm)141 static void gunyah_qtvm_vm_start_fail(struct gunyah_vm *ghvm)
142 {
143 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
144 
145 	gunyah_notify_clients(vm, GUNYAH_QTVM_POWERUP_FAIL);
146 }
147 
gunyah_qtvm_pre_vm_reset(struct gunyah_vm * ghvm)148 static int gunyah_qtvm_pre_vm_reset(struct gunyah_vm *ghvm)
149 {
150 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
151 
152 	gunyah_notify_clients(vm, GUNYAH_QTVM_EXITED);
153 	return 0;
154 }
155 
gunyah_qtvm_post_vm_reset(struct gunyah_vm * ghvm)156 static int gunyah_qtvm_post_vm_reset(struct gunyah_vm *ghvm)
157 {
158 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
159 
160 	gunyah_notify_clients(vm, GUNYAH_QTVM_EARLY_POWEROFF);
161 	return 0;
162 }
163 
164 static struct gunyah_auth_vm_mgr_ops vm_ops = {
165 	.pre_alloc_vmid = gunyah_qtvm_pre_alloc_vmid,
166 	.pre_vm_configure = gunyah_qtvm_pre_vm_configure,
167 	.vm_authenticate = gunyah_qtvm_authenticate,
168 	.pre_vm_start = gunyah_qtvm_pre_vm_start,
169 	.vm_start_fail = gunyah_qtvm_vm_start_fail,
170 	.pre_vm_reset = gunyah_qtvm_pre_vm_reset,
171 	.post_vm_reset = gunyah_qtvm_post_vm_reset,
172 };
173 
gunyah_qtvm_attach(struct gunyah_vm * ghvm,struct gunyah_auth_desc * desc)174 static long gunyah_qtvm_attach(struct gunyah_vm *ghvm, struct gunyah_auth_desc *desc)
175 {
176 	struct gunyah_qtvm_auth_arg arg;
177 	struct gunyah_qtvm *vm;
178 	void __user *argp;
179 
180 	if (desc->arg_size > sizeof(struct gunyah_qtvm_auth_arg))
181 		return -EINVAL;
182 
183 	argp = u64_to_user_ptr(desc->arg);
184 	if (copy_from_user(&arg, argp, desc->arg_size))
185 		return -EFAULT;
186 
187 	if (overflows_type(arg.guest_phys_addr + arg.size,
188 				   u64))
189 		return -EOVERFLOW;
190 
191 	mutex_lock(&gunyah_qtvm_lock);
192 	vm = kzalloc(sizeof(*vm), GFP_KERNEL_ACCOUNT);
193 	if (!vm) {
194 		mutex_unlock(&gunyah_qtvm_lock);
195 		return -ENOMEM;
196 	}
197 
198 	vm->vmid = arg.vm_id;
199 	vm->pas_id = arg.peripheral_id;
200 
201 	/* This would be the primary Image parcel */
202 	vm->vm_image_addr = arg.guest_phys_addr;
203 	vm->vm_image_size = arg.size;
204 	vm->ghvm = ghvm;
205 
206 	ghvm->auth = GUNYAH_RM_VM_AUTH_QCOM_TRUSTED_VM;
207 	ghvm->auth_vm_mgr_ops = &vm_ops;
208 	ghvm->auth_vm_mgr_data = vm;
209 
210 	list_add(&vm->list, &gunyah_qtvm_list);
211 	mutex_unlock(&gunyah_qtvm_lock);
212 	return 0;
213 }
214 
gunyah_qtvm_detach(struct gunyah_vm * ghvm)215 static void gunyah_qtvm_detach(struct gunyah_vm *ghvm)
216 {
217 	struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data;
218 
219 	kfree(vm->parcel_list);
220 	gunyah_notify_clients(vm, GUNYAH_QTVM_POWEROFF);
221 	list_del(&vm->list);
222 	kfree(vm);
223 	ghvm->auth_vm_mgr_ops = NULL;
224 	ghvm->auth_vm_mgr_data = NULL;
225 }
226 
227 static struct gunyah_auth_vm_mgr auth_vm = {
228 	.type = GUNYAH_QCOM_TRUSTED_VM_TYPE,
229 	.name = "gunyah_qtvm",
230 	.mod = THIS_MODULE,
231 	.vm_attach = gunyah_qtvm_attach,
232 	.vm_detach = gunyah_qtvm_detach,
233 };
234 
gunyah_qtvm_init(void)235 static int __init gunyah_qtvm_init(void)
236 {
237 	mutex_init(&gunyah_qtvm_lock);
238 	return gunyah_auth_vm_mgr_register(&auth_vm);
239 }
240 
gunyah_qtvm_exit(void)241 static void __exit gunyah_qtvm_exit(void)
242 {
243 	gunyah_auth_vm_mgr_unregister(&auth_vm);
244 }
245 
246 module_init(gunyah_qtvm_init);
247 module_exit(gunyah_qtvm_exit);
248 
249 MODULE_LICENSE("GPL");
250 MODULE_DESCRIPTION("Gunyah Qualcomm Trusted VM Driver");
251