xref: /aosp_15_r20/external/coreboot/src/cpu/x86/lapic/lapic_cpu_stop.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <console/console.h>
4 #include <cpu/x86/lapic.h>
5 #include <delay.h>
6 #include <halt.h>
7 #include <stdint.h>
8 
9 /**
10  * Sending INIT IPI to self is equivalent of asserting #INIT with a bit of
11  * delay.
12  * An undefined number of instruction cycles will complete. All global locks
13  * must be released before INIT IPI and no printk is allowed after this.
14  * De-asserting INIT IPI is a no-op on later Intel CPUs.
15  *
16  * If you set DEBUG_HALT_SELF to 1, printk's after INIT IPI are enabled
17  * but running thread may halt without releasing the lock and effectively
18  * deadlock other CPUs.
19  */
20 #define DEBUG_HALT_SELF 0
21 
22 #if DEBUG_HALT_SELF
23 #define dprintk(LEVEL, args...) do { printk(LEVEL, ##args); } while (0)
24 #else
25 #define dprintk(LEVEL, args...) do { } while (0)
26 #endif
27 
wait_for_ipi_completion_without_printk(const int timeout_ms)28 static void wait_for_ipi_completion_without_printk(const int timeout_ms)
29 {
30 	int loops = timeout_ms * 10;
31 	uint32_t send_status;
32 
33 	/* wait for the ipi send to finish */
34 	dprintk(BIOS_SPEW, "Waiting for send to finish...\n");
35 	do {
36 		dprintk(BIOS_SPEW, "+");
37 		udelay(100);
38 		send_status = lapic_busy();
39 	} while (send_status && (--loops > 0));
40 
41 	if (send_status)
42 		dprintk(BIOS_ERR, "timed out\n");
43 }
44 
45 /**
46  * Normally this function is defined in lapic.h as an always inline function
47  * that just keeps the CPU in a hlt() loop. This does not work on all CPUs.
48  * I think all hyperthreading CPUs might need this version, but I could only
49  * verify this on the Intel Core Duo
50  */
stop_this_cpu(void)51 void stop_this_cpu(void)
52 {
53 	const int timeout_100ms = 100;
54 	unsigned long id = lapicid();
55 
56 	printk(BIOS_DEBUG, "CPU %ld going down...\n", id);
57 
58 	/* send an LAPIC INIT to myself */
59 	lapic_send_ipi_self(LAPIC_INT_LEVELTRIG | LAPIC_INT_ASSERT | LAPIC_DM_INIT);
60 	wait_for_ipi_completion_without_printk(timeout_100ms);
61 
62 	mdelay(10);
63 
64 	dprintk(BIOS_SPEW, "Deasserting INIT.\n");
65 
66 	/* Deassert the LAPIC INIT */
67 	lapic_send_ipi_self(LAPIC_INT_LEVELTRIG | LAPIC_DM_INIT);
68 	wait_for_ipi_completion_without_printk(timeout_100ms);
69 
70 	halt();
71 }
72