1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <types.h>
8 #include <errno.h>
9 #include <asm/lib/bits.h>
10 #include <asm/lib/atomic.h>
11 #include <asm/irq.h>
12 #include <asm/cpu.h>
13 #include <asm/per_cpu.h>
14 #include <asm/lapic.h>
15 #include <asm/guest/vm.h>
16 #include <asm/guest/virq.h>
17
18 static uint32_t notification_irq = IRQ_INVALID;
19
20 static uint64_t smp_call_mask = 0UL;
21
22 /* run in interrupt context */
kick_notification(__unused uint32_t irq,__unused void * data)23 static void kick_notification(__unused uint32_t irq, __unused void *data)
24 {
25 /* Notification vector is used to kick target cpu out of non-root mode.
26 * And it also serves for smp call.
27 */
28 uint16_t pcpu_id = get_pcpu_id();
29
30 if (bitmap_test(pcpu_id, &smp_call_mask)) {
31 struct smp_call_info_data *smp_call =
32 &per_cpu(smp_call_info, pcpu_id);
33
34 if (smp_call->func != NULL) {
35 smp_call->func(smp_call->data);
36 }
37 bitmap_clear_lock(pcpu_id, &smp_call_mask);
38 }
39 }
40
handle_smp_call(void)41 void handle_smp_call(void)
42 {
43 kick_notification(0, NULL);
44 }
45
smp_call_function(uint64_t mask,smp_call_func_t func,void * data)46 void smp_call_function(uint64_t mask, smp_call_func_t func, void *data)
47 {
48 uint16_t pcpu_id;
49 struct smp_call_info_data *smp_call;
50
51 /* wait for previous smp call complete, which may run on other cpus */
52 while (atomic_cmpxchg64(&smp_call_mask, 0UL, mask) != 0UL);
53 pcpu_id = ffs64(mask);
54 while (pcpu_id < MAX_PCPU_NUM) {
55 bitmap_clear_nolock(pcpu_id, &mask);
56 if (pcpu_id == get_pcpu_id()) {
57 func(data);
58 bitmap_clear_nolock(pcpu_id, &smp_call_mask);
59 } else if (is_pcpu_active(pcpu_id)) {
60 smp_call = &per_cpu(smp_call_info, pcpu_id);
61 smp_call->func = func;
62 smp_call->data = data;
63
64 struct acrn_vcpu *vcpu = get_ever_run_vcpu(pcpu_id);
65
66 if ((vcpu != NULL) && (is_lapic_pt_enabled(vcpu))) {
67 vcpu_make_request(vcpu, ACRN_REQUEST_SMP_CALL);
68 } else {
69 send_single_ipi(pcpu_id, NOTIFY_VCPU_VECTOR);
70 }
71 } else {
72 /* pcpu is not in active, print error */
73 pr_err("pcpu_id %d not in active!", pcpu_id);
74 bitmap_clear_nolock(pcpu_id, &smp_call_mask);
75 }
76 pcpu_id = ffs64(mask);
77 }
78 /* wait for current smp call complete */
79 wait_sync_change(&smp_call_mask, 0UL);
80 }
81
request_notification_irq(irq_action_t func,void * data)82 static int32_t request_notification_irq(irq_action_t func, void *data)
83 {
84 int32_t retval;
85
86 if (notification_irq != IRQ_INVALID) {
87 pr_info("%s, Notification vector already allocated on this CPU", __func__);
88 retval = -EBUSY;
89 } else {
90 /* all cpu register the same notification vector */
91 retval = request_irq(NOTIFY_VCPU_IRQ, func, data, IRQF_NONE);
92 if (retval < 0) {
93 pr_err("Failed to add notify isr");
94 retval = -ENODEV;
95 } else {
96 notification_irq = (uint32_t)retval;
97 }
98 }
99
100 return retval;
101 }
102
103 /*
104 * @pre be called only by BSP initialization process
105 */
setup_notification(void)106 void setup_notification(void)
107 {
108 /* support IPI notification, Service VM will register all CPU */
109 if (request_notification_irq(kick_notification, NULL) < 0) {
110 pr_err("Failed to setup notification");
111 }
112
113 dev_dbg(DBG_LEVEL_PTIRQ, "NOTIFY: irq[%d] setup vector %x",
114 notification_irq, irq_to_vector(notification_irq));
115 }
116
117 /*
118 * posted interrupt handler
119 * @pre (irq - POSTED_INTR_IRQ) < CONFIG_MAX_VM_NUM
120 */
handle_pi_notification(uint32_t irq,__unused void * data)121 static void handle_pi_notification(uint32_t irq, __unused void *data)
122 {
123 uint32_t vcpu_index = irq - POSTED_INTR_IRQ;
124
125 ASSERT(vcpu_index < CONFIG_MAX_VM_NUM, "");
126 vcpu_handle_pi_notification(vcpu_index);
127 }
128
129 /*pre-condition: be called only by BSP initialization proccess*/
setup_pi_notification(void)130 void setup_pi_notification(void)
131 {
132 uint32_t i;
133
134 for (i = 0U; i < CONFIG_MAX_VM_NUM; i++) {
135 if (request_irq(POSTED_INTR_IRQ + i, handle_pi_notification, NULL, IRQF_NONE) < 0) {
136 pr_err("Failed to setup pi notification");
137 break;
138 }
139 }
140 }
141