xref: /linux/drivers/hv/mshv_synic.c (revision b2f1e6301efa4a80becdb0715416c3cbc693fbb4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2023, Microsoft Corporation.
4  *
5  * mshv_root module's main interrupt handler and associated functionality.
6  *
7  * Authors: Microsoft Linux virtualization team
8  */
9 
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/mm.h>
13 #include <linux/interrupt.h>
14 #include <linux/io.h>
15 #include <linux/cpuhotplug.h>
16 #include <linux/hyperv.h>
17 #include <linux/reboot.h>
18 #include <asm/mshyperv.h>
19 #include <linux/acpi.h>
20 
21 #include "mshv_eventfd.h"
22 #include "mshv.h"
23 
24 static int synic_cpuhp_online;
25 static struct hv_synic_pages __percpu *synic_pages;
26 static int mshv_sint_vector = -1; /* hwirq for the SynIC SINTs */
27 static int mshv_sint_irq = -1; /* Linux IRQ for mshv_sint_vector */
28 
29 static u32 synic_event_ring_get_queued_port(u32 sint_index)
30 {
31 	struct hv_synic_event_ring_page **event_ring_page;
32 	volatile struct hv_synic_event_ring *ring;
33 	struct hv_synic_pages *spages;
34 	u8 **synic_eventring_tail;
35 	u32 message;
36 	u8 tail;
37 
38 	spages = this_cpu_ptr(synic_pages);
39 	event_ring_page = &spages->synic_event_ring_page;
40 	synic_eventring_tail = (u8 **)this_cpu_ptr(hv_synic_eventring_tail);
41 
42 	if (unlikely(!*synic_eventring_tail)) {
43 		pr_debug("Missing synic event ring tail!\n");
44 		return 0;
45 	}
46 	tail = (*synic_eventring_tail)[sint_index];
47 
48 	if (unlikely(!*event_ring_page)) {
49 		pr_debug("Missing synic event ring page!\n");
50 		return 0;
51 	}
52 
53 	ring = &(*event_ring_page)->sint_event_ring[sint_index];
54 
55 	/*
56 	 * Get the message.
57 	 */
58 	message = ring->data[tail];
59 
60 	if (!message) {
61 		if (ring->ring_full) {
62 			/*
63 			 * Ring is marked full, but we would have consumed all
64 			 * the messages. Notify the hypervisor that ring is now
65 			 * empty and check again.
66 			 */
67 			ring->ring_full = 0;
68 			hv_call_notify_port_ring_empty(sint_index);
69 			message = ring->data[tail];
70 		}
71 
72 		if (!message) {
73 			ring->signal_masked = 0;
74 			/*
75 			 * Unmask the signal and sync with hypervisor
76 			 * before one last check for any message.
77 			 */
78 			mb();
79 			message = ring->data[tail];
80 
81 			/*
82 			 * Ok, lets bail out.
83 			 */
84 			if (!message)
85 				return 0;
86 		}
87 
88 		ring->signal_masked = 1;
89 	}
90 
91 	/*
92 	 * Clear the message in the ring buffer.
93 	 */
94 	ring->data[tail] = 0;
95 
96 	if (++tail == HV_SYNIC_EVENT_RING_MESSAGE_COUNT)
97 		tail = 0;
98 
99 	(*synic_eventring_tail)[sint_index] = tail;
100 
101 	return message;
102 }
103 
104 static bool
105 mshv_doorbell_isr(struct hv_message *msg)
106 {
107 	struct hv_notification_message_payload *notification;
108 	u32 port;
109 
110 	if (msg->header.message_type != HVMSG_SYNIC_SINT_INTERCEPT)
111 		return false;
112 
113 	notification = (struct hv_notification_message_payload *)msg->u.payload;
114 	if (notification->sint_index != HV_SYNIC_DOORBELL_SINT_INDEX)
115 		return false;
116 
117 	while ((port = synic_event_ring_get_queued_port(HV_SYNIC_DOORBELL_SINT_INDEX))) {
118 		struct port_table_info ptinfo = { 0 };
119 
120 		if (mshv_portid_lookup(port, &ptinfo)) {
121 			pr_debug("Failed to get port info from port_table!\n");
122 			continue;
123 		}
124 
125 		if (ptinfo.hv_port_type != HV_PORT_TYPE_DOORBELL) {
126 			pr_debug("Not a doorbell port!, port: %d, port_type: %d\n",
127 				 port, ptinfo.hv_port_type);
128 			continue;
129 		}
130 
131 		/* Invoke the callback */
132 		ptinfo.hv_port_doorbell.doorbell_cb(port,
133 						 ptinfo.hv_port_doorbell.data);
134 	}
135 
136 	return true;
137 }
138 
139 static bool mshv_async_call_completion_isr(struct hv_message *msg)
140 {
141 	bool handled = false;
142 	struct hv_async_completion_message_payload *async_msg;
143 	struct mshv_partition *partition;
144 	u64 partition_id;
145 
146 	if (msg->header.message_type != HVMSG_ASYNC_CALL_COMPLETION)
147 		goto out;
148 
149 	async_msg =
150 		(struct hv_async_completion_message_payload *)msg->u.payload;
151 
152 	partition_id = async_msg->partition_id;
153 
154 	/*
155 	 * Hold this lock for the rest of the isr, because the partition could
156 	 * be released anytime.
157 	 * e.g. the MSHV_RUN_VP thread could wake on another cpu; it could
158 	 * release the partition unless we hold this!
159 	 */
160 	rcu_read_lock();
161 
162 	partition = mshv_partition_find(partition_id);
163 
164 	if (unlikely(!partition)) {
165 		pr_debug("failed to find partition %llu\n", partition_id);
166 		goto unlock_out;
167 	}
168 
169 	partition->async_hypercall_status = async_msg->status;
170 	complete(&partition->async_hypercall);
171 
172 	handled = true;
173 
174 unlock_out:
175 	rcu_read_unlock();
176 out:
177 	return handled;
178 }
179 
180 static void kick_vp(struct mshv_vp *vp)
181 {
182 	atomic64_inc(&vp->run.vp_signaled_count);
183 	vp->run.kicked_by_hv = 1;
184 	wake_up(&vp->run.vp_suspend_queue);
185 }
186 
187 static void
188 handle_bitset_message(const struct hv_vp_signal_bitset_scheduler_message *msg)
189 {
190 	int bank_idx, vps_signaled = 0, bank_mask_size;
191 	struct mshv_partition *partition;
192 	const struct hv_vpset *vpset;
193 	const u64 *bank_contents;
194 	u64 partition_id = msg->partition_id;
195 
196 	if (msg->vp_bitset.bitset.format != HV_GENERIC_SET_SPARSE_4K) {
197 		pr_debug("scheduler message format is not HV_GENERIC_SET_SPARSE_4K");
198 		return;
199 	}
200 
201 	if (msg->vp_count == 0) {
202 		pr_debug("scheduler message with no VP specified");
203 		return;
204 	}
205 
206 	rcu_read_lock();
207 
208 	partition = mshv_partition_find(partition_id);
209 	if (unlikely(!partition)) {
210 		pr_debug("failed to find partition %llu\n", partition_id);
211 		goto unlock_out;
212 	}
213 
214 	vpset = &msg->vp_bitset.bitset;
215 
216 	bank_idx = -1;
217 	bank_contents = vpset->bank_contents;
218 	bank_mask_size = sizeof(vpset->valid_bank_mask) * BITS_PER_BYTE;
219 
220 	while (true) {
221 		int vp_bank_idx = -1;
222 		int vp_bank_size = sizeof(*bank_contents) * BITS_PER_BYTE;
223 		int vp_index;
224 
225 		bank_idx = find_next_bit((unsigned long *)&vpset->valid_bank_mask,
226 					 bank_mask_size, bank_idx + 1);
227 		if (bank_idx == bank_mask_size)
228 			break;
229 
230 		while (true) {
231 			struct mshv_vp *vp;
232 
233 			vp_bank_idx = find_next_bit((unsigned long *)bank_contents,
234 						    vp_bank_size, vp_bank_idx + 1);
235 			if (vp_bank_idx == vp_bank_size)
236 				break;
237 
238 			vp_index = (bank_idx * vp_bank_size) + vp_bank_idx;
239 
240 			/* This shouldn't happen, but just in case. */
241 			if (unlikely(vp_index >= MSHV_MAX_VPS)) {
242 				pr_debug("VP index %u out of bounds\n",
243 					 vp_index);
244 				goto unlock_out;
245 			}
246 
247 			vp = partition->pt_vp_array[vp_index];
248 			if (unlikely(!vp)) {
249 				pr_debug("failed to find VP %u\n", vp_index);
250 				goto unlock_out;
251 			}
252 
253 			kick_vp(vp);
254 			vps_signaled++;
255 		}
256 
257 		bank_contents++;
258 	}
259 
260 unlock_out:
261 	rcu_read_unlock();
262 
263 	if (vps_signaled != msg->vp_count)
264 		pr_debug("asked to signal %u VPs but only did %u\n",
265 			 msg->vp_count, vps_signaled);
266 }
267 
268 static void
269 handle_pair_message(const struct hv_vp_signal_pair_scheduler_message *msg)
270 {
271 	struct mshv_partition *partition = NULL;
272 	struct mshv_vp *vp;
273 	int idx;
274 
275 	rcu_read_lock();
276 
277 	for (idx = 0; idx < msg->vp_count; idx++) {
278 		u64 partition_id = msg->partition_ids[idx];
279 		u32 vp_index = msg->vp_indexes[idx];
280 
281 		if (idx == 0 || partition->pt_id != partition_id) {
282 			partition = mshv_partition_find(partition_id);
283 			if (unlikely(!partition)) {
284 				pr_debug("failed to find partition %llu\n",
285 					 partition_id);
286 				break;
287 			}
288 		}
289 
290 		/* This shouldn't happen, but just in case. */
291 		if (unlikely(vp_index >= MSHV_MAX_VPS)) {
292 			pr_debug("VP index %u out of bounds\n", vp_index);
293 			break;
294 		}
295 
296 		vp = partition->pt_vp_array[vp_index];
297 		if (!vp) {
298 			pr_debug("failed to find VP %u\n", vp_index);
299 			break;
300 		}
301 
302 		kick_vp(vp);
303 	}
304 
305 	rcu_read_unlock();
306 }
307 
308 static bool
309 mshv_scheduler_isr(struct hv_message *msg)
310 {
311 	if (msg->header.message_type != HVMSG_SCHEDULER_VP_SIGNAL_BITSET &&
312 	    msg->header.message_type != HVMSG_SCHEDULER_VP_SIGNAL_PAIR)
313 		return false;
314 
315 	if (msg->header.message_type == HVMSG_SCHEDULER_VP_SIGNAL_BITSET)
316 		handle_bitset_message((struct hv_vp_signal_bitset_scheduler_message *)
317 				      msg->u.payload);
318 	else
319 		handle_pair_message((struct hv_vp_signal_pair_scheduler_message *)
320 				    msg->u.payload);
321 
322 	return true;
323 }
324 
325 static bool
326 mshv_intercept_isr(struct hv_message *msg)
327 {
328 	struct mshv_partition *partition;
329 	bool handled = false;
330 	struct mshv_vp *vp;
331 	u64 partition_id;
332 	u32 vp_index;
333 
334 	partition_id = msg->header.sender;
335 
336 	rcu_read_lock();
337 
338 	partition = mshv_partition_find(partition_id);
339 	if (unlikely(!partition)) {
340 		pr_debug("failed to find partition %llu\n",
341 			 partition_id);
342 		goto unlock_out;
343 	}
344 
345 	if (msg->header.message_type == HVMSG_X64_APIC_EOI) {
346 		/*
347 		 * Check if this gsi is registered in the
348 		 * ack_notifier list and invoke the callback
349 		 * if registered.
350 		 */
351 
352 		/*
353 		 * If there is a notifier, the ack callback is supposed
354 		 * to handle the VMEXIT. So we need not pass this message
355 		 * to vcpu thread.
356 		 */
357 		struct hv_x64_apic_eoi_message *eoi_msg =
358 			(struct hv_x64_apic_eoi_message *)&msg->u.payload[0];
359 
360 		if (mshv_notify_acked_gsi(partition, eoi_msg->interrupt_vector)) {
361 			handled = true;
362 			goto unlock_out;
363 		}
364 	}
365 
366 	/*
367 	 * We should get an opaque intercept message here for all intercept
368 	 * messages, since we're using the mapped VP intercept message page.
369 	 *
370 	 * The intercept message will have been placed in intercept message
371 	 * page at this point.
372 	 *
373 	 * Make sure the message type matches our expectation.
374 	 */
375 	if (msg->header.message_type != HVMSG_OPAQUE_INTERCEPT) {
376 		pr_debug("wrong message type %d", msg->header.message_type);
377 		goto unlock_out;
378 	}
379 
380 	/*
381 	 * Since we directly index the vp, and it has to exist for us to be here
382 	 * (because the vp is only deleted when the partition is), no additional
383 	 * locking is needed here
384 	 */
385 	vp_index =
386 	       ((struct hv_opaque_intercept_message *)msg->u.payload)->vp_index;
387 	/* This shouldn't happen, but just in case. */
388 	if (unlikely(vp_index >= MSHV_MAX_VPS)) {
389 		pr_debug("VP index %u out of bounds\n", vp_index);
390 		goto unlock_out;
391 	}
392 	vp = partition->pt_vp_array[vp_index];
393 	if (unlikely(!vp)) {
394 		pr_debug("failed to find VP %u\n", vp_index);
395 		goto unlock_out;
396 	}
397 
398 	kick_vp(vp);
399 
400 	handled = true;
401 
402 unlock_out:
403 	rcu_read_unlock();
404 
405 	return handled;
406 }
407 
408 void mshv_isr(void)
409 {
410 	struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
411 	struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
412 	struct hv_message *msg;
413 	bool handled;
414 
415 	if (unlikely(!(*msg_page))) {
416 		pr_debug("Missing synic page!\n");
417 		return;
418 	}
419 
420 	msg = &((*msg_page)->sint_message[HV_SYNIC_INTERCEPTION_SINT_INDEX]);
421 
422 	/*
423 	 * If the type isn't set, there isn't really a message;
424 	 * it may be some other hyperv interrupt
425 	 */
426 	if (msg->header.message_type == HVMSG_NONE)
427 		return;
428 
429 	handled = mshv_doorbell_isr(msg);
430 
431 	if (!handled)
432 		handled = mshv_scheduler_isr(msg);
433 
434 	if (!handled)
435 		handled = mshv_async_call_completion_isr(msg);
436 
437 	if (!handled)
438 		handled = mshv_intercept_isr(msg);
439 
440 	if (handled) {
441 		/*
442 		 * Acknowledge message with hypervisor if another message is
443 		 * pending.
444 		 */
445 		msg->header.message_type = HVMSG_NONE;
446 		/*
447 		 * Ensure the write is complete so the hypervisor will deliver
448 		 * the next message if available.
449 		 */
450 		mb();
451 		if (msg->header.message_flags.msg_pending)
452 			hv_set_non_nested_msr(HV_MSR_EOM, 0);
453 	} else {
454 		pr_warn_once("%s: unknown message type 0x%x\n", __func__,
455 			     msg->header.message_type);
456 	}
457 }
458 
459 static int mshv_synic_cpu_init(unsigned int cpu)
460 {
461 	union hv_synic_simp simp;
462 	union hv_synic_siefp siefp;
463 	union hv_synic_sirbp sirbp;
464 	union hv_synic_sint sint;
465 	struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
466 	struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
467 	struct hv_synic_event_flags_page **event_flags_page =
468 			&spages->synic_event_flags_page;
469 	struct hv_synic_event_ring_page **event_ring_page =
470 			&spages->synic_event_ring_page;
471 	/*
472 	 * VMBus owns SIMP/SIEFP/SCONTROL when it is active.
473 	 * See hv_hyp_synic_enable_regs() for that initialization.
474 	 */
475 	bool vmbus_active = hv_vmbus_exists();
476 
477 	/*
478 	 * Map the SYNIC message page. When VMBus is not active the
479 	 * hypervisor pre-provisions the SIMP GPA but may not set
480 	 * simp_enabled — enable it here.
481 	 */
482 	simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP);
483 	if (!vmbus_active) {
484 		simp.simp_enabled = true;
485 		hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
486 	}
487 	*msg_page = memremap(simp.base_simp_gpa << HV_HYP_PAGE_SHIFT,
488 			     HV_HYP_PAGE_SIZE,
489 			     MEMREMAP_WB);
490 
491 	if (!(*msg_page))
492 		goto cleanup_simp;
493 
494 	/*
495 	 * Map the event flags page. Same as SIMP: enable when
496 	 * VMBus is not active, already enabled by VMBus otherwise.
497 	 */
498 	siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP);
499 	if (!vmbus_active) {
500 		siefp.siefp_enabled = true;
501 		hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
502 	}
503 	*event_flags_page = memremap(siefp.base_siefp_gpa << HV_HYP_PAGE_SHIFT,
504 				     HV_HYP_PAGE_SIZE, MEMREMAP_WB);
505 
506 	if (!(*event_flags_page))
507 		goto cleanup_siefp;
508 
509 	/* Setup the Synic's event ring page */
510 	sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
511 
512 	if (hv_root_partition()) {
513 		*event_ring_page = memremap(sirbp.base_sirbp_gpa << HV_HYP_PAGE_SHIFT,
514 					    HV_HYP_PAGE_SIZE, MEMREMAP_WB);
515 
516 		if (!(*event_ring_page))
517 			goto cleanup_siefp;
518 	} else {
519 		/*
520 		 * On L1VH the hypervisor does not provide a SIRBP page.
521 		 * Allocate one and program its GPA into the MSR.
522 		 */
523 		*event_ring_page = (struct hv_synic_event_ring_page *)
524 			get_zeroed_page(GFP_KERNEL);
525 
526 		if (!(*event_ring_page))
527 			goto cleanup_siefp;
528 
529 		sirbp.base_sirbp_gpa = virt_to_phys(*event_ring_page)
530 				>> HV_HYP_PAGE_SHIFT;
531 	}
532 
533 	sirbp.sirbp_enabled = true;
534 	hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
535 
536 	if (mshv_sint_irq != -1)
537 		enable_percpu_irq(mshv_sint_irq, 0);
538 
539 	/* Enable intercepts */
540 	sint.as_uint64 = 0;
541 	sint.vector = mshv_sint_vector;
542 	sint.masked = false;
543 	sint.auto_eoi = hv_recommend_using_aeoi();
544 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
545 			      sint.as_uint64);
546 
547 	/* Doorbell SINT */
548 	sint.as_uint64 = 0;
549 	sint.vector = mshv_sint_vector;
550 	sint.masked = false;
551 	sint.as_intercept = 1;
552 	sint.auto_eoi = hv_recommend_using_aeoi();
553 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
554 			      sint.as_uint64);
555 
556 	/* When VMBus is active it already enabled SCONTROL. */
557 	if (!vmbus_active) {
558 		union hv_synic_scontrol sctrl;
559 
560 		sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
561 		sctrl.enable = 1;
562 		hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64);
563 	}
564 
565 	return 0;
566 
567 cleanup_siefp:
568 	if (*event_flags_page)
569 		memunmap(*event_flags_page);
570 	if (!vmbus_active) {
571 		siefp.siefp_enabled = false;
572 		hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
573 	}
574 cleanup_simp:
575 	if (*msg_page)
576 		memunmap(*msg_page);
577 	if (!vmbus_active) {
578 		simp.simp_enabled = false;
579 		hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
580 	}
581 
582 	return -EFAULT;
583 }
584 
585 static int mshv_synic_cpu_exit(unsigned int cpu)
586 {
587 	union hv_synic_sint sint;
588 	union hv_synic_sirbp sirbp;
589 	struct hv_synic_pages *spages = this_cpu_ptr(synic_pages);
590 	struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
591 	struct hv_synic_event_flags_page **event_flags_page =
592 		&spages->synic_event_flags_page;
593 	struct hv_synic_event_ring_page **event_ring_page =
594 		&spages->synic_event_ring_page;
595 	/* VMBus owns SIMP/SIEFP/SCONTROL when it is active */
596 	bool vmbus_active = hv_vmbus_exists();
597 
598 	/* Disable the interrupt */
599 	sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX);
600 	sint.masked = true;
601 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
602 			      sint.as_uint64);
603 
604 	/* Disable Doorbell SINT */
605 	sint.as_uint64 = hv_get_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX);
606 	sint.masked = true;
607 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
608 			      sint.as_uint64);
609 
610 	if (mshv_sint_irq != -1)
611 		disable_percpu_irq(mshv_sint_irq);
612 
613 	/* Disable SYNIC event ring page owned by MSHV */
614 	sirbp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIRBP);
615 	sirbp.sirbp_enabled = false;
616 
617 	if (hv_root_partition()) {
618 		hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
619 		memunmap(*event_ring_page);
620 	} else {
621 		sirbp.base_sirbp_gpa = 0;
622 		hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
623 		free_page((unsigned long)*event_ring_page);
624 	}
625 
626 	/*
627 	 * Release our mappings of the message and event flags pages.
628 	 * When VMBus is not active, we enabled SIMP/SIEFP — disable
629 	 * them. Otherwise VMBus owns the MSRs — leave them.
630 	 */
631 	memunmap(*event_flags_page);
632 	if (!vmbus_active) {
633 		union hv_synic_simp simp;
634 		union hv_synic_siefp siefp;
635 
636 		siefp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIEFP);
637 		siefp.siefp_enabled = false;
638 		hv_set_non_nested_msr(HV_MSR_SIEFP, siefp.as_uint64);
639 
640 		simp.as_uint64 = hv_get_non_nested_msr(HV_MSR_SIMP);
641 		simp.simp_enabled = false;
642 		hv_set_non_nested_msr(HV_MSR_SIMP, simp.as_uint64);
643 	}
644 	memunmap(*msg_page);
645 
646 	/* When VMBus is active it owns SCONTROL — leave it. */
647 	if (!vmbus_active) {
648 		union hv_synic_scontrol sctrl;
649 
650 		sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
651 		sctrl.enable = 0;
652 		hv_set_non_nested_msr(HV_MSR_SCONTROL, sctrl.as_uint64);
653 	}
654 
655 	return 0;
656 }
657 
658 int
659 mshv_register_doorbell(u64 partition_id, doorbell_cb_t doorbell_cb, void *data,
660 		       u64 gpa, u64 val, u64 flags)
661 {
662 	struct hv_connection_info connection_info = { 0 };
663 	union hv_connection_id connection_id = { 0 };
664 	struct port_table_info *port_table_info;
665 	struct hv_port_info port_info = { 0 };
666 	union hv_port_id port_id = { 0 };
667 	int ret;
668 
669 	port_table_info = kmalloc_obj(*port_table_info);
670 	if (!port_table_info)
671 		return -ENOMEM;
672 
673 	port_table_info->hv_port_type = HV_PORT_TYPE_DOORBELL;
674 	port_table_info->hv_port_doorbell.doorbell_cb = doorbell_cb;
675 	port_table_info->hv_port_doorbell.data = data;
676 	ret = mshv_portid_alloc(port_table_info);
677 	if (ret < 0) {
678 		kfree(port_table_info);
679 		return ret;
680 	}
681 
682 	port_id.u.id = ret;
683 	port_info.port_type = HV_PORT_TYPE_DOORBELL;
684 	port_info.doorbell_port_info.target_sint = HV_SYNIC_DOORBELL_SINT_INDEX;
685 	port_info.doorbell_port_info.target_vp = HV_ANY_VP;
686 	ret = hv_call_create_port(hv_current_partition_id, port_id, partition_id,
687 				  &port_info,
688 				  0, 0, NUMA_NO_NODE);
689 
690 	if (ret < 0) {
691 		mshv_portid_free(port_id.u.id);
692 		return ret;
693 	}
694 
695 	connection_id.u.id = port_id.u.id;
696 	connection_info.port_type = HV_PORT_TYPE_DOORBELL;
697 	connection_info.doorbell_connection_info.gpa = gpa;
698 	connection_info.doorbell_connection_info.trigger_value = val;
699 	connection_info.doorbell_connection_info.flags = flags;
700 
701 	ret = hv_call_connect_port(hv_current_partition_id, port_id, partition_id,
702 				   connection_id, &connection_info, 0, NUMA_NO_NODE);
703 	if (ret < 0) {
704 		hv_call_delete_port(hv_current_partition_id, port_id);
705 		mshv_portid_free(port_id.u.id);
706 		return ret;
707 	}
708 
709 	// lets use the port_id as the doorbell_id
710 	return port_id.u.id;
711 }
712 
713 void
714 mshv_unregister_doorbell(u64 partition_id, int doorbell_portid)
715 {
716 	union hv_port_id port_id = { 0 };
717 	union hv_connection_id connection_id = { 0 };
718 
719 	connection_id.u.id = doorbell_portid;
720 	hv_call_disconnect_port(partition_id, connection_id);
721 
722 	port_id.u.id = doorbell_portid;
723 	hv_call_delete_port(hv_current_partition_id, port_id);
724 
725 	mshv_portid_free(doorbell_portid);
726 }
727 
728 static int mshv_synic_reboot_notify(struct notifier_block *nb,
729 			      unsigned long code, void *unused)
730 {
731 	mshv_debugfs_exit();
732 	cpuhp_remove_state(synic_cpuhp_online);
733 	return 0;
734 }
735 
736 static struct notifier_block mshv_synic_reboot_nb = {
737 	.notifier_call = mshv_synic_reboot_notify,
738 };
739 
740 #ifndef HYPERVISOR_CALLBACK_VECTOR
741 static DEFINE_PER_CPU(long, mshv_evt);
742 
743 static irqreturn_t mshv_percpu_isr(int irq, void *dev_id)
744 {
745 	mshv_isr();
746 	return IRQ_HANDLED;
747 }
748 
749 #ifdef CONFIG_ACPI
750 static int __init mshv_acpi_setup_sint_irq(void)
751 {
752 	return acpi_register_gsi(NULL, mshv_sint_vector, ACPI_EDGE_SENSITIVE,
753 					ACPI_ACTIVE_HIGH);
754 }
755 
756 static void mshv_acpi_cleanup_sint_irq(void)
757 {
758 	acpi_unregister_gsi(mshv_sint_vector);
759 }
760 #else
761 static int __init mshv_acpi_setup_sint_irq(void)
762 {
763 	return -ENODEV;
764 }
765 
766 static void mshv_acpi_cleanup_sint_irq(void)
767 {
768 }
769 #endif
770 
771 static int __init mshv_sint_vector_setup(void)
772 {
773 	int ret;
774 	struct hv_register_assoc reg = {
775 		.name = HV_ARM64_REGISTER_SINT_RESERVED_INTERRUPT_ID,
776 	};
777 	union hv_input_vtl input_vtl = { 0 };
778 
779 	if (acpi_disabled)
780 		return -ENODEV;
781 
782 	ret = hv_call_get_vp_registers(HV_VP_INDEX_SELF, HV_PARTITION_ID_SELF,
783 				1, input_vtl, &reg);
784 	if (ret || !reg.value.reg64)
785 		return -ENODEV;
786 
787 	mshv_sint_vector = reg.value.reg64;
788 	ret = mshv_acpi_setup_sint_irq();
789 	if (ret < 0) {
790 		pr_err("Failed to setup IRQ for MSHV SINT vector %d: %d\n",
791 			mshv_sint_vector, ret);
792 		goto out_fail;
793 	}
794 
795 	mshv_sint_irq = ret;
796 
797 	ret = request_percpu_irq(mshv_sint_irq, mshv_percpu_isr, "MSHV",
798 		&mshv_evt);
799 	if (ret)
800 		goto out_unregister;
801 
802 	return 0;
803 
804 out_unregister:
805 	mshv_acpi_cleanup_sint_irq();
806 out_fail:
807 	return ret;
808 }
809 
810 static void mshv_sint_vector_cleanup(void)
811 {
812 	free_percpu_irq(mshv_sint_irq, &mshv_evt);
813 	mshv_acpi_cleanup_sint_irq();
814 }
815 #else /* !HYPERVISOR_CALLBACK_VECTOR */
816 static int __init mshv_sint_vector_setup(void)
817 {
818 	mshv_sint_vector = HYPERVISOR_CALLBACK_VECTOR;
819 	return 0;
820 }
821 
822 static void mshv_sint_vector_cleanup(void)
823 {
824 }
825 #endif /* HYPERVISOR_CALLBACK_VECTOR */
826 
827 int __init mshv_synic_init(struct device *dev)
828 {
829 	int ret = 0;
830 
831 	ret = mshv_sint_vector_setup();
832 	if (ret)
833 		return ret;
834 
835 	synic_pages = alloc_percpu(struct hv_synic_pages);
836 	if (!synic_pages) {
837 		dev_err(dev, "Failed to allocate percpu synic page\n");
838 		ret = -ENOMEM;
839 		goto sint_vector_cleanup;
840 	}
841 
842 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mshv_synic",
843 				mshv_synic_cpu_init,
844 				mshv_synic_cpu_exit);
845 	if (ret < 0) {
846 		dev_err(dev, "Failed to setup cpu hotplug state: %i\n", ret);
847 		goto free_synic_pages;
848 	}
849 
850 	synic_cpuhp_online = ret;
851 
852 	ret = register_reboot_notifier(&mshv_synic_reboot_nb);
853 	if (ret)
854 		goto remove_cpuhp_state;
855 
856 	return 0;
857 
858 remove_cpuhp_state:
859 	cpuhp_remove_state(synic_cpuhp_online);
860 free_synic_pages:
861 	free_percpu(synic_pages);
862 sint_vector_cleanup:
863 	mshv_sint_vector_cleanup();
864 	return ret;
865 }
866 
867 void mshv_synic_exit(void)
868 {
869 	unregister_reboot_notifier(&mshv_synic_reboot_nb);
870 	cpuhp_remove_state(synic_cpuhp_online);
871 	free_percpu(synic_pages);
872 	mshv_sint_vector_cleanup();
873 }
874