/ src / arch / riscv / smp.c
smp.c
 1  /* SPDX-License-Identifier: GPL-2.0-only */
 2  
 3  #include <arch/barrier.h>
 4  #include <arch/encoding.h>
 5  #include <arch/smp/smp.h>
 6  #include <arch/smp/atomic.h>
 7  #include <console/console.h>
 8  #include <mcall.h>
 9  
10  // made up value to sync hart state
11  #define HART_SLEEPING 0x1
12  #define HART_AWAKE    0x2
13  
14  void smp_pause(int working_hartid)
15  {
16  	int hartid = read_csr(mhartid);
17  
18  	// pause all harts which are not the working hart
19  	if (hartid != working_hartid) {
20  		clear_csr(mstatus, MSTATUS_MIE); // disable all interrupts
21  		set_msip(hartid, 0); // clear pending interrupts
22  		write_csr(mie, MIP_MSIP); // enable only IPI (for smp_resume)
23  		barrier();
24  		atomic_set(&HLS()->entry.sync_a, HART_SLEEPING); // mark the hart as sleeping.
25  
26  		// pause hart
27  		do {
28  			__asm__ volatile ("wfi"); // wait for interrupt
29  		} while ((read_csr(mip) & MIP_MSIP) == 0);
30  
31  		atomic_set(&HLS()->entry.sync_a, HART_AWAKE); // mark the hart as awake
32  		HLS()->entry.fn(HLS()->entry.arg);
33  	}
34  }
35  
36  // must only be called by the WORKING_HARTID
37  void smp_resume(void (*fn)(void *), void *arg)
38  {
39  	if (fn == NULL) {
40  		printk(BIOS_ERR, "must pass a non-null function pointer\n");
41  		return; // we can still boot with one hart
42  	}
43  
44  	int working_hartid = read_csr(mhartid);
45  
46  	int hart_count = CONFIG_MAX_CPUS;
47  	if (CONFIG(RISCV_GET_HART_COUNT_AT_RUNTIME))
48  		hart_count = smp_get_hart_count();
49  
50  	// check that all harts are present
51  
52  	u32 count_awake_harts = 0;
53  	for (int i = 0; i < hart_count; i++) {
54  		// The working hart never sleeps. It is a hard working hart.
55  		if (i == working_hartid)
56  			continue;
57  
58  		if (atomic_read(&OTHER_HLS(i)->entry.sync_a) != HART_SLEEPING) {
59  			/*
60  			 * we assmue here that the time between smp_pause and smp_resume
61  			 * is enough for all harts to reach the smp_pause state.
62  			 * But for some reason that was not the case for this hart ...
63  			 */
64  			printk(BIOS_ERR, "hart %d did not enter smp_pause\n", i);
65  			OTHER_HLS(i)->enabled = 0; // disable hart
66  		} else {
67  			// hart is in wfi (wait for interrupt) state like it should be.
68  
69  			OTHER_HLS(i)->entry.fn = fn;
70  			OTHER_HLS(i)->entry.arg = arg;
71  			barrier();
72  			set_msip(i, 1); // wake up hart
73  		}
74  	}
75  
76  	printk(BIOS_DEBUG, "waiting for all harts to wake up...\n");
77  	// confirm that all harts are wake
78  	for (int i = 0; i < hart_count; i++) {
79  		// The working hart never sleeps. It is a hard working hart.
80  		if (i == working_hartid || !OTHER_HLS(i)->enabled)
81  			continue;
82  
83  		// wait for hart to publish its waking state
84  		while (atomic_read(&OTHER_HLS(i)->entry.sync_a) != HART_AWAKE)
85  			;
86  		count_awake_harts++;
87  	}
88  	printk(BIOS_DEBUG, "all harts up and running...\n");
89  
90  	if ((hart_count - 1) != count_awake_harts) { // exclude working hart
91  		/*
92  		 * Apparently one or more harts did not reach smp_pause before smp_resume has
93  		 * been called by the working hart. That should not happen and may indicate we
94  		 * need a timeout of sorts to make sure we get all harts resumed.
95  		 */
96  		printk(BIOS_ERR, "some harts were too slow and could not resume\n");
97  	}
98  	fn(arg); // jump to fn with working hart
99  }