xref: /kvm-unit-tests/lib/s390x/smp.c (revision 24a8db6272b4cd77bdaf29df9454e1ab4b6a8dc1)
1f77c0515SJanosch Frank /*
2f77c0515SJanosch Frank  * s390x smp
3f77c0515SJanosch Frank  * Based on Linux's arch/s390/kernel/smp.c and
4f77c0515SJanosch Frank  * arch/s390/include/asm/sigp.h
5f77c0515SJanosch Frank  *
6f77c0515SJanosch Frank  * Copyright (c) 2019 IBM Corp
7f77c0515SJanosch Frank  *
8f77c0515SJanosch Frank  * Authors:
9f77c0515SJanosch Frank  *  Janosch Frank <frankja@linux.ibm.com>
10f77c0515SJanosch Frank  *
11f77c0515SJanosch Frank  * This code is free software; you can redistribute it and/or modify it
12f77c0515SJanosch Frank  * under the terms of the GNU General Public License version 2.
13f77c0515SJanosch Frank  */
14f77c0515SJanosch Frank #include <libcflat.h>
15f77c0515SJanosch Frank #include <asm/arch_def.h>
16f77c0515SJanosch Frank #include <asm/sigp.h>
17f77c0515SJanosch Frank #include <asm/page.h>
18f77c0515SJanosch Frank #include <asm/barrier.h>
19f77c0515SJanosch Frank #include <asm/spinlock.h>
20f77c0515SJanosch Frank #include <asm/asm-offsets.h>
21f77c0515SJanosch Frank 
22f77c0515SJanosch Frank #include <alloc.h>
23f77c0515SJanosch Frank #include <alloc_page.h>
24f77c0515SJanosch Frank 
25f77c0515SJanosch Frank #include "smp.h"
26f77c0515SJanosch Frank #include "sclp.h"
27f77c0515SJanosch Frank 
28f77c0515SJanosch Frank static char cpu_info_buffer[PAGE_SIZE] __attribute__((__aligned__(4096)));
29f77c0515SJanosch Frank static struct cpu *cpus;
30f77c0515SJanosch Frank static struct cpu *cpu0;
31f77c0515SJanosch Frank static struct spinlock lock;
32f77c0515SJanosch Frank 
33f77c0515SJanosch Frank extern void smp_cpu_setup_state(void);
34f77c0515SJanosch Frank 
35f77c0515SJanosch Frank int smp_query_num_cpus(void)
36f77c0515SJanosch Frank {
37f77c0515SJanosch Frank 	struct ReadCpuInfo *info = (void *)cpu_info_buffer;
38f77c0515SJanosch Frank 	return info->nr_configured;
39f77c0515SJanosch Frank }
40f77c0515SJanosch Frank 
41f77c0515SJanosch Frank struct cpu *smp_cpu_from_addr(uint16_t addr)
42f77c0515SJanosch Frank {
43f77c0515SJanosch Frank 	int i, num = smp_query_num_cpus();
44f77c0515SJanosch Frank 
45f77c0515SJanosch Frank 	for (i = 0; i < num; i++) {
46f77c0515SJanosch Frank 		if (cpus[i].addr == addr)
47f77c0515SJanosch Frank 			return &cpus[i];
48f77c0515SJanosch Frank 	}
49f77c0515SJanosch Frank 	return NULL;
50f77c0515SJanosch Frank }
51f77c0515SJanosch Frank 
52f77c0515SJanosch Frank bool smp_cpu_stopped(uint16_t addr)
53f77c0515SJanosch Frank {
54f77c0515SJanosch Frank 	uint32_t status;
55f77c0515SJanosch Frank 
56f77c0515SJanosch Frank 	if (sigp(addr, SIGP_SENSE, 0, &status) != SIGP_CC_STATUS_STORED)
57f77c0515SJanosch Frank 		return false;
58f77c0515SJanosch Frank 	return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));
59f77c0515SJanosch Frank }
60f77c0515SJanosch Frank 
61f77c0515SJanosch Frank bool smp_cpu_running(uint16_t addr)
62f77c0515SJanosch Frank {
63f77c0515SJanosch Frank 	if (sigp(addr, SIGP_SENSE_RUNNING, 0, NULL) != SIGP_CC_STATUS_STORED)
64f77c0515SJanosch Frank 		return true;
65f77c0515SJanosch Frank 	/* Status stored condition code is equivalent to cpu not running. */
66f77c0515SJanosch Frank 	return false;
67f77c0515SJanosch Frank }
68f77c0515SJanosch Frank 
69f77c0515SJanosch Frank static int smp_cpu_stop_nolock(uint16_t addr, bool store)
70f77c0515SJanosch Frank {
71f77c0515SJanosch Frank 	struct cpu *cpu;
72f77c0515SJanosch Frank 	uint8_t order = store ? SIGP_STOP_AND_STORE_STATUS : SIGP_STOP;
73f77c0515SJanosch Frank 
74f77c0515SJanosch Frank 	cpu = smp_cpu_from_addr(addr);
75f77c0515SJanosch Frank 	if (!cpu || cpu == cpu0)
76f77c0515SJanosch Frank 		return -1;
77f77c0515SJanosch Frank 
78f77c0515SJanosch Frank 	if (sigp_retry(addr, order, 0, NULL))
79f77c0515SJanosch Frank 		return -1;
80f77c0515SJanosch Frank 
81f77c0515SJanosch Frank 	while (!smp_cpu_stopped(addr))
82f77c0515SJanosch Frank 		mb();
83f77c0515SJanosch Frank 	cpu->active = false;
84f77c0515SJanosch Frank 	return 0;
85f77c0515SJanosch Frank }
86f77c0515SJanosch Frank 
87f77c0515SJanosch Frank int smp_cpu_stop(uint16_t addr)
88f77c0515SJanosch Frank {
89f77c0515SJanosch Frank 	int rc;
90f77c0515SJanosch Frank 
91f77c0515SJanosch Frank 	spin_lock(&lock);
92f77c0515SJanosch Frank 	rc = smp_cpu_stop_nolock(addr, false);
93f77c0515SJanosch Frank 	spin_unlock(&lock);
94f77c0515SJanosch Frank 	return rc;
95f77c0515SJanosch Frank }
96f77c0515SJanosch Frank 
97f77c0515SJanosch Frank int smp_cpu_stop_store_status(uint16_t addr)
98f77c0515SJanosch Frank {
99f77c0515SJanosch Frank 	int rc;
100f77c0515SJanosch Frank 
101f77c0515SJanosch Frank 	spin_lock(&lock);
102f77c0515SJanosch Frank 	rc = smp_cpu_stop_nolock(addr, true);
103f77c0515SJanosch Frank 	spin_unlock(&lock);
104f77c0515SJanosch Frank 	return rc;
105f77c0515SJanosch Frank }
106f77c0515SJanosch Frank 
1072ca255a0SJanosch Frank static int smp_cpu_restart_nolock(uint16_t addr, struct psw *psw)
1082ca255a0SJanosch Frank {
1092ca255a0SJanosch Frank 	int rc;
1102ca255a0SJanosch Frank 	struct cpu *cpu = smp_cpu_from_addr(addr);
1112ca255a0SJanosch Frank 
1122ca255a0SJanosch Frank 	if (!cpu)
1132ca255a0SJanosch Frank 		return -1;
1142ca255a0SJanosch Frank 	if (psw) {
1152ca255a0SJanosch Frank 		cpu->lowcore->restart_new_psw.mask = psw->mask;
1162ca255a0SJanosch Frank 		cpu->lowcore->restart_new_psw.addr = psw->addr;
1172ca255a0SJanosch Frank 	}
1182ca255a0SJanosch Frank 	/*
1192ca255a0SJanosch Frank 	 * Stop the cpu, so we don't have a race between a running cpu
1202ca255a0SJanosch Frank 	 * and the restart in the test that checks if the cpu is
1212ca255a0SJanosch Frank 	 * running after the restart.
1222ca255a0SJanosch Frank 	 */
1232ca255a0SJanosch Frank 	smp_cpu_stop_nolock(addr, false);
1242ca255a0SJanosch Frank 	rc = sigp(addr, SIGP_RESTART, 0, NULL);
1252ca255a0SJanosch Frank 	if (rc)
1262ca255a0SJanosch Frank 		return rc;
1272ca255a0SJanosch Frank 	/*
1282ca255a0SJanosch Frank 	 * The order has been accepted, but the actual restart may not
1292ca255a0SJanosch Frank 	 * have been performed yet, so wait until the cpu is running.
1302ca255a0SJanosch Frank 	 */
1312ca255a0SJanosch Frank 	while (!smp_cpu_running(addr))
1322ca255a0SJanosch Frank 		mb();
1332ca255a0SJanosch Frank 	cpu->active = true;
1342ca255a0SJanosch Frank 	return 0;
1352ca255a0SJanosch Frank }
1362ca255a0SJanosch Frank 
137f77c0515SJanosch Frank int smp_cpu_restart(uint16_t addr)
138f77c0515SJanosch Frank {
1392ca255a0SJanosch Frank 	int rc;
140f77c0515SJanosch Frank 
141f77c0515SJanosch Frank 	spin_lock(&lock);
1422ca255a0SJanosch Frank 	rc = smp_cpu_restart_nolock(addr, NULL);
143f77c0515SJanosch Frank 	spin_unlock(&lock);
144f77c0515SJanosch Frank 	return rc;
145f77c0515SJanosch Frank }
146f77c0515SJanosch Frank 
147f77c0515SJanosch Frank int smp_cpu_start(uint16_t addr, struct psw psw)
148f77c0515SJanosch Frank {
1492ca255a0SJanosch Frank 	int rc;
150f77c0515SJanosch Frank 
151f77c0515SJanosch Frank 	spin_lock(&lock);
1522ca255a0SJanosch Frank 	rc = smp_cpu_restart_nolock(addr, &psw);
153f77c0515SJanosch Frank 	spin_unlock(&lock);
154f77c0515SJanosch Frank 	return rc;
155f77c0515SJanosch Frank }
156f77c0515SJanosch Frank 
157f77c0515SJanosch Frank int smp_cpu_destroy(uint16_t addr)
158f77c0515SJanosch Frank {
159f77c0515SJanosch Frank 	struct cpu *cpu;
160f77c0515SJanosch Frank 	int rc;
161f77c0515SJanosch Frank 
162f77c0515SJanosch Frank 	spin_lock(&lock);
163f77c0515SJanosch Frank 	rc = smp_cpu_stop_nolock(addr, false);
164f77c0515SJanosch Frank 	if (!rc) {
165f77c0515SJanosch Frank 		cpu = smp_cpu_from_addr(addr);
166f77c0515SJanosch Frank 		free_pages(cpu->lowcore, 2 * PAGE_SIZE);
167f77c0515SJanosch Frank 		free_pages(cpu->stack, 4 * PAGE_SIZE);
168f77c0515SJanosch Frank 		cpu->lowcore = (void *)-1UL;
169f77c0515SJanosch Frank 		cpu->stack = (void *)-1UL;
170f77c0515SJanosch Frank 	}
171f77c0515SJanosch Frank 	spin_unlock(&lock);
172f77c0515SJanosch Frank 	return rc;
173f77c0515SJanosch Frank }
174f77c0515SJanosch Frank 
175f77c0515SJanosch Frank int smp_cpu_setup(uint16_t addr, struct psw psw)
176f77c0515SJanosch Frank {
177f77c0515SJanosch Frank 	struct lowcore *lc;
178f77c0515SJanosch Frank 	struct cpu *cpu;
179f77c0515SJanosch Frank 	int rc = -1;
180f77c0515SJanosch Frank 
181f77c0515SJanosch Frank 	spin_lock(&lock);
182f77c0515SJanosch Frank 
183f77c0515SJanosch Frank 	if (!cpus)
184f77c0515SJanosch Frank 		goto out;
185f77c0515SJanosch Frank 
186f77c0515SJanosch Frank 	cpu = smp_cpu_from_addr(addr);
187f77c0515SJanosch Frank 
188f77c0515SJanosch Frank 	if (!cpu || cpu->active)
189f77c0515SJanosch Frank 		goto out;
190f77c0515SJanosch Frank 
191f77c0515SJanosch Frank 	sigp_retry(cpu->addr, SIGP_INITIAL_CPU_RESET, 0, NULL);
192f77c0515SJanosch Frank 
193f77c0515SJanosch Frank 	lc = alloc_pages(1);
194f77c0515SJanosch Frank 	cpu->lowcore = lc;
195f77c0515SJanosch Frank 	memset(lc, 0, PAGE_SIZE * 2);
196f77c0515SJanosch Frank 	sigp_retry(cpu->addr, SIGP_SET_PREFIX, (unsigned long )lc, NULL);
197f77c0515SJanosch Frank 
198f77c0515SJanosch Frank 	/* Copy all exception psws. */
199f77c0515SJanosch Frank 	memcpy(lc, cpu0->lowcore, 512);
200f77c0515SJanosch Frank 
201f77c0515SJanosch Frank 	/* Setup stack */
202f77c0515SJanosch Frank 	cpu->stack = (uint64_t *)alloc_pages(2);
203f77c0515SJanosch Frank 
204f77c0515SJanosch Frank 	/* Start without DAT and any other mask bits. */
205f77c0515SJanosch Frank 	cpu->lowcore->sw_int_grs[14] = psw.addr;
206f77c0515SJanosch Frank 	cpu->lowcore->sw_int_grs[15] = (uint64_t)cpu->stack + (PAGE_SIZE * 4);
207f77c0515SJanosch Frank 	lc->restart_new_psw.mask = 0x0000000180000000UL;
208f77c0515SJanosch Frank 	lc->restart_new_psw.addr = (uint64_t)smp_cpu_setup_state;
209736b9295SJanosch Frank 	lc->sw_int_crs[0] = 0x0000000000040000UL;
210f77c0515SJanosch Frank 
211f77c0515SJanosch Frank 	/* Start processing */
2122ca255a0SJanosch Frank 	smp_cpu_restart_nolock(addr, NULL);
213*24a8db62SJanosch Frank 	/* Wait until the cpu has finished setup and started the provided psw */
214*24a8db62SJanosch Frank 	while (lc->restart_new_psw.addr != psw.addr)
215*24a8db62SJanosch Frank 		mb();
216f77c0515SJanosch Frank out:
217f77c0515SJanosch Frank 	spin_unlock(&lock);
218f77c0515SJanosch Frank 	return rc;
219f77c0515SJanosch Frank }
220f77c0515SJanosch Frank 
221f77c0515SJanosch Frank /*
222f77c0515SJanosch Frank  * Disregarding state, stop all cpus that once were online except for
223f77c0515SJanosch Frank  * calling cpu.
224f77c0515SJanosch Frank  */
225f77c0515SJanosch Frank void smp_teardown(void)
226f77c0515SJanosch Frank {
227f77c0515SJanosch Frank 	int i = 0;
228f77c0515SJanosch Frank 	uint16_t this_cpu = stap();
229f77c0515SJanosch Frank 	struct ReadCpuInfo *info = (void *)cpu_info_buffer;
230f77c0515SJanosch Frank 
231f77c0515SJanosch Frank 	spin_lock(&lock);
232f77c0515SJanosch Frank 	for (; i < info->nr_configured; i++) {
233f77c0515SJanosch Frank 		if (cpus[i].active &&
234f77c0515SJanosch Frank 		    cpus[i].addr != this_cpu) {
235f77c0515SJanosch Frank 			sigp_retry(cpus[i].addr, SIGP_STOP, 0, NULL);
236f77c0515SJanosch Frank 		}
237f77c0515SJanosch Frank 	}
238f77c0515SJanosch Frank 	spin_unlock(&lock);
239f77c0515SJanosch Frank }
240f77c0515SJanosch Frank 
241f77c0515SJanosch Frank /*Expected to be called from boot cpu */
242f77c0515SJanosch Frank extern uint64_t *stackptr;
243f77c0515SJanosch Frank void smp_setup(void)
244f77c0515SJanosch Frank {
245f77c0515SJanosch Frank 	int i = 0;
246f77c0515SJanosch Frank 	unsigned short cpu0_addr = stap();
247f77c0515SJanosch Frank 	struct ReadCpuInfo *info = (void *)cpu_info_buffer;
248f77c0515SJanosch Frank 
249f77c0515SJanosch Frank 	spin_lock(&lock);
250f77c0515SJanosch Frank 	sclp_mark_busy();
251f77c0515SJanosch Frank 	info->h.length = PAGE_SIZE;
252f77c0515SJanosch Frank 	sclp_service_call(SCLP_READ_CPU_INFO, cpu_info_buffer);
253f77c0515SJanosch Frank 
254f77c0515SJanosch Frank 	if (smp_query_num_cpus() > 1)
255f77c0515SJanosch Frank 		printf("SMP: Initializing, found %d cpus\n", info->nr_configured);
256f77c0515SJanosch Frank 
257f77c0515SJanosch Frank 	cpus = calloc(info->nr_configured, sizeof(cpus));
258f77c0515SJanosch Frank 	for (i = 0; i < info->nr_configured; i++) {
259f77c0515SJanosch Frank 		cpus[i].addr = info->entries[i].address;
260f77c0515SJanosch Frank 		cpus[i].active = false;
261f77c0515SJanosch Frank 		if (info->entries[i].address == cpu0_addr) {
262f77c0515SJanosch Frank 			cpu0 = &cpus[i];
263f77c0515SJanosch Frank 			cpu0->stack = stackptr;
264f77c0515SJanosch Frank 			cpu0->lowcore = (void *)0;
265f77c0515SJanosch Frank 			cpu0->active = true;
266f77c0515SJanosch Frank 		}
267f77c0515SJanosch Frank 	}
268f77c0515SJanosch Frank 	spin_unlock(&lock);
269f77c0515SJanosch Frank }
270