xref: /kvm-unit-tests/lib/s390x/sclp.c (revision 52076a63d569e0cea2838216979227d6bfb36628)
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;
26*52076a63SJanosch Frank char _read_info[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
27*52076a63SJanosch Frank static ReadInfo *read_info;
283db880b6SDavid Hildenbrand 
2935bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096)));
308ead801eSJanosch Frank static volatile bool sclp_busy;
318ead801eSJanosch Frank static struct spinlock sclp_lock;
3235bfd1c4SJanosch Frank 
3392bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end)
3492bbd322SDavid Hildenbrand {
3592bbd322SDavid Hildenbrand 	phys_addr_t freemem_start = (phys_addr_t)&stacktop;
36b356c913SJanosch Frank 	phys_addr_t base, top;
3792bbd322SDavid Hildenbrand 
3892bbd322SDavid Hildenbrand 	phys_alloc_init(freemem_start, mem_end - freemem_start);
39b356c913SJanosch Frank 	phys_alloc_get_unused(&base, &top);
408131e91aSClaudio Imbrenda 	base = PAGE_ALIGN(base) >> PAGE_SHIFT;
418131e91aSClaudio Imbrenda 	top = top >> PAGE_SHIFT;
42b356c913SJanosch Frank 
43b356c913SJanosch Frank 	/* Make the pages available to the physical allocator */
448131e91aSClaudio Imbrenda 	page_alloc_init_area(AREA_ANY_NUMBER, base, top);
45b356c913SJanosch Frank 	page_alloc_ops_enable();
4692bbd322SDavid Hildenbrand }
4792bbd322SDavid Hildenbrand 
48fd6fc0d9SClaudio Imbrenda void sclp_setup_int(void)
498ead801eSJanosch Frank {
508ead801eSJanosch Frank 	uint64_t mask;
518ead801eSJanosch Frank 
528ead801eSJanosch Frank 	ctl_set_bit(0, 9);
538ead801eSJanosch Frank 
548ead801eSJanosch Frank 	mask = extract_psw_mask();
558ead801eSJanosch Frank 	mask |= PSW_MASK_EXT;
568ead801eSJanosch Frank 	load_psw_mask(mask);
578ead801eSJanosch Frank }
588ead801eSJanosch Frank 
598ead801eSJanosch Frank void sclp_handle_ext(void)
608ead801eSJanosch Frank {
618ead801eSJanosch Frank 	ctl_clear_bit(0, 9);
628ead801eSJanosch Frank 	spin_lock(&sclp_lock);
638ead801eSJanosch Frank 	sclp_busy = false;
648ead801eSJanosch Frank 	spin_unlock(&sclp_lock);
658ead801eSJanosch Frank }
668ead801eSJanosch Frank 
678ead801eSJanosch Frank void sclp_wait_busy(void)
688ead801eSJanosch Frank {
698ead801eSJanosch Frank 	while (sclp_busy)
708ead801eSJanosch Frank 		mb();
718ead801eSJanosch Frank }
728ead801eSJanosch Frank 
738ead801eSJanosch Frank void sclp_mark_busy(void)
748ead801eSJanosch Frank {
758ead801eSJanosch Frank 	/*
768ead801eSJanosch Frank 	 * With multiple CPUs we might need to wait for another CPU's
778ead801eSJanosch Frank 	 * request before grabbing the busy indication.
788ead801eSJanosch Frank 	 */
798ead801eSJanosch Frank 	while (true) {
808ead801eSJanosch Frank 		sclp_wait_busy();
818ead801eSJanosch Frank 		spin_lock(&sclp_lock);
828ead801eSJanosch Frank 		if (!sclp_busy) {
838ead801eSJanosch Frank 			sclp_busy = true;
848ead801eSJanosch Frank 			spin_unlock(&sclp_lock);
858ead801eSJanosch Frank 			return;
868ead801eSJanosch Frank 		}
878ead801eSJanosch Frank 		spin_unlock(&sclp_lock);
888ead801eSJanosch Frank 	}
898ead801eSJanosch Frank }
908ead801eSJanosch Frank 
91a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length)
92a45cb177SDavid Hildenbrand {
93a45cb177SDavid Hildenbrand 	unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED,
94a45cb177SDavid Hildenbrand 				    SCLP_CMDW_READ_SCP_INFO };
958ead801eSJanosch Frank 	int i, cc;
96a45cb177SDavid Hildenbrand 
97a45cb177SDavid Hildenbrand 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
988ead801eSJanosch Frank 		sclp_mark_busy();
99a45cb177SDavid Hildenbrand 		memset(&ri->h, 0, sizeof(ri->h));
100a45cb177SDavid Hildenbrand 		ri->h.length = length;
101a45cb177SDavid Hildenbrand 
1028ead801eSJanosch Frank 		cc = sclp_service_call(commands[i], ri);
1038ead801eSJanosch Frank 		if (cc)
104a45cb177SDavid Hildenbrand 			break;
105a45cb177SDavid Hildenbrand 		if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION)
106a45cb177SDavid Hildenbrand 			return;
107a45cb177SDavid Hildenbrand 		if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND)
108a45cb177SDavid Hildenbrand 			break;
109a45cb177SDavid Hildenbrand 	}
110a45cb177SDavid Hildenbrand 	report_abort("READ_SCP_INFO failed");
111a45cb177SDavid Hildenbrand }
112a45cb177SDavid Hildenbrand 
113*52076a63SJanosch Frank void sclp_read_info(void)
114*52076a63SJanosch Frank {
115*52076a63SJanosch Frank 	sclp_read_scp_info((void *)_read_info, SCCB_SIZE);
116*52076a63SJanosch Frank 	read_info = (ReadInfo *)_read_info;
117*52076a63SJanosch Frank }
118*52076a63SJanosch Frank 
119*52076a63SJanosch Frank int sclp_get_cpu_num(void)
120*52076a63SJanosch Frank {
121*52076a63SJanosch Frank 	assert(read_info);
122*52076a63SJanosch Frank 	return read_info->entries_cpu;
123*52076a63SJanosch Frank }
124*52076a63SJanosch Frank 
125*52076a63SJanosch Frank CPUEntry *sclp_get_cpu_entries(void)
126*52076a63SJanosch Frank {
127*52076a63SJanosch Frank 	assert(read_info);
128*52076a63SJanosch Frank 	return (CPUEntry *)(_read_info + read_info->offset_cpu);
129*52076a63SJanosch Frank }
130*52076a63SJanosch Frank 
13135bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */
13235bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb)
13335bfd1c4SJanosch Frank {
13435bfd1c4SJanosch Frank 	int cc;
13535bfd1c4SJanosch Frank 
1368ead801eSJanosch Frank 	sclp_setup_int();
137f9395bfeSClaudio Imbrenda 	cc = servc(command, __pa(sccb));
1388ead801eSJanosch Frank 	sclp_wait_busy();
13935bfd1c4SJanosch Frank 	if (cc == 3)
14035bfd1c4SJanosch Frank 		return -1;
14135bfd1c4SJanosch Frank 	if (cc == 2)
14235bfd1c4SJanosch Frank 		return -1;
14335bfd1c4SJanosch Frank 	return 0;
14435bfd1c4SJanosch Frank }
14535bfd1c4SJanosch Frank 
1463db880b6SDavid Hildenbrand void sclp_memory_setup(void)
1473db880b6SDavid Hildenbrand {
1483db880b6SDavid Hildenbrand 	uint64_t rnmax, rnsize;
1493db880b6SDavid Hildenbrand 	int cc;
1503db880b6SDavid Hildenbrand 
151*52076a63SJanosch Frank 	assert(read_info);
1523db880b6SDavid Hildenbrand 
1533db880b6SDavid Hildenbrand 	/* calculate the storage increment size */
154*52076a63SJanosch Frank 	rnsize = read_info->rnsize;
1553db880b6SDavid Hildenbrand 	if (!rnsize) {
156*52076a63SJanosch Frank 		rnsize = read_info->rnsize2;
1573db880b6SDavid Hildenbrand 	}
1583db880b6SDavid Hildenbrand 	storage_increment_size = rnsize << 20;
1593db880b6SDavid Hildenbrand 
1603db880b6SDavid Hildenbrand 	/* calculate the maximum memory size */
161*52076a63SJanosch Frank 	rnmax = read_info->rnmax;
1623db880b6SDavid Hildenbrand 	if (!rnmax) {
163*52076a63SJanosch Frank 		rnmax = read_info->rnmax2;
1643db880b6SDavid Hildenbrand 	}
1653db880b6SDavid Hildenbrand 	max_ram_size = rnmax * storage_increment_size;
1663db880b6SDavid Hildenbrand 
1673db880b6SDavid Hildenbrand 	/* lowcore is always accessible, so the first increment is accessible */
1683db880b6SDavid Hildenbrand 	ram_size = storage_increment_size;
1693db880b6SDavid Hildenbrand 
1703db880b6SDavid Hildenbrand 	/* probe for r/w memory up to max memory size */
1713db880b6SDavid Hildenbrand 	while (ram_size < max_ram_size) {
1723db880b6SDavid Hildenbrand 		expect_pgm_int();
1733db880b6SDavid Hildenbrand 		cc = tprot(ram_size + storage_increment_size - 1);
1743db880b6SDavid Hildenbrand 		/* stop once we receive an exception or have protected memory */
1753db880b6SDavid Hildenbrand 		if (clear_pgm_int() || cc != 0)
1763db880b6SDavid Hildenbrand 			break;
1773db880b6SDavid Hildenbrand 		ram_size += storage_increment_size;
1783db880b6SDavid Hildenbrand 	}
17992bbd322SDavid Hildenbrand 
18092bbd322SDavid Hildenbrand 	mem_init(ram_size);
1813db880b6SDavid Hildenbrand }
18284a79f17SClaudio Imbrenda 
18384a79f17SClaudio Imbrenda uint64_t get_ram_size(void)
18484a79f17SClaudio Imbrenda {
18584a79f17SClaudio Imbrenda 	return ram_size;
18684a79f17SClaudio Imbrenda }
18784a79f17SClaudio Imbrenda 
18884a79f17SClaudio Imbrenda uint64_t get_max_ram_size(void)
18984a79f17SClaudio Imbrenda {
19084a79f17SClaudio Imbrenda 	return max_ram_size;
19184a79f17SClaudio Imbrenda }
192