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