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