| /* SPDX-License-Identifier: GPL-2.0-only */ | 
 | /* | 
 |  * s390x smp | 
 |  * Based on Linux's arch/s390/kernel/smp.c and | 
 |  * arch/s390/include/asm/sigp.h | 
 |  * | 
 |  * Copyright (c) 2019 IBM Corp | 
 |  * | 
 |  * Authors: | 
 |  *  Janosch Frank <frankja@linux.ibm.com> | 
 |  */ | 
 | #include <libcflat.h> | 
 | #include <bitops.h> | 
 | #include <asm/arch_def.h> | 
 | #include <asm/sigp.h> | 
 | #include <asm/page.h> | 
 | #include <asm/barrier.h> | 
 | #include <asm/spinlock.h> | 
 | #include <asm/asm-offsets.h> | 
 |  | 
 | #include <alloc.h> | 
 | #include <alloc_page.h> | 
 |  | 
 | #include "smp.h" | 
 | #include "sclp.h" | 
 |  | 
 | static struct cpu *cpus; | 
 | static struct spinlock lock; | 
 |  | 
 | extern void smp_cpu_setup_state(void); | 
 |  | 
 | static void check_idx(uint16_t idx) | 
 | { | 
 | 	assert(idx < smp_query_num_cpus()); | 
 | } | 
 |  | 
 | int smp_query_num_cpus(void) | 
 | { | 
 | 	return sclp_get_cpu_num(); | 
 | } | 
 |  | 
 | int smp_sigp(uint16_t idx, uint8_t order, unsigned long parm, uint32_t *status) | 
 | { | 
 | 	check_idx(idx); | 
 | 	return sigp_retry(cpus[idx].addr, order, parm, status); | 
 | } | 
 |  | 
 | struct cpu *smp_cpu_from_addr(uint16_t addr) | 
 | { | 
 | 	int i, num = smp_query_num_cpus(); | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		if (cpus[i].addr == addr) | 
 | 			return &cpus[i]; | 
 | 	} | 
 | 	return NULL; | 
 | } | 
 |  | 
 | struct cpu *smp_cpu_from_idx(uint16_t idx) | 
 | { | 
 | 	check_idx(idx); | 
 | 	return &cpus[idx]; | 
 | } | 
 |  | 
 | uint16_t smp_cpu_addr(uint16_t idx) | 
 | { | 
 | 	check_idx(idx); | 
 | 	return cpus[idx].addr; | 
 | } | 
 |  | 
 | bool smp_cpu_stopped(uint16_t idx) | 
 | { | 
 | 	uint32_t status; | 
 |  | 
 | 	if (smp_sigp(idx, SIGP_SENSE, 0, &status) != SIGP_CC_STATUS_STORED) | 
 | 		return false; | 
 | 	return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED)); | 
 | } | 
 |  | 
 | bool smp_sense_running_status(uint16_t idx) | 
 | { | 
 | 	if (smp_sigp(idx, SIGP_SENSE_RUNNING, 0, NULL) != SIGP_CC_STATUS_STORED) | 
 | 		return true; | 
 | 	/* Status stored condition code is equivalent to cpu not running. */ | 
 | 	return false; | 
 | } | 
 |  | 
 | static int smp_cpu_stop_nolock(uint16_t idx, bool store) | 
 | { | 
 | 	uint8_t order = store ? SIGP_STOP_AND_STORE_STATUS : SIGP_STOP; | 
 |  | 
 | 	/* refuse to work on the boot CPU */ | 
 | 	if (idx == 0) | 
 | 		return -1; | 
 |  | 
 | 	if (smp_sigp(idx, order, 0, NULL)) | 
 | 		return -1; | 
 |  | 
 | 	while (!smp_cpu_stopped(idx)) | 
 | 		mb(); | 
 | 	/* idx has been already checked by the smp_* functions called above */ | 
 | 	cpus[idx].active = false; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int smp_cpu_stop(uint16_t idx) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	rc = smp_cpu_stop_nolock(idx, false); | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* | 
 |  * Functionally equivalent to smp_cpu_stop(), but without the | 
 |  * elements that wait/serialize matters itself. | 
 |  * Used to see if KVM itself is serialized correctly. | 
 |  */ | 
 | int smp_cpu_stop_nowait(uint16_t idx) | 
 | { | 
 | 	check_idx(idx); | 
 |  | 
 | 	/* refuse to work on the boot CPU */ | 
 | 	if (idx == 0) | 
 | 		return -1; | 
 |  | 
 | 	spin_lock(&lock); | 
 |  | 
 | 	/* Don't suppress a CC2 with sigp_retry() */ | 
 | 	if (sigp(cpus[idx].addr, SIGP_STOP, 0, NULL)) { | 
 | 		spin_unlock(&lock); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	cpus[idx].active = false; | 
 | 	spin_unlock(&lock); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int smp_cpu_stop_store_status(uint16_t idx) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	rc = smp_cpu_stop_nolock(idx, true); | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int smp_cpu_restart_nolock(uint16_t idx, struct psw *psw) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	check_idx(idx); | 
 | 	if (psw) { | 
 | 		cpus[idx].lowcore->restart_new_psw.mask = psw->mask; | 
 | 		cpus[idx].lowcore->restart_new_psw.addr = psw->addr; | 
 | 	} | 
 | 	/* | 
 | 	 * Stop the cpu, so we don't have a race between a running cpu | 
 | 	 * and the restart in the test that checks if the cpu is | 
 | 	 * running after the restart. | 
 | 	 */ | 
 | 	smp_cpu_stop_nolock(idx, false); | 
 | 	rc = smp_sigp(idx, SIGP_RESTART, 0, NULL); | 
 | 	if (rc) | 
 | 		return rc; | 
 | 	/* | 
 | 	 * The order has been accepted, but the actual restart may not | 
 | 	 * have been performed yet, so wait until the cpu is running. | 
 | 	 */ | 
 | 	while (smp_cpu_stopped(idx)) | 
 | 		mb(); | 
 | 	cpus[idx].active = true; | 
 | 	return 0; | 
 | } | 
 |  | 
 | int smp_cpu_restart(uint16_t idx) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	rc = smp_cpu_restart_nolock(idx, NULL); | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* | 
 |  * Functionally equivalent to smp_cpu_restart(), but without the | 
 |  * elements that wait/serialize matters here in the test. | 
 |  * Used to see if KVM itself is serialized correctly. | 
 |  */ | 
 | int smp_cpu_restart_nowait(uint16_t idx) | 
 | { | 
 | 	check_idx(idx); | 
 |  | 
 | 	spin_lock(&lock); | 
 |  | 
 | 	/* Don't suppress a CC2 with sigp_retry() */ | 
 | 	if (sigp(cpus[idx].addr, SIGP_RESTART, 0, NULL)) { | 
 | 		spin_unlock(&lock); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	cpus[idx].active = true; | 
 |  | 
 | 	spin_unlock(&lock); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int smp_cpu_start(uint16_t idx, struct psw psw) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	rc = smp_cpu_restart_nolock(idx, &psw); | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | int smp_cpu_destroy(uint16_t idx) | 
 | { | 
 | 	int rc; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	rc = smp_cpu_stop_nolock(idx, false); | 
 | 	if (!rc) { | 
 | 		free_pages(cpus[idx].lowcore); | 
 | 		free_pages(cpus[idx].stack); | 
 | 		cpus[idx].lowcore = (void *)-1UL; | 
 | 		cpus[idx].stack = (void *)-1UL; | 
 | 	} | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int smp_cpu_setup_nolock(uint16_t idx, struct psw psw) | 
 | { | 
 | 	struct lowcore *lc; | 
 |  | 
 | 	if (cpus[idx].active) | 
 | 		return -1; | 
 |  | 
 | 	smp_sigp(idx, SIGP_INITIAL_CPU_RESET, 0, NULL); | 
 |  | 
 | 	lc = alloc_pages_flags(1, AREA_DMA31); | 
 | 	cpus[idx].lowcore = lc; | 
 | 	smp_sigp(idx, SIGP_SET_PREFIX, (unsigned long )lc, NULL); | 
 |  | 
 | 	/* Copy all exception psws. */ | 
 | 	memcpy(lc, cpus[0].lowcore, 512); | 
 |  | 
 | 	/* Setup stack */ | 
 | 	cpus[idx].stack = (uint64_t *)alloc_pages(2); | 
 |  | 
 | 	/* Start without DAT and any other mask bits. */ | 
 | 	lc->sw_int_psw.mask = psw.mask; | 
 | 	lc->sw_int_psw.addr = psw.addr; | 
 | 	lc->sw_int_grs[14] = psw.addr; | 
 | 	lc->sw_int_grs[15] = (uint64_t)cpus[idx].stack + (PAGE_SIZE * 4); | 
 | 	lc->restart_new_psw.mask = PSW_MASK_64; | 
 | 	lc->restart_new_psw.addr = (uint64_t)smp_cpu_setup_state; | 
 | 	lc->sw_int_crs[0] = BIT_ULL(CTL0_AFP); | 
 |  | 
 | 	/* Start processing */ | 
 | 	smp_cpu_restart_nolock(idx, NULL); | 
 | 	/* Wait until the cpu has finished setup and started the provided psw */ | 
 | 	while (lc->restart_new_psw.addr != psw.addr) | 
 | 		mb(); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int smp_cpu_setup(uint16_t idx, struct psw psw) | 
 | { | 
 | 	int rc = -1; | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	if (cpus) { | 
 | 		check_idx(idx); | 
 | 		rc = smp_cpu_setup_nolock(idx, psw); | 
 | 	} | 
 | 	spin_unlock(&lock); | 
 | 	return rc; | 
 | } | 
 |  | 
 | /* | 
 |  * Disregarding state, stop all cpus that once were online except for | 
 |  * calling cpu. | 
 |  */ | 
 | void smp_teardown(void) | 
 | { | 
 | 	int i = 0; | 
 | 	uint16_t this_cpu = stap(); | 
 | 	int num = smp_query_num_cpus(); | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	for (; i < num; i++) { | 
 | 		if (cpus[i].active && | 
 | 		    cpus[i].addr != this_cpu) { | 
 | 			sigp_retry(cpus[i].addr, SIGP_STOP, 0, NULL); | 
 | 		} | 
 | 	} | 
 | 	spin_unlock(&lock); | 
 | } | 
 |  | 
 | /*Expected to be called from boot cpu */ | 
 | extern uint64_t *stackptr; | 
 | void smp_setup(void) | 
 | { | 
 | 	int i = 0; | 
 | 	int num = smp_query_num_cpus(); | 
 | 	unsigned short cpu0_addr = stap(); | 
 | 	struct CPUEntry *entry = sclp_get_cpu_entries(); | 
 |  | 
 | 	spin_lock(&lock); | 
 | 	if (num > 1) | 
 | 		printf("SMP: Initializing, found %d cpus\n", num); | 
 |  | 
 | 	cpus = calloc(num, sizeof(*cpus)); | 
 | 	for (i = 0; i < num; i++) { | 
 | 		cpus[i].addr = entry[i].address; | 
 | 		cpus[i].active = false; | 
 | 		/* | 
 | 		 * Fill in the boot CPU. If the boot CPU is not at index 0, | 
 | 		 * swap it with the one at index 0. This guarantees that the | 
 | 		 * boot CPU will always have index 0. If the boot CPU was | 
 | 		 * already at index 0, a few extra useless assignments are | 
 | 		 * performed, but everything will work ok. | 
 | 		 * Notice that there is no guarantee that the list of CPUs | 
 | 		 * returned by the Read SCP Info command is in any | 
 | 		 * particular order, or that its order will stay consistent | 
 | 		 * across multiple invocations. | 
 | 		 */ | 
 | 		if (entry[i].address == cpu0_addr) { | 
 | 			cpus[i].addr = cpus[0].addr; | 
 | 			cpus[0].addr = cpu0_addr; | 
 | 			cpus[0].stack = stackptr; | 
 | 			cpus[0].lowcore = (void *)0; | 
 | 			cpus[0].active = true; | 
 | 		} | 
 | 	} | 
 | 	spin_unlock(&lock); | 
 | } |