1 /*
2  * Copyright 2019 The Hafnium Authors.
3  *
4  * Use of this source code is governed by a BSD-style
5  * license that can be found in the LICENSE file or at
6  * https://opensource.org/licenses/BSD-3-Clause.
7  */
8 
9 #include "hf/vcpu.h"
10 
11 #include "hf/arch/cpu.h"
12 
13 #include "hf/check.h"
14 #include "hf/dlog.h"
15 #include "hf/std.h"
16 #include "hf/vm.h"
17 
18 static struct vcpu *boot_vcpu;
19 
20 /** GP register to be used to pass the current vCPU ID, at core bring up. */
21 #define PHYS_CORE_IDX_GP_REG 4
22 
23 /**
24  * Locks the given vCPU and updates `locked` to hold the newly locked vCPU.
25  */
vcpu_lock(struct vcpu * vcpu)26 struct vcpu_locked vcpu_lock(struct vcpu *vcpu)
27 {
28 	struct vcpu_locked locked = {
29 		.vcpu = vcpu,
30 	};
31 
32 	sl_lock(&vcpu->lock);
33 
34 	return locked;
35 }
36 
37 /**
38  * Locks two vCPUs ensuring that the locking order is according to the locks'
39  * addresses.
40  */
vcpu_lock_both(struct vcpu * vcpu1,struct vcpu * vcpu2)41 struct two_vcpu_locked vcpu_lock_both(struct vcpu *vcpu1, struct vcpu *vcpu2)
42 {
43 	struct two_vcpu_locked dual_lock;
44 
45 	sl_lock_both(&vcpu1->lock, &vcpu2->lock);
46 	dual_lock.vcpu1.vcpu = vcpu1;
47 	dual_lock.vcpu2.vcpu = vcpu2;
48 
49 	return dual_lock;
50 }
51 
52 /**
53  * Unlocks a vCPU previously locked with vpu_lock, and updates `locked` to
54  * reflect the fact that the vCPU is no longer locked.
55  */
vcpu_unlock(struct vcpu_locked * locked)56 void vcpu_unlock(struct vcpu_locked *locked)
57 {
58 	sl_unlock(&locked->vcpu->lock);
59 	locked->vcpu = NULL;
60 }
61 
vcpu_init(struct vcpu * vcpu,struct vm * vm)62 void vcpu_init(struct vcpu *vcpu, struct vm *vm)
63 {
64 	memset_s(vcpu, sizeof(*vcpu), 0, sizeof(*vcpu));
65 	sl_init(&vcpu->lock);
66 	vcpu->regs_available = true;
67 	vcpu->vm = vm;
68 	vcpu->state = VCPU_STATE_OFF;
69 	vcpu->direct_request_origin.is_ffa_req2 = false;
70 	vcpu->direct_request_origin.vm_id = HF_INVALID_VM_ID;
71 	vcpu->rt_model = RTM_SP_INIT;
72 	vcpu->next_boot = NULL;
73 }
74 
75 /**
76  * Initialise the registers for the given vCPU and set the state to
77  * VCPU_STATE_WAITING. The caller must hold the vCPU lock while calling this.
78  */
vcpu_on(struct vcpu_locked vcpu,ipaddr_t entry,uintreg_t arg)79 void vcpu_on(struct vcpu_locked vcpu, ipaddr_t entry, uintreg_t arg)
80 {
81 	arch_regs_set_pc_arg(&vcpu.vcpu->regs, entry, arg);
82 	vcpu.vcpu->state = VCPU_STATE_WAITING;
83 }
84 
vcpu_index(const struct vcpu * vcpu)85 ffa_vcpu_index_t vcpu_index(const struct vcpu *vcpu)
86 {
87 	size_t index = vcpu - vcpu->vm->vcpus;
88 
89 	CHECK(index < UINT16_MAX);
90 	return index;
91 }
92 
93 /**
94  * Check whether the given vcpu_state is an off state, for the purpose of
95  * turning vCPUs on and off. Note that Aborted still counts as ON for the
96  * purposes of PSCI, because according to the PSCI specification (section
97  * 5.7.1) a core is only considered to be off if it has been turned off
98  * with a CPU_OFF call or hasn't yet been turned on with a CPU_ON call.
99  */
vcpu_is_off(struct vcpu_locked vcpu)100 bool vcpu_is_off(struct vcpu_locked vcpu)
101 {
102 	return (vcpu.vcpu->state == VCPU_STATE_OFF);
103 }
104 
105 /**
106  * Starts a vCPU of a secondary VM.
107  *
108  * Returns true if the secondary was reset and started, or false if it was
109  * already on and so nothing was done.
110  */
vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked,ipaddr_t entry,uintreg_t arg)111 bool vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked,
112 				    ipaddr_t entry, uintreg_t arg)
113 {
114 	struct vm *vm = vcpu_locked.vcpu->vm;
115 	bool vcpu_was_off;
116 
117 	CHECK(vm->id != HF_PRIMARY_VM_ID);
118 
119 	vcpu_was_off = vcpu_is_off(vcpu_locked);
120 	if (vcpu_was_off) {
121 		/*
122 		 * Set vCPU registers to a clean state ready for boot. As this
123 		 * is a secondary which can migrate between pCPUs, the ID of the
124 		 * vCPU is defined as the index and does not match the ID of the
125 		 * pCPU it is running on.
126 		 */
127 		arch_regs_reset(vcpu_locked.vcpu);
128 		vcpu_on(vcpu_locked, entry, arg);
129 	}
130 
131 	return vcpu_was_off;
132 }
133 
134 /**
135  * Handles a page fault. It does so by determining if it's a legitimate or
136  * spurious fault, and recovering from the latter.
137  *
138  * Returns true if the caller should resume the current vCPU, or false if its VM
139  * should be aborted.
140  */
vcpu_handle_page_fault(const struct vcpu * current,struct vcpu_fault_info * f)141 bool vcpu_handle_page_fault(const struct vcpu *current,
142 			    struct vcpu_fault_info *f)
143 {
144 	struct vm *vm = current->vm;
145 	uint32_t mode;
146 	uint32_t mask = f->mode | MM_MODE_INVALID;
147 	bool resume;
148 	struct vm_locked locked_vm;
149 
150 	locked_vm = vm_lock(vm);
151 	/*
152 	 * Check if this is a legitimate fault, i.e., if the page table doesn't
153 	 * allow the access attempted by the VM.
154 	 *
155 	 * Otherwise, this is a spurious fault, likely because another CPU is
156 	 * updating the page table. It is responsible for issuing global TLB
157 	 * invalidations while holding the VM lock, so we don't need to do
158 	 * anything else to recover from it. (Acquiring/releasing the lock
159 	 * ensured that the invalidations have completed.)
160 	 */
161 	if (!locked_vm.vm->el0_partition) {
162 		resume = vm_mem_get_mode(locked_vm, f->ipaddr,
163 					 ipa_add(f->ipaddr, 1), &mode) &&
164 			 (mode & mask) == f->mode;
165 	} else {
166 		/*
167 		 * For EL0 partitions we need to get the mode for the faulting
168 		 * vaddr.
169 		 */
170 		resume =
171 			vm_mem_get_mode(locked_vm, ipa_init(va_addr(f->vaddr)),
172 					ipa_add(ipa_init(va_addr(f->vaddr)), 1),
173 					&mode) &&
174 			(mode & mask) == f->mode;
175 
176 		/*
177 		 * For EL0 partitions, if there is an instruction abort and the
178 		 * mode of the page is RWX, we don't resume since Hafnium does
179 		 * not allow write and executable pages.
180 		 */
181 		if ((f->mode == MM_MODE_X) &&
182 		    ((mode & MM_MODE_W) == MM_MODE_W)) {
183 			resume = false;
184 		}
185 	}
186 
187 	vm_unlock(&locked_vm);
188 
189 	if (!resume) {
190 		dlog_warning(
191 			"Stage-%d page fault: pc=%#lx, vmid=%#x, vcpu=%u, "
192 			"vaddr=%#lx, ipaddr=%#lx, mode=%#x %#x\n",
193 			current->vm->el0_partition ? 1 : 2, va_addr(f->pc),
194 			vm->id, vcpu_index(current), va_addr(f->vaddr),
195 			ipa_addr(f->ipaddr), f->mode, mode);
196 	}
197 
198 	return resume;
199 }
200 
vcpu_set_phys_core_idx(struct vcpu * vcpu)201 void vcpu_set_phys_core_idx(struct vcpu *vcpu)
202 {
203 	arch_regs_set_gp_reg(&vcpu->regs, cpu_index(vcpu->cpu),
204 			     PHYS_CORE_IDX_GP_REG);
205 }
206 
207 /**
208  * Sets the designated GP register through which the vCPU expects to receive the
209  * boot info's address.
210  */
vcpu_set_boot_info_gp_reg(struct vcpu * vcpu)211 void vcpu_set_boot_info_gp_reg(struct vcpu *vcpu)
212 {
213 	struct vm *vm = vcpu->vm;
214 	uint32_t gp_register_num = vm->boot_info.gp_register_num;
215 
216 	if (vm->boot_info.blob_addr.ipa != 0U) {
217 		arch_regs_set_gp_reg(&vcpu->regs,
218 				     ipa_addr(vm->boot_info.blob_addr),
219 				     gp_register_num);
220 	}
221 }
222 
223 /**
224  * Gets the first partition to boot, according to Boot Protocol from FFA spec.
225  */
vcpu_get_boot_vcpu(void)226 struct vcpu *vcpu_get_boot_vcpu(void)
227 {
228 	return boot_vcpu;
229 }
230 
231 /**
232  * Insert in boot list, sorted by `boot_order` parameter in the vm structure
233  * and rooted in `first_boot_vm`.
234  */
vcpu_update_boot(struct vcpu * vcpu)235 void vcpu_update_boot(struct vcpu *vcpu)
236 {
237 	struct vcpu *current = NULL;
238 	struct vcpu *previous = NULL;
239 
240 	if (boot_vcpu == NULL) {
241 		boot_vcpu = vcpu;
242 		return;
243 	}
244 
245 	current = boot_vcpu;
246 
247 	while (current != NULL &&
248 	       current->vm->boot_order <= vcpu->vm->boot_order) {
249 		previous = current;
250 		current = current->next_boot;
251 	}
252 
253 	if (previous != NULL) {
254 		previous->next_boot = vcpu;
255 	} else {
256 		boot_vcpu = vcpu;
257 	}
258 
259 	vcpu->next_boot = current;
260 }
261 
262 /**
263  * Sets the vcpu in the VCPU_STATE_RUNNING.
264  * With that, its register are set as "not available". In case the vCPU
265  * was priorly in a waiting state it takes the arguments provided,
266  * and writes them to the gp register state.
267  */
vcpu_set_running(struct vcpu_locked target_locked,struct ffa_value * args)268 void vcpu_set_running(struct vcpu_locked target_locked, struct ffa_value *args)
269 {
270 	struct vcpu *target_vcpu = target_locked.vcpu;
271 
272 	if (target_locked.vcpu->state == VCPU_STATE_WAITING) {
273 		CHECK(target_vcpu->regs_available);
274 		assert(args != NULL);
275 
276 		arch_regs_set_retval(&target_vcpu->regs, *args);
277 	}
278 
279 	/* Mark the registers as unavailable now. */
280 	target_vcpu->regs_available = false;
281 
282 	/* We are about to resume target vCPU. */
283 	target_vcpu->state = VCPU_STATE_RUNNING;
284 }
285 
286 /**
287  * Saves the current interrupt priority.
288  */
vcpu_save_interrupt_priority(struct vcpu_locked vcpu_locked,uint8_t priority)289 void vcpu_save_interrupt_priority(struct vcpu_locked vcpu_locked,
290 				  uint8_t priority)
291 {
292 	vcpu_locked.vcpu->priority_mask = priority;
293 }
294 
295 /**
296  * It injects a virtual interrupt in the vcpu if is enabled and is not pending.
297  */
vcpu_interrupt_inject(struct vcpu_locked target_locked,uint32_t intid)298 void vcpu_interrupt_inject(struct vcpu_locked target_locked, uint32_t intid)
299 {
300 	struct vcpu *target_vcpu = target_locked.vcpu;
301 	struct interrupts *interrupts = &target_vcpu->interrupts;
302 
303 	/*
304 	 * We only need to change state and (maybe) trigger a virtual interrupt
305 	 * if it is enabled and was not previously pending. Otherwise we can
306 	 * skip everything except setting the pending bit.
307 	 */
308 	if (!(vcpu_is_virt_interrupt_enabled(interrupts, intid) &&
309 	      !vcpu_is_virt_interrupt_pending(interrupts, intid))) {
310 		goto out;
311 	}
312 
313 	/* Increment the count. */
314 	vcpu_interrupt_count_increment(target_locked, interrupts, intid);
315 
316 	/*
317 	 * Only need to update state if there was not already an
318 	 * interrupt enabled and pending.
319 	 */
320 	if (vcpu_interrupt_count_get(target_locked) != 1) {
321 		goto out;
322 	}
323 
324 out:
325 	/* Either way, make it pending. */
326 	vcpu_virt_interrupt_set_pending(interrupts, intid);
327 }
328 
vcpu_set_processing_interrupt(struct vcpu_locked vcpu_locked,uint32_t intid,struct vcpu_locked preempted_locked)329 void vcpu_set_processing_interrupt(struct vcpu_locked vcpu_locked,
330 				   uint32_t intid,
331 				   struct vcpu_locked preempted_locked)
332 {
333 	struct vcpu *target_vcpu = vcpu_locked.vcpu;
334 
335 	if (preempted_locked.vcpu != NULL) {
336 		target_vcpu->preempted_vcpu = preempted_locked.vcpu;
337 		preempted_locked.vcpu->state = VCPU_STATE_PREEMPTED;
338 	}
339 
340 	target_vcpu->processing_secure_interrupt = true;
341 	target_vcpu->current_sec_interrupt_id = intid;
342 }
343 
vcpu_enter_secure_interrupt_rtm(struct vcpu_locked vcpu_locked)344 void vcpu_enter_secure_interrupt_rtm(struct vcpu_locked vcpu_locked)
345 {
346 	struct vcpu *target_vcpu = vcpu_locked.vcpu;
347 
348 	assert(target_vcpu->scheduling_mode == NONE);
349 	assert(target_vcpu->call_chain.prev_node == NULL);
350 	assert(target_vcpu->call_chain.next_node == NULL);
351 	assert(target_vcpu->rt_model == RTM_NONE);
352 
353 	target_vcpu->scheduling_mode = SPMC_MODE;
354 	target_vcpu->rt_model = RTM_SEC_INTERRUPT;
355 }
356