xref: /kvm-unit-tests/lib/s390x/sclp.c (revision 086985a39ccb9b7b3da910d3a23eb764f0b76423)
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>
2092bbd322SDavid Hildenbrand 
2192bbd322SDavid Hildenbrand extern unsigned long stacktop;
223db880b6SDavid Hildenbrand 
233db880b6SDavid Hildenbrand static uint64_t storage_increment_size;
243db880b6SDavid Hildenbrand static uint64_t max_ram_size;
253db880b6SDavid Hildenbrand static uint64_t ram_size;
2652076a63SJanosch Frank char _read_info[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
2752076a63SJanosch Frank static ReadInfo *read_info;
286dff7c9aSJanosch Frank struct sclp_facilities sclp_facilities;
293db880b6SDavid Hildenbrand 
3035bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096)));
318ead801eSJanosch Frank static volatile bool sclp_busy;
328ead801eSJanosch Frank static struct spinlock sclp_lock;
3335bfd1c4SJanosch Frank 
3492bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end)
3592bbd322SDavid Hildenbrand {
3692bbd322SDavid Hildenbrand 	phys_addr_t freemem_start = (phys_addr_t)&stacktop;
37b356c913SJanosch Frank 	phys_addr_t base, top;
3892bbd322SDavid Hildenbrand 
3992bbd322SDavid Hildenbrand 	phys_alloc_init(freemem_start, mem_end - freemem_start);
40b356c913SJanosch Frank 	phys_alloc_get_unused(&base, &top);
418131e91aSClaudio Imbrenda 	base = PAGE_ALIGN(base) >> PAGE_SHIFT;
428131e91aSClaudio Imbrenda 	top = top >> PAGE_SHIFT;
43b356c913SJanosch Frank 
44b356c913SJanosch Frank 	/* Make the pages available to the physical allocator */
458131e91aSClaudio Imbrenda 	page_alloc_init_area(AREA_ANY_NUMBER, base, top);
46b356c913SJanosch Frank 	page_alloc_ops_enable();
4792bbd322SDavid Hildenbrand }
4892bbd322SDavid Hildenbrand 
49fd6fc0d9SClaudio Imbrenda void sclp_setup_int(void)
508ead801eSJanosch Frank {
511b2c0437SClaudio Imbrenda 	ctl_set_bit(0, CTL0_SERVICE_SIGNAL);
52*086985a3SClaudio Imbrenda 	psw_mask_set_bits(PSW_MASK_EXT);
538ead801eSJanosch Frank }
548ead801eSJanosch Frank 
558ead801eSJanosch Frank void sclp_handle_ext(void)
568ead801eSJanosch Frank {
571b2c0437SClaudio Imbrenda 	ctl_clear_bit(0, CTL0_SERVICE_SIGNAL);
588b98745dSDavid Hildenbrand 	sclp_clear_busy();
598ead801eSJanosch Frank }
608ead801eSJanosch Frank 
618ead801eSJanosch Frank void sclp_wait_busy(void)
628ead801eSJanosch Frank {
638ead801eSJanosch Frank 	while (sclp_busy)
648ead801eSJanosch Frank 		mb();
658ead801eSJanosch Frank }
668ead801eSJanosch Frank 
678ead801eSJanosch Frank void sclp_mark_busy(void)
688ead801eSJanosch Frank {
698ead801eSJanosch Frank 	/*
708ead801eSJanosch Frank 	 * With multiple CPUs we might need to wait for another CPU's
718ead801eSJanosch Frank 	 * request before grabbing the busy indication.
728ead801eSJanosch Frank 	 */
738ead801eSJanosch Frank 	while (true) {
748ead801eSJanosch Frank 		sclp_wait_busy();
758ead801eSJanosch Frank 		spin_lock(&sclp_lock);
768ead801eSJanosch Frank 		if (!sclp_busy) {
778ead801eSJanosch Frank 			sclp_busy = true;
788ead801eSJanosch Frank 			spin_unlock(&sclp_lock);
798ead801eSJanosch Frank 			return;
808ead801eSJanosch Frank 		}
818ead801eSJanosch Frank 		spin_unlock(&sclp_lock);
828ead801eSJanosch Frank 	}
838ead801eSJanosch Frank }
848ead801eSJanosch Frank 
858b98745dSDavid Hildenbrand void sclp_clear_busy(void)
868b98745dSDavid Hildenbrand {
878b98745dSDavid Hildenbrand 	spin_lock(&sclp_lock);
888b98745dSDavid Hildenbrand 	sclp_busy = false;
898b98745dSDavid Hildenbrand 	spin_unlock(&sclp_lock);
908b98745dSDavid Hildenbrand }
918b98745dSDavid Hildenbrand 
92a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length)
93a45cb177SDavid Hildenbrand {
94a45cb177SDavid Hildenbrand 	unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED,
95a45cb177SDavid Hildenbrand 				    SCLP_CMDW_READ_SCP_INFO };
968ead801eSJanosch Frank 	int i, cc;
97a45cb177SDavid Hildenbrand 
98a45cb177SDavid Hildenbrand 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
998ead801eSJanosch Frank 		sclp_mark_busy();
100a45cb177SDavid Hildenbrand 		memset(&ri->h, 0, sizeof(ri->h));
101a45cb177SDavid Hildenbrand 		ri->h.length = length;
102a45cb177SDavid Hildenbrand 
1038ead801eSJanosch Frank 		cc = sclp_service_call(commands[i], ri);
1048ead801eSJanosch Frank 		if (cc)
105a45cb177SDavid Hildenbrand 			break;
106a45cb177SDavid Hildenbrand 		if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION)
107a45cb177SDavid Hildenbrand 			return;
108a45cb177SDavid Hildenbrand 		if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND)
109a45cb177SDavid Hildenbrand 			break;
110a45cb177SDavid Hildenbrand 	}
111a45cb177SDavid Hildenbrand 	report_abort("READ_SCP_INFO failed");
112a45cb177SDavid Hildenbrand }
113a45cb177SDavid Hildenbrand 
11452076a63SJanosch Frank void sclp_read_info(void)
11552076a63SJanosch Frank {
11652076a63SJanosch Frank 	sclp_read_scp_info((void *)_read_info, SCCB_SIZE);
11752076a63SJanosch Frank 	read_info = (ReadInfo *)_read_info;
11852076a63SJanosch Frank }
11952076a63SJanosch Frank 
12052076a63SJanosch Frank int sclp_get_cpu_num(void)
12152076a63SJanosch Frank {
12252076a63SJanosch Frank 	assert(read_info);
12352076a63SJanosch Frank 	return read_info->entries_cpu;
12452076a63SJanosch Frank }
12552076a63SJanosch Frank 
12652076a63SJanosch Frank CPUEntry *sclp_get_cpu_entries(void)
12752076a63SJanosch Frank {
12852076a63SJanosch Frank 	assert(read_info);
12952076a63SJanosch Frank 	return (CPUEntry *)(_read_info + read_info->offset_cpu);
13052076a63SJanosch Frank }
13152076a63SJanosch Frank 
1324dd649c8SJanosch Frank static bool sclp_feat_check(int byte, int bit)
1334dd649c8SJanosch Frank {
1344dd649c8SJanosch Frank 	uint8_t *rib = (uint8_t *)read_info;
1354dd649c8SJanosch Frank 
1364dd649c8SJanosch Frank 	return !!(rib[byte] & (0x80 >> bit));
1374dd649c8SJanosch Frank }
1384dd649c8SJanosch Frank 
1396dff7c9aSJanosch Frank void sclp_facilities_setup(void)
1406dff7c9aSJanosch Frank {
1416dff7c9aSJanosch Frank 	unsigned short cpu0_addr = stap();
1426dff7c9aSJanosch Frank 	CPUEntry *cpu;
1436dff7c9aSJanosch Frank 	int i;
1446dff7c9aSJanosch Frank 
1456dff7c9aSJanosch Frank 	assert(read_info);
1466dff7c9aSJanosch Frank 
1476dff7c9aSJanosch Frank 	cpu = sclp_get_cpu_entries();
1485141dc1aSJanosch Frank 	if (read_info->offset_cpu > 134)
149fc03b7f4SJanosch Frank 		sclp_facilities.has_diag318 = read_info->byte_134_diag318;
1507c4e732dSJanis Schoetterl-Glausch 	sclp_facilities.has_sop = sclp_feat_check(80, SCLP_FEAT_80_BIT_SOP);
1514dd649c8SJanosch Frank 	sclp_facilities.has_gsls = sclp_feat_check(85, SCLP_FEAT_85_BIT_GSLS);
1527c4e732dSJanis Schoetterl-Glausch 	sclp_facilities.has_esop = sclp_feat_check(85, SCLP_FEAT_85_BIT_ESOP);
1534dd649c8SJanosch Frank 	sclp_facilities.has_kss = sclp_feat_check(98, SCLP_FEAT_98_BIT_KSS);
1544dd649c8SJanosch Frank 	sclp_facilities.has_cmma = sclp_feat_check(116, SCLP_FEAT_116_BIT_CMMA);
1554dd649c8SJanosch Frank 	sclp_facilities.has_64bscao = sclp_feat_check(116, SCLP_FEAT_116_BIT_64BSCAO);
1564dd649c8SJanosch Frank 	sclp_facilities.has_esca = sclp_feat_check(116, SCLP_FEAT_116_BIT_ESCA);
1574dd649c8SJanosch Frank 	sclp_facilities.has_ibs = sclp_feat_check(117, SCLP_FEAT_117_BIT_IBS);
1584dd649c8SJanosch Frank 	sclp_facilities.has_pfmfi = sclp_feat_check(117, SCLP_FEAT_117_BIT_PFMFI);
1594dd649c8SJanosch Frank 
1606dff7c9aSJanosch Frank 	for (i = 0; i < read_info->entries_cpu; i++, cpu++) {
1616dff7c9aSJanosch Frank 		/*
1626dff7c9aSJanosch Frank 		 * The logic for only reading the facilities from the
1636dff7c9aSJanosch Frank 		 * boot cpu comes from the kernel. I haven't yet found
1646dff7c9aSJanosch Frank 		 * documentation that explains why this is necessary
1656dff7c9aSJanosch Frank 		 * but I figure there's a reason behind doing it this
1666dff7c9aSJanosch Frank 		 * way.
1676dff7c9aSJanosch Frank 		 */
1686dff7c9aSJanosch Frank 		if (cpu->address == cpu0_addr) {
1696dff7c9aSJanosch Frank 			sclp_facilities.has_sief2 = cpu->feat_sief2;
1704dd649c8SJanosch Frank 			sclp_facilities.has_skeyi = cpu->feat_skeyi;
1714dd649c8SJanosch Frank 			sclp_facilities.has_siif = cpu->feat_siif;
1724dd649c8SJanosch Frank 			sclp_facilities.has_sigpif = cpu->feat_sigpif;
1734dd649c8SJanosch Frank 			sclp_facilities.has_ib = cpu->feat_ib;
1744dd649c8SJanosch Frank 			sclp_facilities.has_cei = cpu->feat_cei;
1756dff7c9aSJanosch Frank 			break;
1766dff7c9aSJanosch Frank 		}
1776dff7c9aSJanosch Frank 	}
1786dff7c9aSJanosch Frank }
1796dff7c9aSJanosch Frank 
18035bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */
18135bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb)
18235bfd1c4SJanosch Frank {
18335bfd1c4SJanosch Frank 	int cc;
18435bfd1c4SJanosch Frank 
1858ead801eSJanosch Frank 	sclp_setup_int();
186f9395bfeSClaudio Imbrenda 	cc = servc(command, __pa(sccb));
1878ead801eSJanosch Frank 	sclp_wait_busy();
18835bfd1c4SJanosch Frank 	if (cc == 3)
18935bfd1c4SJanosch Frank 		return -1;
19035bfd1c4SJanosch Frank 	if (cc == 2)
19135bfd1c4SJanosch Frank 		return -1;
19235bfd1c4SJanosch Frank 	return 0;
19335bfd1c4SJanosch Frank }
19435bfd1c4SJanosch Frank 
1953db880b6SDavid Hildenbrand void sclp_memory_setup(void)
1963db880b6SDavid Hildenbrand {
1973db880b6SDavid Hildenbrand 	uint64_t rnmax, rnsize;
198b5b28387SJanis Schoetterl-Glausch 	enum tprot_permission permission;
1993db880b6SDavid Hildenbrand 
20052076a63SJanosch Frank 	assert(read_info);
2013db880b6SDavid Hildenbrand 
2023db880b6SDavid Hildenbrand 	/* calculate the storage increment size */
20352076a63SJanosch Frank 	rnsize = read_info->rnsize;
2043db880b6SDavid Hildenbrand 	if (!rnsize) {
20552076a63SJanosch Frank 		rnsize = read_info->rnsize2;
2063db880b6SDavid Hildenbrand 	}
2073db880b6SDavid Hildenbrand 	storage_increment_size = rnsize << 20;
2083db880b6SDavid Hildenbrand 
2093db880b6SDavid Hildenbrand 	/* calculate the maximum memory size */
21052076a63SJanosch Frank 	rnmax = read_info->rnmax;
2113db880b6SDavid Hildenbrand 	if (!rnmax) {
21252076a63SJanosch Frank 		rnmax = read_info->rnmax2;
2133db880b6SDavid Hildenbrand 	}
2143db880b6SDavid Hildenbrand 	max_ram_size = rnmax * storage_increment_size;
2153db880b6SDavid Hildenbrand 
2163db880b6SDavid Hildenbrand 	/* lowcore is always accessible, so the first increment is accessible */
2173db880b6SDavid Hildenbrand 	ram_size = storage_increment_size;
2183db880b6SDavid Hildenbrand 
2193db880b6SDavid Hildenbrand 	/* probe for r/w memory up to max memory size */
2203db880b6SDavid Hildenbrand 	while (ram_size < max_ram_size) {
2213db880b6SDavid Hildenbrand 		expect_pgm_int();
222b5b28387SJanis Schoetterl-Glausch 		permission = tprot(ram_size + storage_increment_size - 1, 0);
2233db880b6SDavid Hildenbrand 		/* stop once we receive an exception or have protected memory */
224b5b28387SJanis Schoetterl-Glausch 		if (clear_pgm_int() || permission != TPROT_READ_WRITE)
2253db880b6SDavid Hildenbrand 			break;
2263db880b6SDavid Hildenbrand 		ram_size += storage_increment_size;
2273db880b6SDavid Hildenbrand 	}
22892bbd322SDavid Hildenbrand 
22992bbd322SDavid Hildenbrand 	mem_init(ram_size);
2303db880b6SDavid Hildenbrand }
23184a79f17SClaudio Imbrenda 
23284a79f17SClaudio Imbrenda uint64_t get_ram_size(void)
23384a79f17SClaudio Imbrenda {
23484a79f17SClaudio Imbrenda 	return ram_size;
23584a79f17SClaudio Imbrenda }
23684a79f17SClaudio Imbrenda 
23784a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void)
23884a79f17SClaudio Imbrenda {
23984a79f17SClaudio Imbrenda 	return max_ram_size;
24084a79f17SClaudio Imbrenda }
241