16c9f99dfSJanosch Frank /* SPDX-License-Identifier: GPL-2.0-only */ 23db880b6SDavid Hildenbrand /* 33db880b6SDavid Hildenbrand * s390x SCLP driver 43db880b6SDavid Hildenbrand * 53db880b6SDavid Hildenbrand * Copyright (c) 2017 Red Hat Inc 63db880b6SDavid Hildenbrand * 73db880b6SDavid Hildenbrand * Authors: 83db880b6SDavid Hildenbrand * David Hildenbrand <david@redhat.com> 93db880b6SDavid Hildenbrand */ 103db880b6SDavid Hildenbrand 113db880b6SDavid Hildenbrand #include <libcflat.h> 123db880b6SDavid Hildenbrand #include <asm/page.h> 133db880b6SDavid Hildenbrand #include <asm/arch_def.h> 143db880b6SDavid Hildenbrand #include <asm/interrupt.h> 158ead801eSJanosch Frank #include <asm/barrier.h> 168ead801eSJanosch Frank #include <asm/spinlock.h> 173db880b6SDavid Hildenbrand #include "sclp.h" 1892bbd322SDavid Hildenbrand #include <alloc_phys.h> 19b356c913SJanosch Frank #include <alloc_page.h> 203d49e2acSPierre Morel #include <asm/facility.h> 2192bbd322SDavid Hildenbrand 2292bbd322SDavid Hildenbrand extern unsigned long stacktop; 233db880b6SDavid Hildenbrand 243db880b6SDavid Hildenbrand static uint64_t storage_increment_size; 253db880b6SDavid Hildenbrand static uint64_t max_ram_size; 263db880b6SDavid Hildenbrand static uint64_t ram_size; 273d49e2acSPierre Morel char _read_info[2 * PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); 2852076a63SJanosch Frank static ReadInfo *read_info; 296dff7c9aSJanosch Frank struct sclp_facilities sclp_facilities; 303db880b6SDavid Hildenbrand 3135bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096))); 328ead801eSJanosch Frank static volatile bool sclp_busy; 338ead801eSJanosch Frank static struct spinlock sclp_lock; 3435bfd1c4SJanosch Frank 3592bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end) 3692bbd322SDavid Hildenbrand { 3792bbd322SDavid Hildenbrand phys_addr_t freemem_start = (phys_addr_t)&stacktop; 38b356c913SJanosch Frank phys_addr_t base, top; 3992bbd322SDavid Hildenbrand 4092bbd322SDavid Hildenbrand phys_alloc_init(freemem_start, mem_end - freemem_start); 41b356c913SJanosch Frank phys_alloc_get_unused(&base, &top); 428131e91aSClaudio Imbrenda base = PAGE_ALIGN(base) >> PAGE_SHIFT; 438131e91aSClaudio Imbrenda top = top >> PAGE_SHIFT; 44b356c913SJanosch Frank 45b356c913SJanosch Frank /* Make the pages available to the physical allocator */ 468131e91aSClaudio Imbrenda page_alloc_init_area(AREA_ANY_NUMBER, base, top); 47b356c913SJanosch Frank page_alloc_ops_enable(); 4892bbd322SDavid Hildenbrand } 4992bbd322SDavid Hildenbrand 50fd6fc0d9SClaudio Imbrenda void sclp_setup_int(void) 518ead801eSJanosch Frank { 521b2c0437SClaudio Imbrenda ctl_set_bit(0, CTL0_SERVICE_SIGNAL); 53086985a3SClaudio Imbrenda psw_mask_set_bits(PSW_MASK_EXT); 548ead801eSJanosch Frank } 558ead801eSJanosch Frank 568ead801eSJanosch Frank void sclp_handle_ext(void) 578ead801eSJanosch Frank { 581b2c0437SClaudio Imbrenda ctl_clear_bit(0, CTL0_SERVICE_SIGNAL); 598b98745dSDavid Hildenbrand sclp_clear_busy(); 608ead801eSJanosch Frank } 618ead801eSJanosch Frank 628ead801eSJanosch Frank void sclp_wait_busy(void) 638ead801eSJanosch Frank { 648ead801eSJanosch Frank while (sclp_busy) 658ead801eSJanosch Frank mb(); 668ead801eSJanosch Frank } 678ead801eSJanosch Frank 688ead801eSJanosch Frank void sclp_mark_busy(void) 698ead801eSJanosch Frank { 708ead801eSJanosch Frank /* 718ead801eSJanosch Frank * With multiple CPUs we might need to wait for another CPU's 728ead801eSJanosch Frank * request before grabbing the busy indication. 738ead801eSJanosch Frank */ 748ead801eSJanosch Frank while (true) { 758ead801eSJanosch Frank sclp_wait_busy(); 768ead801eSJanosch Frank spin_lock(&sclp_lock); 778ead801eSJanosch Frank if (!sclp_busy) { 788ead801eSJanosch Frank sclp_busy = true; 798ead801eSJanosch Frank spin_unlock(&sclp_lock); 808ead801eSJanosch Frank return; 818ead801eSJanosch Frank } 828ead801eSJanosch Frank spin_unlock(&sclp_lock); 838ead801eSJanosch Frank } 848ead801eSJanosch Frank } 858ead801eSJanosch Frank 868b98745dSDavid Hildenbrand void sclp_clear_busy(void) 878b98745dSDavid Hildenbrand { 888b98745dSDavid Hildenbrand spin_lock(&sclp_lock); 898b98745dSDavid Hildenbrand sclp_busy = false; 908b98745dSDavid Hildenbrand spin_unlock(&sclp_lock); 918b98745dSDavid Hildenbrand } 928b98745dSDavid Hildenbrand 93a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length) 94a45cb177SDavid Hildenbrand { 95a45cb177SDavid Hildenbrand unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, 96a45cb177SDavid Hildenbrand SCLP_CMDW_READ_SCP_INFO }; 978ead801eSJanosch Frank int i, cc; 98a45cb177SDavid Hildenbrand 99a45cb177SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(commands); i++) { 1008ead801eSJanosch Frank sclp_mark_busy(); 101a45cb177SDavid Hildenbrand memset(&ri->h, 0, sizeof(ri->h)); 102a45cb177SDavid Hildenbrand ri->h.length = length; 103a45cb177SDavid Hildenbrand 1048ead801eSJanosch Frank cc = sclp_service_call(commands[i], ri); 1058ead801eSJanosch Frank if (cc) 106a45cb177SDavid Hildenbrand break; 107a45cb177SDavid Hildenbrand if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION) 108a45cb177SDavid Hildenbrand return; 109a45cb177SDavid Hildenbrand if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND) 110a45cb177SDavid Hildenbrand break; 111a45cb177SDavid Hildenbrand } 112a45cb177SDavid Hildenbrand report_abort("READ_SCP_INFO failed"); 113a45cb177SDavid Hildenbrand } 114a45cb177SDavid Hildenbrand 11552076a63SJanosch Frank void sclp_read_info(void) 11652076a63SJanosch Frank { 1173d49e2acSPierre Morel sclp_read_scp_info((void *)_read_info, 1183d49e2acSPierre Morel test_facility(140) ? sizeof(_read_info) : SCCB_SIZE); 11952076a63SJanosch Frank read_info = (ReadInfo *)_read_info; 12052076a63SJanosch Frank } 12152076a63SJanosch Frank 12252076a63SJanosch Frank int sclp_get_cpu_num(void) 12352076a63SJanosch Frank { 124a3a8ea2aSPierre Morel if (read_info) 12552076a63SJanosch Frank return read_info->entries_cpu; 126a3a8ea2aSPierre Morel /* 127a3a8ea2aSPierre Morel * Don't abort here if read_info is NULL since abort() calls 128a3a8ea2aSPierre Morel * smp_teardown() which eventually calls this function and thus 129a3a8ea2aSPierre Morel * causes an infinite abort() chain, causing the test to hang. 130a3a8ea2aSPierre Morel * Since we obviously have at least one CPU, just return one. 131a3a8ea2aSPierre Morel */ 132a3a8ea2aSPierre Morel return 1; 13352076a63SJanosch Frank } 13452076a63SJanosch Frank 13552076a63SJanosch Frank CPUEntry *sclp_get_cpu_entries(void) 13652076a63SJanosch Frank { 13752076a63SJanosch Frank assert(read_info); 13852076a63SJanosch Frank return (CPUEntry *)(_read_info + read_info->offset_cpu); 13952076a63SJanosch Frank } 14052076a63SJanosch Frank 1414dd649c8SJanosch Frank static bool sclp_feat_check(int byte, int bit) 1424dd649c8SJanosch Frank { 1434dd649c8SJanosch Frank uint8_t *rib = (uint8_t *)read_info; 1444dd649c8SJanosch Frank 1454dd649c8SJanosch Frank return !!(rib[byte] & (0x80 >> bit)); 1464dd649c8SJanosch Frank } 1474dd649c8SJanosch Frank 1486dff7c9aSJanosch Frank void sclp_facilities_setup(void) 1496dff7c9aSJanosch Frank { 1506dff7c9aSJanosch Frank unsigned short cpu0_addr = stap(); 1516dff7c9aSJanosch Frank CPUEntry *cpu; 1526dff7c9aSJanosch Frank int i; 1536dff7c9aSJanosch Frank 1546dff7c9aSJanosch Frank assert(read_info); 1556dff7c9aSJanosch Frank 1566dff7c9aSJanosch Frank cpu = sclp_get_cpu_entries(); 1575141dc1aSJanosch Frank if (read_info->offset_cpu > 134) 158fc03b7f4SJanosch Frank sclp_facilities.has_diag318 = read_info->byte_134_diag318; 1597c4e732dSJanis Schoetterl-Glausch sclp_facilities.has_sop = sclp_feat_check(80, SCLP_FEAT_80_BIT_SOP); 1604dd649c8SJanosch Frank sclp_facilities.has_gsls = sclp_feat_check(85, SCLP_FEAT_85_BIT_GSLS); 1617c4e732dSJanis Schoetterl-Glausch sclp_facilities.has_esop = sclp_feat_check(85, SCLP_FEAT_85_BIT_ESOP); 1624dd649c8SJanosch Frank sclp_facilities.has_kss = sclp_feat_check(98, SCLP_FEAT_98_BIT_KSS); 1634dd649c8SJanosch Frank sclp_facilities.has_cmma = sclp_feat_check(116, SCLP_FEAT_116_BIT_CMMA); 1644dd649c8SJanosch Frank sclp_facilities.has_64bscao = sclp_feat_check(116, SCLP_FEAT_116_BIT_64BSCAO); 1654dd649c8SJanosch Frank sclp_facilities.has_esca = sclp_feat_check(116, SCLP_FEAT_116_BIT_ESCA); 1664dd649c8SJanosch Frank sclp_facilities.has_ibs = sclp_feat_check(117, SCLP_FEAT_117_BIT_IBS); 1674dd649c8SJanosch Frank sclp_facilities.has_pfmfi = sclp_feat_check(117, SCLP_FEAT_117_BIT_PFMFI); 1684dd649c8SJanosch Frank 1696dff7c9aSJanosch Frank for (i = 0; i < read_info->entries_cpu; i++, cpu++) { 1706dff7c9aSJanosch Frank /* 1716dff7c9aSJanosch Frank * The logic for only reading the facilities from the 1726dff7c9aSJanosch Frank * boot cpu comes from the kernel. I haven't yet found 1736dff7c9aSJanosch Frank * documentation that explains why this is necessary 1746dff7c9aSJanosch Frank * but I figure there's a reason behind doing it this 1756dff7c9aSJanosch Frank * way. 1766dff7c9aSJanosch Frank */ 1776dff7c9aSJanosch Frank if (cpu->address == cpu0_addr) { 1786dff7c9aSJanosch Frank sclp_facilities.has_sief2 = cpu->feat_sief2; 1794dd649c8SJanosch Frank sclp_facilities.has_skeyi = cpu->feat_skeyi; 1804dd649c8SJanosch Frank sclp_facilities.has_siif = cpu->feat_siif; 1814dd649c8SJanosch Frank sclp_facilities.has_sigpif = cpu->feat_sigpif; 1824dd649c8SJanosch Frank sclp_facilities.has_ib = cpu->feat_ib; 1834dd649c8SJanosch Frank sclp_facilities.has_cei = cpu->feat_cei; 1846dff7c9aSJanosch Frank break; 1856dff7c9aSJanosch Frank } 1866dff7c9aSJanosch Frank } 1876dff7c9aSJanosch Frank } 1886dff7c9aSJanosch Frank 18935bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */ 19035bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb) 19135bfd1c4SJanosch Frank { 19235bfd1c4SJanosch Frank int cc; 19335bfd1c4SJanosch Frank 1948ead801eSJanosch Frank sclp_setup_int(); 195f9395bfeSClaudio Imbrenda cc = servc(command, __pa(sccb)); 1968ead801eSJanosch Frank sclp_wait_busy(); 19735bfd1c4SJanosch Frank if (cc == 3) 19835bfd1c4SJanosch Frank return -1; 19935bfd1c4SJanosch Frank if (cc == 2) 20035bfd1c4SJanosch Frank return -1; 20135bfd1c4SJanosch Frank return 0; 20235bfd1c4SJanosch Frank } 20335bfd1c4SJanosch Frank 2043db880b6SDavid Hildenbrand void sclp_memory_setup(void) 2053db880b6SDavid Hildenbrand { 2063db880b6SDavid Hildenbrand uint64_t rnmax, rnsize; 207b5b28387SJanis Schoetterl-Glausch enum tprot_permission permission; 2083db880b6SDavid Hildenbrand 20952076a63SJanosch Frank assert(read_info); 2103db880b6SDavid Hildenbrand 2113db880b6SDavid Hildenbrand /* calculate the storage increment size */ 21252076a63SJanosch Frank rnsize = read_info->rnsize; 2133db880b6SDavid Hildenbrand if (!rnsize) { 21452076a63SJanosch Frank rnsize = read_info->rnsize2; 2153db880b6SDavid Hildenbrand } 2163db880b6SDavid Hildenbrand storage_increment_size = rnsize << 20; 2173db880b6SDavid Hildenbrand 2183db880b6SDavid Hildenbrand /* calculate the maximum memory size */ 21952076a63SJanosch Frank rnmax = read_info->rnmax; 2203db880b6SDavid Hildenbrand if (!rnmax) { 22152076a63SJanosch Frank rnmax = read_info->rnmax2; 2223db880b6SDavid Hildenbrand } 2233db880b6SDavid Hildenbrand max_ram_size = rnmax * storage_increment_size; 2243db880b6SDavid Hildenbrand 2253db880b6SDavid Hildenbrand /* lowcore is always accessible, so the first increment is accessible */ 2263db880b6SDavid Hildenbrand ram_size = storage_increment_size; 2273db880b6SDavid Hildenbrand 2283db880b6SDavid Hildenbrand /* probe for r/w memory up to max memory size */ 2293db880b6SDavid Hildenbrand while (ram_size < max_ram_size) { 2303db880b6SDavid Hildenbrand expect_pgm_int(); 231b5b28387SJanis Schoetterl-Glausch permission = tprot(ram_size + storage_increment_size - 1, 0); 2323db880b6SDavid Hildenbrand /* stop once we receive an exception or have protected memory */ 233b5b28387SJanis Schoetterl-Glausch if (clear_pgm_int() || permission != TPROT_READ_WRITE) 2343db880b6SDavid Hildenbrand break; 2353db880b6SDavid Hildenbrand ram_size += storage_increment_size; 2363db880b6SDavid Hildenbrand } 23792bbd322SDavid Hildenbrand 23892bbd322SDavid Hildenbrand mem_init(ram_size); 2393db880b6SDavid Hildenbrand } 24084a79f17SClaudio Imbrenda 24184a79f17SClaudio Imbrenda uint64_t get_ram_size(void) 24284a79f17SClaudio Imbrenda { 24384a79f17SClaudio Imbrenda return ram_size; 24484a79f17SClaudio Imbrenda } 24584a79f17SClaudio Imbrenda 24684a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void) 24784a79f17SClaudio Imbrenda { 24884a79f17SClaudio Imbrenda return max_ram_size; 24984a79f17SClaudio Imbrenda } 250*6f33f0b7SPierre Morel 251*6f33f0b7SPierre Morel uint64_t sclp_get_stsi_mnest(void) 252*6f33f0b7SPierre Morel { 253*6f33f0b7SPierre Morel assert(read_info); 254*6f33f0b7SPierre Morel return read_info->stsi_parm; 255*6f33f0b7SPierre Morel } 256