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