blob: 6ff71a37189cd0019ab98a04e8933ad934110911 [file] [log] [blame]
Fuad Tabba5c738432019-12-02 11:02:42 +00001/*
2 * Copyright 2019 The Hafnium Authors.
3 *
Andrew Walbrane959ec12020-06-17 15:01:09 +01004 * 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.
Fuad Tabba5c738432019-12-02 11:02:42 +00007 */
8
9#include "hf/vcpu.h"
10
Olivier Depreze6f7b9d2021-02-01 11:55:48 +010011#include "hf/arch/cpu.h"
12
Fuad Tabba5c738432019-12-02 11:02:42 +000013#include "hf/check.h"
14#include "hf/dlog.h"
15#include "hf/std.h"
16#include "hf/vm.h"
17
J-Alves67f5ba32024-09-27 18:07:11 +010018static struct list_entry boot_list = LIST_INIT(boot_list);
Olivier Deprez181074b2023-02-02 14:53:23 +010019
J-Alves7ac49052022-02-08 17:20:53 +000020/** GP register to be used to pass the current vCPU ID, at core bring up. */
21#define PHYS_CORE_IDX_GP_REG 4
22
Fuad Tabba5c738432019-12-02 11:02:42 +000023/**
24 * Locks the given vCPU and updates `locked` to hold the newly locked vCPU.
25 */
26struct 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/**
Olivier Deprez0b6f10a2020-08-05 18:21:33 +020038 * Locks two vCPUs ensuring that the locking order is according to the locks'
39 * addresses.
40 */
41struct 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/**
Fuad Tabba5c738432019-12-02 11:02:42 +000053 * Unlocks a vCPU previously locked with vpu_lock, and updates `locked` to
54 * reflect the fact that the vCPU is no longer locked.
55 */
56void vcpu_unlock(struct vcpu_locked *locked)
57{
58 sl_unlock(&locked->vcpu->lock);
59 locked->vcpu = NULL;
60}
61
62void 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;
Kathleen Capellae468c112023-12-13 17:56:28 -050069 vcpu->direct_request_origin.is_ffa_req2 = false;
70 vcpu->direct_request_origin.vm_id = HF_INVALID_VM_ID;
Olivier Deprezb2808332023-02-02 15:25:40 +010071 vcpu->rt_model = RTM_SP_INIT;
J-Alves67f5ba32024-09-27 18:07:11 +010072 list_init(&vcpu->boot_list_node);
Madhukar Pappireddyeed861e2024-09-25 13:50:54 -050073 list_init(&vcpu->timer_node);
Daniel Boulby7011b5a2024-10-15 18:27:26 +010074 list_init(&vcpu->ipi_list_node);
Fuad Tabba5c738432019-12-02 11:02:42 +000075}
76
77/**
78 * Initialise the registers for the given vCPU and set the state to
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050079 * VCPU_STATE_WAITING. The caller must hold the vCPU lock while calling this.
Fuad Tabba5c738432019-12-02 11:02:42 +000080 */
81void vcpu_on(struct vcpu_locked vcpu, ipaddr_t entry, uintreg_t arg)
82{
83 arch_regs_set_pc_arg(&vcpu.vcpu->regs, entry, arg);
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050084 vcpu.vcpu->state = VCPU_STATE_WAITING;
Fuad Tabba5c738432019-12-02 11:02:42 +000085}
86
Andrew Walbranb5ab43c2020-04-30 11:32:54 +010087ffa_vcpu_index_t vcpu_index(const struct vcpu *vcpu)
Fuad Tabba5c738432019-12-02 11:02:42 +000088{
89 size_t index = vcpu - vcpu->vm->vcpus;
90
91 CHECK(index < UINT16_MAX);
92 return index;
93}
94
95/**
96 * Check whether the given vcpu_state is an off state, for the purpose of
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -050097 * turning vCPUs on and off. Note that Aborted still counts as ON for the
98 * purposes of PSCI, because according to the PSCI specification (section
Olivier Depreze7eb1682022-03-16 17:09:03 +010099 * 5.7.1) a core is only considered to be off if it has been turned off
100 * with a CPU_OFF call or hasn't yet been turned on with a CPU_ON call.
Fuad Tabba5c738432019-12-02 11:02:42 +0000101 */
102bool vcpu_is_off(struct vcpu_locked vcpu)
103{
Madhukar Pappireddyb11e0d12021-08-02 19:44:35 -0500104 return (vcpu.vcpu->state == VCPU_STATE_OFF);
Fuad Tabba5c738432019-12-02 11:02:42 +0000105}
106
107/**
108 * Starts a vCPU of a secondary VM.
109 *
110 * Returns true if the secondary was reset and started, or false if it was
111 * already on and so nothing was done.
112 */
Max Shvetsov40108e72020-08-27 12:39:50 +0100113bool vcpu_secondary_reset_and_start(struct vcpu_locked vcpu_locked,
114 ipaddr_t entry, uintreg_t arg)
Fuad Tabba5c738432019-12-02 11:02:42 +0000115{
Max Shvetsov40108e72020-08-27 12:39:50 +0100116 struct vm *vm = vcpu_locked.vcpu->vm;
Fuad Tabba5c738432019-12-02 11:02:42 +0000117 bool vcpu_was_off;
118
119 CHECK(vm->id != HF_PRIMARY_VM_ID);
120
Fuad Tabba5c738432019-12-02 11:02:42 +0000121 vcpu_was_off = vcpu_is_off(vcpu_locked);
122 if (vcpu_was_off) {
123 /*
124 * Set vCPU registers to a clean state ready for boot. As this
125 * is a secondary which can migrate between pCPUs, the ID of the
126 * vCPU is defined as the index and does not match the ID of the
127 * pCPU it is running on.
128 */
Max Shvetsov40108e72020-08-27 12:39:50 +0100129 arch_regs_reset(vcpu_locked.vcpu);
Fuad Tabba5c738432019-12-02 11:02:42 +0000130 vcpu_on(vcpu_locked, entry, arg);
131 }
Fuad Tabba5c738432019-12-02 11:02:42 +0000132
133 return vcpu_was_off;
134}
135
136/**
137 * Handles a page fault. It does so by determining if it's a legitimate or
138 * spurious fault, and recovering from the latter.
139 *
Fuad Tabbaed294af2019-12-20 10:43:01 +0000140 * Returns true if the caller should resume the current vCPU, or false if its VM
Fuad Tabba5c738432019-12-02 11:02:42 +0000141 * should be aborted.
142 */
143bool vcpu_handle_page_fault(const struct vcpu *current,
144 struct vcpu_fault_info *f)
145{
146 struct vm *vm = current->vm;
147 uint32_t mode;
148 uint32_t mask = f->mode | MM_MODE_INVALID;
149 bool resume;
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800150 struct vm_locked locked_vm;
Fuad Tabba5c738432019-12-02 11:02:42 +0000151
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800152 locked_vm = vm_lock(vm);
Fuad Tabba5c738432019-12-02 11:02:42 +0000153 /*
154 * Check if this is a legitimate fault, i.e., if the page table doesn't
155 * allow the access attempted by the VM.
156 *
157 * Otherwise, this is a spurious fault, likely because another CPU is
158 * updating the page table. It is responsible for issuing global TLB
159 * invalidations while holding the VM lock, so we don't need to do
160 * anything else to recover from it. (Acquiring/releasing the lock
161 * ensured that the invalidations have completed.)
162 */
Raghu Krishnamurthyb5775d22021-02-26 18:54:40 -0800163 if (!locked_vm.vm->el0_partition) {
164 resume = vm_mem_get_mode(locked_vm, f->ipaddr,
165 ipa_add(f->ipaddr, 1), &mode) &&
166 (mode & mask) == f->mode;
167 } else {
168 /*
169 * For EL0 partitions we need to get the mode for the faulting
170 * vaddr.
171 */
172 resume =
173 vm_mem_get_mode(locked_vm, ipa_init(va_addr(f->vaddr)),
174 ipa_add(ipa_init(va_addr(f->vaddr)), 1),
175 &mode) &&
176 (mode & mask) == f->mode;
Raghu Krishnamurthyf16b2ce2021-11-02 07:48:38 -0700177
178 /*
179 * For EL0 partitions, if there is an instruction abort and the
180 * mode of the page is RWX, we don't resume since Hafnium does
181 * not allow write and executable pages.
182 */
183 if ((f->mode == MM_MODE_X) &&
184 ((mode & MM_MODE_W) == MM_MODE_W)) {
185 resume = false;
186 }
Raghu Krishnamurthyb5775d22021-02-26 18:54:40 -0800187 }
Fuad Tabba5c738432019-12-02 11:02:42 +0000188
Raghu Krishnamurthy785d52f2021-02-13 00:02:40 -0800189 vm_unlock(&locked_vm);
Fuad Tabba5c738432019-12-02 11:02:42 +0000190
191 if (!resume) {
Andrew Walbran17eebf92020-02-05 16:35:49 +0000192 dlog_warning(
Karl Meakine8937d92024-03-19 16:04:25 +0000193 "Stage-%d page fault: pc=%#lx, vmid=%#x, vcpu=%u, "
194 "vaddr=%#lx, ipaddr=%#lx, mode=%#x %#x\n",
195 current->vm->el0_partition ? 1 : 2, va_addr(f->pc),
196 vm->id, vcpu_index(current), va_addr(f->vaddr),
197 ipa_addr(f->ipaddr), f->mode, mode);
Fuad Tabba5c738432019-12-02 11:02:42 +0000198 }
199
200 return resume;
201}
Olivier Deprez2ebae3a2020-06-11 16:34:30 +0200202
J-Alves7ac49052022-02-08 17:20:53 +0000203void vcpu_set_phys_core_idx(struct vcpu *vcpu)
204{
205 arch_regs_set_gp_reg(&vcpu->regs, cpu_index(vcpu->cpu),
206 PHYS_CORE_IDX_GP_REG);
207}
Olivier Deprez181074b2023-02-02 14:53:23 +0100208
209/**
Olivier Deprez632249e2022-09-26 09:18:31 +0200210 * Sets the designated GP register through which the vCPU expects to receive the
211 * boot info's address.
212 */
213void vcpu_set_boot_info_gp_reg(struct vcpu *vcpu)
214{
215 struct vm *vm = vcpu->vm;
216 uint32_t gp_register_num = vm->boot_info.gp_register_num;
217
218 if (vm->boot_info.blob_addr.ipa != 0U) {
219 arch_regs_set_gp_reg(&vcpu->regs,
220 ipa_addr(vm->boot_info.blob_addr),
221 gp_register_num);
222 }
223}
224
225/**
J-Alves67f5ba32024-09-27 18:07:11 +0100226 * The 'boot_list' is used as the start and end of the list.
227 * Start: the nodes it points to is the first vCPU to boot.
228 * End: the last node's next points to the entry.
229 */
230static bool vcpu_is_boot_list_end(struct vcpu *vcpu)
231{
232 return vcpu->boot_list_node.next == &boot_list;
233}
234
235/**
Olivier Deprez181074b2023-02-02 14:53:23 +0100236 * Gets the first partition to boot, according to Boot Protocol from FFA spec.
237 */
238struct vcpu *vcpu_get_boot_vcpu(void)
239{
J-Alves67f5ba32024-09-27 18:07:11 +0100240 assert(!list_empty(&boot_list));
241
242 return CONTAINER_OF(boot_list.next, struct vcpu, boot_list_node);
243}
244
245/**
246 * Returns the next element in the boot order list, if there is one.
247 */
248struct vcpu *vcpu_get_next_boot(struct vcpu *vcpu)
249{
250 return vcpu_is_boot_list_end(vcpu)
251 ? NULL
252 : CONTAINER_OF(vcpu->boot_list_node.next, struct vcpu,
253 boot_list_node);
Olivier Deprez181074b2023-02-02 14:53:23 +0100254}
255
256/**
257 * Insert in boot list, sorted by `boot_order` parameter in the vm structure
258 * and rooted in `first_boot_vm`.
259 */
260void vcpu_update_boot(struct vcpu *vcpu)
261{
262 struct vcpu *current = NULL;
Olivier Deprez181074b2023-02-02 14:53:23 +0100263
J-Alves67f5ba32024-09-27 18:07:11 +0100264 if (list_empty(&boot_list)) {
265 list_prepend(&boot_list, &vcpu->boot_list_node);
Olivier Deprez181074b2023-02-02 14:53:23 +0100266 return;
267 }
268
J-Alves67f5ba32024-09-27 18:07:11 +0100269 /*
270 * When getting to this point the first insertion should have
271 * been done.
272 */
273 current = vcpu_get_boot_vcpu();
274 assert(current != NULL);
Olivier Deprez181074b2023-02-02 14:53:23 +0100275
J-Alves67f5ba32024-09-27 18:07:11 +0100276 /*
277 * Iterate until the position is found according to boot order, or
278 * until we reach end of the list.
279 */
280 while (!vcpu_is_boot_list_end(current) &&
Olivier Deprez181074b2023-02-02 14:53:23 +0100281 current->vm->boot_order <= vcpu->vm->boot_order) {
J-Alves67f5ba32024-09-27 18:07:11 +0100282 current = vcpu_get_next_boot(current);
Olivier Deprez181074b2023-02-02 14:53:23 +0100283 }
284
J-Alves67f5ba32024-09-27 18:07:11 +0100285 current->vm->boot_order > vcpu->vm->boot_order
286 ? list_prepend(&current->boot_list_node, &vcpu->boot_list_node)
287 : list_append(&current->boot_list_node, &vcpu->boot_list_node);
Olivier Deprez181074b2023-02-02 14:53:23 +0100288}
J-Alves12cedae2023-08-04 14:37:37 +0100289
J-Alvesb8730e92024-08-07 18:28:55 +0100290void vcpu_interrupt_clear_decrement(struct vcpu_locked vcpu_locked,
291 uint32_t intid)
292{
293 struct interrupts *interrupts = &(vcpu_locked.vcpu->interrupts);
294
Daniel Boulby1f2babf2024-08-29 16:39:47 +0100295 /* Clear any specifics for the current intid. */
296 switch (intid) {
297 case HF_IPI_INTID:
298 vcpu_ipi_clear_info_get_retrieved(vcpu_locked);
299 break;
300 default:
301 /* Do no additional work. */
302 break;
303 }
304
J-Alvesb8730e92024-08-07 18:28:55 +0100305 vcpu_virt_interrupt_clear_pending(interrupts, intid);
306 vcpu_interrupt_count_decrement(vcpu_locked, interrupts, intid);
307}
308
J-Alves0247fe62024-02-23 10:21:46 +0000309/**
310 * Sets the vcpu in the VCPU_STATE_RUNNING.
J-Alves67a79262024-07-17 12:01:39 +0100311 * With that, its register are set as "not available".
312 * If there are registers to be written to vCPU's context, do so.
313 * However, this action is restricted to WAITING and BLOCKED states,
314 * as such, assert accordingly.
J-Alves0247fe62024-02-23 10:21:46 +0000315 */
J-Alves478faac2024-10-23 10:35:57 +0100316void vcpu_set_running(struct vcpu_locked target_locked,
317 const struct ffa_value *args)
J-Alves12cedae2023-08-04 14:37:37 +0100318{
319 struct vcpu *target_vcpu = target_locked.vcpu;
320
J-Alves67a79262024-07-17 12:01:39 +0100321 if (args != NULL) {
J-Alves0247fe62024-02-23 10:21:46 +0000322 CHECK(target_vcpu->regs_available);
J-Alves67a79262024-07-17 12:01:39 +0100323 assert(target_vcpu->state == VCPU_STATE_WAITING ||
324 target_vcpu->state == VCPU_STATE_BLOCKED);
J-Alves0247fe62024-02-23 10:21:46 +0000325
326 arch_regs_set_retval(&target_vcpu->regs, *args);
327 }
J-Alves12cedae2023-08-04 14:37:37 +0100328
329 /* Mark the registers as unavailable now. */
330 target_vcpu->regs_available = false;
331
332 /* We are about to resume target vCPU. */
333 target_vcpu->state = VCPU_STATE_RUNNING;
334}
335
336/**
J-Alves12cedae2023-08-04 14:37:37 +0100337 * It injects a virtual interrupt in the vcpu if is enabled and is not pending.
338 */
339void vcpu_interrupt_inject(struct vcpu_locked target_locked, uint32_t intid)
340{
341 struct vcpu *target_vcpu = target_locked.vcpu;
342 struct interrupts *interrupts = &target_vcpu->interrupts;
343
344 /*
345 * We only need to change state and (maybe) trigger a virtual interrupt
346 * if it is enabled and was not previously pending. Otherwise we can
347 * skip everything except setting the pending bit.
348 */
349 if (!(vcpu_is_virt_interrupt_enabled(interrupts, intid) &&
350 !vcpu_is_virt_interrupt_pending(interrupts, intid))) {
351 goto out;
352 }
353
354 /* Increment the count. */
355 vcpu_interrupt_count_increment(target_locked, interrupts, intid);
356
357 /*
358 * Only need to update state if there was not already an
359 * interrupt enabled and pending.
360 */
361 if (vcpu_interrupt_count_get(target_locked) != 1) {
362 goto out;
363 }
364
365out:
366 /* Either way, make it pending. */
367 vcpu_virt_interrupt_set_pending(interrupts, intid);
368}
369
J-Alves12cedae2023-08-04 14:37:37 +0100370void vcpu_enter_secure_interrupt_rtm(struct vcpu_locked vcpu_locked)
371{
372 struct vcpu *target_vcpu = vcpu_locked.vcpu;
373
374 assert(target_vcpu->scheduling_mode == NONE);
375 assert(target_vcpu->call_chain.prev_node == NULL);
376 assert(target_vcpu->call_chain.next_node == NULL);
377 assert(target_vcpu->rt_model == RTM_NONE);
378
379 target_vcpu->scheduling_mode = SPMC_MODE;
380 target_vcpu->rt_model = RTM_SEC_INTERRUPT;
381}
Madhukar Pappireddy32913cb2024-07-19 13:04:05 -0500382
383static uint16_t queue_increment_index(uint16_t current_idx)
384{
385 /* Look at the next index. Wrap around if necessary. */
386 if (current_idx == VINT_QUEUE_MAX - 1) {
387 return 0;
388 }
389
390 return current_idx + 1;
391}
392
393static bool is_queue_empty(struct interrupt_queue *q)
394{
395 if (q->head == q->tail) {
396 return true;
397 }
398
399 return false;
400}
401
402/**
403 * Queue the pending virtual interrupt for target vCPU.
404 *
405 * Returns true if successful in pushing a new entry to the queue, or false
406 * otherwise.
407 */
408bool vcpu_interrupt_queue_push(struct vcpu_locked vcpu_locked, uint32_t vint_id)
409{
410 struct interrupt_queue *q;
411 uint16_t new_tail;
412
413 assert(vint_id != HF_INVALID_INTID);
414
415 q = &vcpu_locked.vcpu->interrupts.vint_q;
416
417 /*
418 * A new entry is pushed at the tail of the queue. Upon successful
419 * push operation, the tail increments or wraps around.
420 */
421 new_tail = queue_increment_index(q->tail);
422
423 /* If new_tail reaches head of the queue, then the queue is full. */
424 if (new_tail == q->head) {
425 return false;
426 }
427
428 /* Add the virtual interrupt to the queue. */
429 q->vint_buffer[q->tail] = vint_id;
430 q->tail = new_tail;
431
432 return true;
433}
434
435/**
436 * Remove an entry from the specified vCPU's queue at the head.
437 *
438 * Returns true if successful in removing the entry, or false otherwise.
439 */
440bool vcpu_interrupt_queue_pop(struct vcpu_locked vcpu_locked, uint32_t *vint_id)
441{
442 struct interrupt_queue *q;
443 uint16_t new_head;
444
445 assert(vint_id != NULL);
446
447 q = &vcpu_locked.vcpu->interrupts.vint_q;
448
449 /* Check if queue is empty. */
450 if (is_queue_empty(q)) {
451 return false;
452 }
453
454 /*
455 * An entry is removed from the head of the queue. Once successful, the
456 * head is incremented or wrapped around if needed.
457 */
458 new_head = queue_increment_index(q->head);
459 *vint_id = q->vint_buffer[q->head];
460 q->head = new_head;
461
462 return true;
463}
464
465/**
466 * Look for the first pending virtual interrupt from the vcpu's queue. Note
467 * that the entry is not removed from the queue.
468 *
469 * Returns true if a valid entry exists in the queue, or false otherwise.
470 */
471bool vcpu_interrupt_queue_peek(struct vcpu_locked vcpu_locked,
472 uint32_t *vint_id)
473{
474 struct interrupt_queue *q;
475 uint32_t queued_vint;
476
477 assert(vint_id != NULL);
478
479 q = &vcpu_locked.vcpu->interrupts.vint_q;
480
481 /* Check if queue is empty. */
482 if (is_queue_empty(q)) {
483 return false;
484 }
485
486 queued_vint = q->vint_buffer[q->head];
487 assert(queued_vint != HF_INVALID_INTID);
488
489 *vint_id = queued_vint;
490 return true;
491}
492
493/**
494 * Find if a specific virtual interrupt exists in the specified vCPU's queue.
495 *
496 * Returns true if such an entry exists in the queue, or false otherwise.
497 */
498bool vcpu_is_interrupt_in_queue(struct vcpu_locked vcpu_locked,
499 uint32_t vint_id)
500{
501 struct interrupt_queue *q;
502 uint16_t next;
503
504 assert(vint_id != HF_INVALID_INTID);
505
506 q = &vcpu_locked.vcpu->interrupts.vint_q;
507
508 /* Check if the queue is empty. */
509 if (is_queue_empty(q)) {
510 return false;
511 }
512
513 next = q->head;
514 while (true) {
515 /* Match found. */
516 if (q->vint_buffer[next] == vint_id) {
517 return true;
518 }
519
520 next = queue_increment_index(next);
521
522 /* Reached the end of queue. */
523 if (next == q->tail) {
524 break;
525 }
526 }
527
528 return false;
529}
530
531/**
532 * Check if there are any entries in the interrupt queue.
533 *
534 * Returns true if queue is empty, or false otherwise.
535 */
536bool vcpu_is_interrupt_queue_empty(struct vcpu_locked vcpu_locked)
537{
538 struct interrupt_queue *q;
539
540 q = &vcpu_locked.vcpu->interrupts.vint_q;
541
542 if (is_queue_empty(q)) {
543 return true;
544 }
545
546 return false;
547}
J-Alves3b31f092024-08-07 13:26:29 +0100548
549/**
550 * When interrupt handling is complete the preempted_vcpu field should go back
551 * to NULL.
552 */
553void vcpu_secure_interrupt_complete(struct vcpu_locked vcpu_locked)
554{
555 struct vcpu *vcpu;
556
557 vcpu = vcpu_locked.vcpu;
558 vcpu->preempted_vcpu = NULL;
J-Alvesac940752024-08-07 14:02:51 +0100559 vcpu->requires_deactivate_call = false;
J-Alves3b31f092024-08-07 13:26:29 +0100560}