xref: /kvm-unit-tests/lib/s390x/sclp.c (revision 35bfd1c43957cac617d380f231b11fa746e60cb4)
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>
173db880b6SDavid Hildenbrand #include "sclp.h"
1892bbd322SDavid Hildenbrand #include <alloc_phys.h>
1992bbd322SDavid Hildenbrand 
2092bbd322SDavid Hildenbrand extern unsigned long stacktop;
213db880b6SDavid Hildenbrand 
223db880b6SDavid Hildenbrand static uint64_t storage_increment_size;
233db880b6SDavid Hildenbrand static uint64_t max_ram_size;
243db880b6SDavid Hildenbrand static uint64_t ram_size;
253db880b6SDavid Hildenbrand 
26*35bfd1c4SJanosch Frank char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096)));
27*35bfd1c4SJanosch Frank 
2892bbd322SDavid Hildenbrand static void mem_init(phys_addr_t mem_end)
2992bbd322SDavid Hildenbrand {
3092bbd322SDavid Hildenbrand 	phys_addr_t freemem_start = (phys_addr_t)&stacktop;
3192bbd322SDavid Hildenbrand 
3292bbd322SDavid Hildenbrand 	phys_alloc_init(freemem_start, mem_end - freemem_start);
3392bbd322SDavid Hildenbrand }
3492bbd322SDavid Hildenbrand 
35a45cb177SDavid Hildenbrand static void sclp_read_scp_info(ReadInfo *ri, int length)
36a45cb177SDavid Hildenbrand {
37a45cb177SDavid Hildenbrand 	unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED,
38a45cb177SDavid Hildenbrand 				    SCLP_CMDW_READ_SCP_INFO };
39a45cb177SDavid Hildenbrand 	int i;
40a45cb177SDavid Hildenbrand 
41a45cb177SDavid Hildenbrand 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
42a45cb177SDavid Hildenbrand 		memset(&ri->h, 0, sizeof(ri->h));
43a45cb177SDavid Hildenbrand 		ri->h.length = length;
44a45cb177SDavid Hildenbrand 
45a45cb177SDavid Hildenbrand 		if (sclp_service_call(commands[i], ri))
46a45cb177SDavid Hildenbrand 			break;
47a45cb177SDavid Hildenbrand 		if (ri->h.response_code == SCLP_RC_NORMAL_READ_COMPLETION)
48a45cb177SDavid Hildenbrand 			return;
49a45cb177SDavid Hildenbrand 		if (ri->h.response_code != SCLP_RC_INVALID_SCLP_COMMAND)
50a45cb177SDavid Hildenbrand 			break;
51a45cb177SDavid Hildenbrand 	}
52a45cb177SDavid Hildenbrand 	report_abort("READ_SCP_INFO failed");
53a45cb177SDavid Hildenbrand }
54a45cb177SDavid Hildenbrand 
55*35bfd1c4SJanosch Frank /* Perform service call. Return 0 on success, non-zero otherwise. */
56*35bfd1c4SJanosch Frank int sclp_service_call(unsigned int command, void *sccb)
57*35bfd1c4SJanosch Frank {
58*35bfd1c4SJanosch Frank 	int cc;
59*35bfd1c4SJanosch Frank 
60*35bfd1c4SJanosch Frank 	asm volatile(
61*35bfd1c4SJanosch Frank 		"       .insn   rre,0xb2200000,%1,%2\n"  /* servc %1,%2 */
62*35bfd1c4SJanosch Frank 		"       ipm     %0\n"
63*35bfd1c4SJanosch Frank 		"       srl     %0,28"
64*35bfd1c4SJanosch Frank 		: "=&d" (cc) : "d" (command), "a" (__pa(sccb))
65*35bfd1c4SJanosch Frank 		: "cc", "memory");
66*35bfd1c4SJanosch Frank 	if (cc == 3)
67*35bfd1c4SJanosch Frank 		return -1;
68*35bfd1c4SJanosch Frank 	if (cc == 2)
69*35bfd1c4SJanosch Frank 		return -1;
70*35bfd1c4SJanosch Frank 	return 0;
71*35bfd1c4SJanosch Frank }
72*35bfd1c4SJanosch Frank 
733db880b6SDavid Hildenbrand void sclp_memory_setup(void)
743db880b6SDavid Hildenbrand {
753db880b6SDavid Hildenbrand 	ReadInfo *ri = (void *)_sccb;
763db880b6SDavid Hildenbrand 	uint64_t rnmax, rnsize;
773db880b6SDavid Hildenbrand 	int cc;
783db880b6SDavid Hildenbrand 
79a45cb177SDavid Hildenbrand 	sclp_read_scp_info(ri, SCCB_SIZE);
803db880b6SDavid Hildenbrand 
813db880b6SDavid Hildenbrand 	/* calculate the storage increment size */
823db880b6SDavid Hildenbrand 	rnsize = ri->rnsize;
833db880b6SDavid Hildenbrand 	if (!rnsize) {
843db880b6SDavid Hildenbrand 		rnsize = ri->rnsize2;
853db880b6SDavid Hildenbrand 	}
863db880b6SDavid Hildenbrand 	storage_increment_size = rnsize << 20;
873db880b6SDavid Hildenbrand 
883db880b6SDavid Hildenbrand 	/* calculate the maximum memory size */
893db880b6SDavid Hildenbrand 	rnmax = ri->rnmax;
903db880b6SDavid Hildenbrand 	if (!rnmax) {
913db880b6SDavid Hildenbrand 		rnmax = ri->rnmax2;
923db880b6SDavid Hildenbrand 	}
933db880b6SDavid Hildenbrand 	max_ram_size = rnmax * storage_increment_size;
943db880b6SDavid Hildenbrand 
953db880b6SDavid Hildenbrand 	/* lowcore is always accessible, so the first increment is accessible */
963db880b6SDavid Hildenbrand 	ram_size = storage_increment_size;
973db880b6SDavid Hildenbrand 
983db880b6SDavid Hildenbrand 	/* probe for r/w memory up to max memory size */
993db880b6SDavid Hildenbrand 	while (ram_size < max_ram_size) {
1003db880b6SDavid Hildenbrand 		expect_pgm_int();
1013db880b6SDavid Hildenbrand 		cc = tprot(ram_size + storage_increment_size - 1);
1023db880b6SDavid Hildenbrand 		/* stop once we receive an exception or have protected memory */
1033db880b6SDavid Hildenbrand 		if (clear_pgm_int() || cc != 0)
1043db880b6SDavid Hildenbrand 			break;
1053db880b6SDavid Hildenbrand 		ram_size += storage_increment_size;
1063db880b6SDavid Hildenbrand 	}
10792bbd322SDavid Hildenbrand 
10892bbd322SDavid Hildenbrand 	mem_init(ram_size);
1093db880b6SDavid Hildenbrand }
110