xref: /kvm-unit-tests/lib/s390x/sclp.c (revision 90cacd85c6ad50f032a3fe95586fab4f2335b93d)
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 
mem_init(phys_addr_t mem_end)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 
sclp_setup_int(void)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 
sclp_handle_ext(void)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 
sclp_wait_busy(void)628ead801eSJanosch Frank void sclp_wait_busy(void)
638ead801eSJanosch Frank {
648ead801eSJanosch Frank 	while (sclp_busy)
658ead801eSJanosch Frank 		mb();
668ead801eSJanosch Frank }
678ead801eSJanosch Frank 
sclp_mark_busy(void)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 
sclp_clear_busy(void)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 
sclp_read_scp_info(ReadInfo * ri,int length)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 
sclp_read_info(void)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 
sclp_get_cpu_num(void)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 
sclp_get_cpu_entries(void)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 
sclp_feat_check(int byte,int bit)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 
sclp_facilities_setup(void)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. */
sclp_service_call(unsigned int command,void * sccb)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 
sclp_memory_setup(void)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 
get_ram_size(void)24184a79f17SClaudio Imbrenda uint64_t get_ram_size(void)
24284a79f17SClaudio Imbrenda {
24384a79f17SClaudio Imbrenda 	return ram_size;
24484a79f17SClaudio Imbrenda }
24584a79f17SClaudio Imbrenda 
get_max_ram_size(void)24684a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void)
24784a79f17SClaudio Imbrenda {
24884a79f17SClaudio Imbrenda 	return max_ram_size;
24984a79f17SClaudio Imbrenda }
250*6f33f0b7SPierre Morel 
sclp_get_stsi_mnest(void)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