summaryrefslogtreecommitdiff
path: root/drivers/s390/char/sclp_info.c
blob: a1136e052750565803b90ac478d69d3e46a70218 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 *  drivers/s390/char/sclp_info.c
 *
 *    Copyright IBM Corp. 2007
 *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
 */

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <asm/sclp.h>
#include "sclp.h"

struct sclp_readinfo_sccb {
	struct	sccb_header header;	/* 0-7 */
	u16	rnmax;			/* 8-9 */
	u8	rnsize;			/* 10 */
	u8	_reserved0[24 - 11];	/* 11-23 */
	u8	loadparm[8];		/* 24-31 */
	u8	_reserved1[48 - 32];	/* 32-47 */
	u64	facilities;		/* 48-55 */
	u8	_reserved2[91 - 56];	/* 56-90 */
	u8	flags;			/* 91 */
	u8	_reserved3[100 - 92];	/* 92-99 */
	u32	rnsize2;		/* 100-103 */
	u64	rnmax2;			/* 104-111 */
	u8	_reserved4[4096 - 112];	/* 112-4095 */
} __attribute__((packed, aligned(4096)));

static struct sclp_readinfo_sccb __initdata early_readinfo_sccb;
static int __initdata early_readinfo_sccb_valid;

u64 sclp_facilities;

void __init sclp_readinfo_early(void)
{
	int ret;
	int i;
	struct sclp_readinfo_sccb *sccb;
	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
				  SCLP_CMDW_READ_SCP_INFO};

	/* Enable service signal subclass mask. */
	__ctl_set_bit(0, 9);
	sccb = &early_readinfo_sccb;
	for (i = 0; i < ARRAY_SIZE(commands); i++) {
		do {
			memset(sccb, 0, sizeof(*sccb));
			sccb->header.length = sizeof(*sccb);
			sccb->header.control_mask[2] = 0x80;
			ret = sclp_service_call(commands[i], sccb);
		} while (ret == -EBUSY);

		if (ret)
			break;
		__load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
				PSW_MASK_WAIT | PSW_DEFAULT_KEY);
		local_irq_disable();
		/*
		 * Contents of the sccb might have changed
		 * therefore a barrier is needed.
		 */
		barrier();
		if (sccb->header.response_code == 0x10) {
			early_readinfo_sccb_valid = 1;
			break;
		}
		if (sccb->header.response_code != 0x1f0)
			break;
	}
	/* Disable service signal subclass mask again. */
	__ctl_clear_bit(0, 9);
}

void __init sclp_facilities_detect(void)
{
	if (!early_readinfo_sccb_valid)
		return;
	sclp_facilities = early_readinfo_sccb.facilities;
}

unsigned long long __init sclp_memory_detect(void)
{
	unsigned long long memsize;
	struct sclp_readinfo_sccb *sccb;

	if (!early_readinfo_sccb_valid)
		return 0;
	sccb = &early_readinfo_sccb;
	if (sccb->rnsize)
		memsize = sccb->rnsize << 20;
	else
		memsize = sccb->rnsize2 << 20;
	if (sccb->rnmax)
		memsize *= sccb->rnmax;
	else
		memsize *= sccb->rnmax2;
	return memsize;
}

/*
 * This function will be called after sclp_memory_detect(), which gets called
 * early from early.c code. Therefore the sccb should have valid contents.
 */
void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
{
	struct sclp_readinfo_sccb *sccb;

	if (!early_readinfo_sccb_valid)
		return;
	sccb = &early_readinfo_sccb;
	info->is_valid = 1;
	if (sccb->flags & 0x2)
		info->has_dump = 1;
	memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
}