xref: /kvm-unit-tests/lib/s390x/sclp.c (revision 8ead801e5a97ba47b7c1691e56867061b94bd57b)
13db880b6SDavid Hildenbrand /*
23db880b6SDavid Hildenbrand  * s390x SCLP driver
33db880b6SDavid Hildenbrand  *
43db880b6SDavid Hildenbrand  * Copyright (c) 2017 Red Hat Inc
53db880b6SDavid Hildenbrand  *
63db880b6SDavid Hildenbrand  * Authors:
73db880b6SDavid Hildenbrand  *  David Hildenbrand <david@redhat.com>
83db880b6SDavid Hildenbrand  *
93db880b6SDavid Hildenbrand  * This code is free software; you can redistribute it and/or modify it
103db880b6SDavid Hildenbrand  * under the terms of the GNU Library General Public License version 2.
113db880b6SDavid Hildenbrand  */
123db880b6SDavid Hildenbrand 
133db880b6SDavid Hildenbrand #include <libcflat.h>
143db880b6SDavid Hildenbrand #include <asm/page.h>
153db880b6SDavid Hildenbrand #include <asm/arch_def.h>
163db880b6SDavid Hildenbrand #include <asm/interrupt.h>
17*8ead801eSJanosch Frank #include <asm/barrier.h>
18*8ead801eSJanosch Frank #include <asm/spinlock.h>
193db880b6SDavid Hildenbrand #include "sclp.h"
2092bbd322SDavid Hildenbrand #include <alloc_phys.h>
21b356c913SJanosch Frank #include <alloc_page.h>
2292bbd322SDavid Hildenbrand 
2392bbd322SDavid Hildenbrand extern unsigned long stacktop;
243db880b6SDavid Hildenbrand 
253db880b6SDavid Hildenbrand static uint64_t storage_increment_size;
263db880b6SDavid Hildenbrand static uint64_t max_ram_size;
273db880b6SDavid Hildenbrand static uint64_t ram_size;
283db880b6SDavid Hildenbrand 
2935bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096)));
30*8ead801eSJanosch Frank static volatile bool sclp_busy;
31*8ead801eSJanosch 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);
40b356c913SJanosch Frank 	base = (base + PAGE_SIZE - 1) & -PAGE_SIZE;
41b356c913SJanosch Frank 	top = top & -PAGE_SIZE;
42b356c913SJanosch Frank 
43b356c913SJanosch Frank 	/* Make the pages available to the physical allocator */
44b356c913SJanosch Frank 	free_pages((void *)(unsigned long)base, top - base);
45b356c913SJanosch Frank 	page_alloc_ops_enable();
4692bbd322SDavid Hildenbrand }
4792bbd322SDavid Hildenbrand 
48*8ead801eSJanosch Frank static void sclp_setup_int(void)
49*8ead801eSJanosch Frank {
50*8ead801eSJanosch Frank 	uint64_t mask;
51*8ead801eSJanosch Frank 
52*8ead801eSJanosch Frank 	ctl_set_bit(0, 9);
53*8ead801eSJanosch Frank 
54*8ead801eSJanosch Frank 	mask = extract_psw_mask();
55*8ead801eSJanosch Frank 	mask |= PSW_MASK_EXT;
56*8ead801eSJanosch Frank 	load_psw_mask(mask);
57*8ead801eSJanosch Frank }
58*8ead801eSJanosch Frank 
59*8ead801eSJanosch Frank void sclp_handle_ext(void)
60*8ead801eSJanosch Frank {
61*8ead801eSJanosch Frank 	ctl_clear_bit(0, 9);
62*8ead801eSJanosch Frank 	spin_lock(&sclp_lock);
63*8ead801eSJanosch Frank 	sclp_busy = false;
64*8ead801eSJanosch Frank 	spin_unlock(&sclp_lock);
65*8ead801eSJanosch Frank }
66*8ead801eSJanosch Frank 
67*8ead801eSJanosch Frank void sclp_wait_busy(void)
68*8ead801eSJanosch Frank {
69*8ead801eSJanosch Frank 	while (sclp_busy)
70*8ead801eSJanosch Frank 		mb();
71*8ead801eSJanosch Frank }
72*8ead801eSJanosch Frank 
73*8ead801eSJanosch Frank void sclp_mark_busy(void)
74*8ead801eSJanosch Frank {
75*8ead801eSJanosch Frank 	/*
76*8ead801eSJanosch Frank 	 * With multiple CPUs we might need to wait for another CPU's
77*8ead801eSJanosch Frank 	 * request before grabbing the busy indication.
78*8ead801eSJanosch Frank 	 */
79*8ead801eSJanosch Frank 	while (true) {
80*8ead801eSJanosch Frank 		sclp_wait_busy();
81*8ead801eSJanosch Frank 		spin_lock(&sclp_lock);
82*8ead801eSJanosch Frank 		if (!sclp_busy) {
83*8ead801eSJanosch Frank 			sclp_busy = true;
84*8ead801eSJanosch Frank 			spin_unlock(&sclp_lock);
85*8ead801eSJanosch Frank 			return;
86*8ead801eSJanosch Frank 		}
87*8ead801eSJanosch Frank 		spin_unlock(&sclp_lock);
88*8ead801eSJanosch Frank 	}
89*8ead801eSJanosch Frank }
90*8ead801eSJanosch 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 };
95*8ead801eSJanosch Frank 	int i, cc;
96a45cb177SDavid Hildenbrand 
97a45cb177SDavid Hildenbrand 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
98*8ead801eSJanosch Frank 		sclp_mark_busy();
99a45cb177SDavid Hildenbrand 		memset(&ri->h, 0, sizeof(ri->h));
100a45cb177SDavid Hildenbrand 		ri->h.length = length;
101a45cb177SDavid Hildenbrand 
102*8ead801eSJanosch Frank 		cc = sclp_service_call(commands[i], ri);
103*8ead801eSJanosch 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 
11335bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */
11435bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb)
11535bfd1c4SJanosch Frank {
11635bfd1c4SJanosch Frank 	int cc;
11735bfd1c4SJanosch Frank 
118*8ead801eSJanosch Frank 	sclp_setup_int();
11935bfd1c4SJanosch Frank 	asm volatile(
12035bfd1c4SJanosch Frank 		"       .insn   rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
12135bfd1c4SJanosch Frank 		"       ipm     %0\n"
12235bfd1c4SJanosch Frank 		"       srl     %0,28"
12335bfd1c4SJanosch Frank 		: "=&d" (cc) : "d" (command), "a" (__pa(sccb))
12435bfd1c4SJanosch Frank 		: "cc", "memory");
125*8ead801eSJanosch Frank 	sclp_wait_busy();
12635bfd1c4SJanosch Frank 	if (cc == 3)
12735bfd1c4SJanosch Frank 		return -1;
12835bfd1c4SJanosch Frank 	if (cc == 2)
12935bfd1c4SJanosch Frank 		return -1;
13035bfd1c4SJanosch Frank 	return 0;
13135bfd1c4SJanosch Frank }
13235bfd1c4SJanosch Frank 
1333db880b6SDavid Hildenbrand void sclp_memory_setup(void)
1343db880b6SDavid Hildenbrand {
1353db880b6SDavid Hildenbrand 	ReadInfo *ri = (void *)_sccb;
1363db880b6SDavid Hildenbrand 	uint64_t rnmax, rnsize;
1373db880b6SDavid Hildenbrand 	int cc;
1383db880b6SDavid Hildenbrand 
139a45cb177SDavid Hildenbrand 	sclp_read_scp_info(ri, SCCB_SIZE);
1403db880b6SDavid Hildenbrand 
1413db880b6SDavid Hildenbrand 	/* calculate the storage increment size */
1423db880b6SDavid Hildenbrand 	rnsize = ri->rnsize;
1433db880b6SDavid Hildenbrand 	if (!rnsize) {
1443db880b6SDavid Hildenbrand 		rnsize = ri->rnsize2;
1453db880b6SDavid Hildenbrand 	}
1463db880b6SDavid Hildenbrand 	storage_increment_size = rnsize << 20;
1473db880b6SDavid Hildenbrand 
1483db880b6SDavid Hildenbrand 	/* calculate the maximum memory size */
1493db880b6SDavid Hildenbrand 	rnmax = ri->rnmax;
1503db880b6SDavid Hildenbrand 	if (!rnmax) {
1513db880b6SDavid Hildenbrand 		rnmax = ri->rnmax2;
1523db880b6SDavid Hildenbrand 	}
1533db880b6SDavid Hildenbrand 	max_ram_size = rnmax * storage_increment_size;
1543db880b6SDavid Hildenbrand 
1553db880b6SDavid Hildenbrand 	/* lowcore is always accessible, so the first increment is accessible */
1563db880b6SDavid Hildenbrand 	ram_size = storage_increment_size;
1573db880b6SDavid Hildenbrand 
1583db880b6SDavid Hildenbrand 	/* probe for r/w memory up to max memory size */
1593db880b6SDavid Hildenbrand 	while (ram_size < max_ram_size) {
1603db880b6SDavid Hildenbrand 		expect_pgm_int();
1613db880b6SDavid Hildenbrand 		cc = tprot(ram_size + storage_increment_size - 1);
1623db880b6SDavid Hildenbrand 		/* stop once we receive an exception or have protected memory */
1633db880b6SDavid Hildenbrand 		if (clear_pgm_int() || cc != 0)
1643db880b6SDavid Hildenbrand 			break;
1653db880b6SDavid Hildenbrand 		ram_size += storage_increment_size;
1663db880b6SDavid Hildenbrand 	}
16792bbd322SDavid Hildenbrand 
16892bbd322SDavid Hildenbrand 	mem_init(ram_size);
1693db880b6SDavid Hildenbrand }
170