xref: /kvm-unit-tests/s390x/firq.c (revision 8b98745defc547d3c3c07c8835249dfd9587fb5b)
1*8b98745dSDavid Hildenbrand /* SPDX-License-Identifier: GPL-2.0-only */
2*8b98745dSDavid Hildenbrand /*
3*8b98745dSDavid Hildenbrand  * Floating interrupt tests.
4*8b98745dSDavid Hildenbrand  *
5*8b98745dSDavid Hildenbrand  * Copyright 2021 Red Hat Inc
6*8b98745dSDavid Hildenbrand  *
7*8b98745dSDavid Hildenbrand  * Authors:
8*8b98745dSDavid Hildenbrand  *    David Hildenbrand <david@redhat.com>
9*8b98745dSDavid Hildenbrand  */
10*8b98745dSDavid Hildenbrand #include <libcflat.h>
11*8b98745dSDavid Hildenbrand #include <asm/asm-offsets.h>
12*8b98745dSDavid Hildenbrand #include <asm/interrupt.h>
13*8b98745dSDavid Hildenbrand #include <asm/page.h>
14*8b98745dSDavid Hildenbrand #include <asm-generic/barrier.h>
15*8b98745dSDavid Hildenbrand 
16*8b98745dSDavid Hildenbrand #include <sclp.h>
17*8b98745dSDavid Hildenbrand #include <smp.h>
18*8b98745dSDavid Hildenbrand #include <alloc_page.h>
19*8b98745dSDavid Hildenbrand 
20*8b98745dSDavid Hildenbrand static void wait_for_sclp_int(void)
21*8b98745dSDavid Hildenbrand {
22*8b98745dSDavid Hildenbrand 	/* Enable SCLP interrupts on this CPU only. */
23*8b98745dSDavid Hildenbrand 	ctl_set_bit(0, CTL0_SERVICE_SIGNAL);
24*8b98745dSDavid Hildenbrand 
25*8b98745dSDavid Hildenbrand 	/* Enable external interrupts and go to the wait state. */
26*8b98745dSDavid Hildenbrand 	wait_for_interrupt(PSW_MASK_EXT);
27*8b98745dSDavid Hildenbrand }
28*8b98745dSDavid Hildenbrand 
29*8b98745dSDavid Hildenbrand /*
30*8b98745dSDavid Hildenbrand  * Some KVM versions might mix CPUs when looking for a floating IRQ target,
31*8b98745dSDavid Hildenbrand  * accidentially detecting a stopped CPU as waiting and resulting in the actually
32*8b98745dSDavid Hildenbrand  * waiting CPU not getting woken up for the interrupt.
33*8b98745dSDavid Hildenbrand  */
34*8b98745dSDavid Hildenbrand static void test_wait_state_delivery(void)
35*8b98745dSDavid Hildenbrand {
36*8b98745dSDavid Hildenbrand 	struct psw psw;
37*8b98745dSDavid Hildenbrand 	SCCBHeader *h;
38*8b98745dSDavid Hildenbrand 	int ret;
39*8b98745dSDavid Hildenbrand 
40*8b98745dSDavid Hildenbrand 	report_prefix_push("wait state delivery");
41*8b98745dSDavid Hildenbrand 
42*8b98745dSDavid Hildenbrand 	if (smp_query_num_cpus() < 3) {
43*8b98745dSDavid Hildenbrand 		report_skip("need at least 3 CPUs for this test");
44*8b98745dSDavid Hildenbrand 		goto out;
45*8b98745dSDavid Hildenbrand 	}
46*8b98745dSDavid Hildenbrand 
47*8b98745dSDavid Hildenbrand 	if (stap()) {
48*8b98745dSDavid Hildenbrand 		report_skip("need to start on CPU #0");
49*8b98745dSDavid Hildenbrand 		goto out;
50*8b98745dSDavid Hildenbrand 	}
51*8b98745dSDavid Hildenbrand 
52*8b98745dSDavid Hildenbrand 	/*
53*8b98745dSDavid Hildenbrand 	 * We want CPU #2 to be stopped. This should be the case at this
54*8b98745dSDavid Hildenbrand 	 * point, however, we want to sense if it even exists as well.
55*8b98745dSDavid Hildenbrand 	 */
56*8b98745dSDavid Hildenbrand 	ret = smp_cpu_stop(2);
57*8b98745dSDavid Hildenbrand 	if (ret) {
58*8b98745dSDavid Hildenbrand 		report_skip("CPU #2 not found");
59*8b98745dSDavid Hildenbrand 		goto out;
60*8b98745dSDavid Hildenbrand 	}
61*8b98745dSDavid Hildenbrand 
62*8b98745dSDavid Hildenbrand 	/*
63*8b98745dSDavid Hildenbrand 	 * We're going to perform an SCLP service call but expect
64*8b98745dSDavid Hildenbrand 	 * the interrupt on CPU #1 while it is in the wait state.
65*8b98745dSDavid Hildenbrand 	 */
66*8b98745dSDavid Hildenbrand 	sclp_mark_busy();
67*8b98745dSDavid Hildenbrand 
68*8b98745dSDavid Hildenbrand 	/* Start CPU #1 and let it wait for the interrupt. */
69*8b98745dSDavid Hildenbrand 	psw.mask = extract_psw_mask();
70*8b98745dSDavid Hildenbrand 	psw.addr = (unsigned long)wait_for_sclp_int;
71*8b98745dSDavid Hildenbrand 	ret = smp_cpu_setup(1, psw);
72*8b98745dSDavid Hildenbrand 	if (ret) {
73*8b98745dSDavid Hildenbrand 		sclp_clear_busy();
74*8b98745dSDavid Hildenbrand 		report_skip("cpu #1 not found");
75*8b98745dSDavid Hildenbrand 		goto out;
76*8b98745dSDavid Hildenbrand 	}
77*8b98745dSDavid Hildenbrand 
78*8b98745dSDavid Hildenbrand 	/*
79*8b98745dSDavid Hildenbrand 	 * We'd have to jump trough some hoops to sense e.g., via SIGP
80*8b98745dSDavid Hildenbrand 	 * CONDITIONAL EMERGENCY SIGNAL if CPU #1 is already in the
81*8b98745dSDavid Hildenbrand 	 * wait state.
82*8b98745dSDavid Hildenbrand 	 *
83*8b98745dSDavid Hildenbrand 	 * Although not completely reliable, use SIGP SENSE RUNNING STATUS
84*8b98745dSDavid Hildenbrand 	 * until not reported as running -- after all, our SCLP processing
85*8b98745dSDavid Hildenbrand 	 * will take some time as well and smp_cpu_setup() returns when we're
86*8b98745dSDavid Hildenbrand 	 * either already in wait_for_sclp_int() or just about to execute it.
87*8b98745dSDavid Hildenbrand 	 */
88*8b98745dSDavid Hildenbrand 	while(smp_sense_running_status(1));
89*8b98745dSDavid Hildenbrand 
90*8b98745dSDavid Hildenbrand 	h = alloc_page();
91*8b98745dSDavid Hildenbrand 	h->length = 4096;
92*8b98745dSDavid Hildenbrand 	ret = servc(SCLP_CMDW_READ_CPU_INFO, __pa(h));
93*8b98745dSDavid Hildenbrand 	if (ret) {
94*8b98745dSDavid Hildenbrand 		sclp_clear_busy();
95*8b98745dSDavid Hildenbrand 		report_fail("SCLP_CMDW_READ_CPU_INFO failed");
96*8b98745dSDavid Hildenbrand 		goto out_destroy;
97*8b98745dSDavid Hildenbrand 	}
98*8b98745dSDavid Hildenbrand 
99*8b98745dSDavid Hildenbrand 	/*
100*8b98745dSDavid Hildenbrand 	 * Wait until the interrupt gets delivered on CPU #1, marking the
101*8b98745dSDavid Hildenbrand 	 * SCLP requests as done.
102*8b98745dSDavid Hildenbrand 	 */
103*8b98745dSDavid Hildenbrand 	sclp_wait_busy();
104*8b98745dSDavid Hildenbrand 
105*8b98745dSDavid Hildenbrand 	report(true, "sclp interrupt delivered");
106*8b98745dSDavid Hildenbrand 
107*8b98745dSDavid Hildenbrand out_destroy:
108*8b98745dSDavid Hildenbrand 	free_page(h);
109*8b98745dSDavid Hildenbrand 	smp_cpu_destroy(1);
110*8b98745dSDavid Hildenbrand out:
111*8b98745dSDavid Hildenbrand 	report_prefix_pop();
112*8b98745dSDavid Hildenbrand }
113*8b98745dSDavid Hildenbrand 
114*8b98745dSDavid Hildenbrand int main(void)
115*8b98745dSDavid Hildenbrand {
116*8b98745dSDavid Hildenbrand 	report_prefix_push("firq");
117*8b98745dSDavid Hildenbrand 
118*8b98745dSDavid Hildenbrand 	test_wait_state_delivery();
119*8b98745dSDavid Hildenbrand 
120*8b98745dSDavid Hildenbrand 	report_prefix_pop();
121*8b98745dSDavid Hildenbrand 	return report_summary();
122*8b98745dSDavid Hildenbrand }
123