aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/arch/s390/include/asm/physmem_info.h
blob: e747b067f8dbbf34c7ee0e3922c82325838d2a43 (plain) (blame)
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_MEM_DETECT_H
#define _ASM_S390_MEM_DETECT_H

#include <linux/types.h>
#include <asm/page.h>

enum physmem_info_source {
	MEM_DETECT_NONE = 0,
	MEM_DETECT_SCLP_STOR_INFO,
	MEM_DETECT_DIAG260,
	MEM_DETECT_SCLP_READ_INFO,
	MEM_DETECT_BIN_SEARCH
};

struct physmem_range {
	u64 start;
	u64 end;
};

enum reserved_range_type {
	RR_DECOMPRESSOR,
	RR_INITRD,
	RR_VMLINUX,
	RR_RELOC,
	RR_AMODE31,
	RR_IPLREPORT,
	RR_CERT_COMP_LIST,
	RR_MEM_DETECT_EXTENDED,
	RR_VMEM,
	RR_MAX
};

struct reserved_range {
	unsigned long start;
	unsigned long end;
	struct reserved_range *chain;
};

/*
 * Storage element id is defined as 1 byte (up to 256 storage elements).
 * In practise only storage element id 0 and 1 are used).
 * According to architecture one storage element could have as much as
 * 1020 subincrements. 255 physmem_ranges are embedded in physmem_info.
 * If more physmem_ranges are required, a block of memory from already
 * known physmem_range is taken (online_extended points to it).
 */
#define MEM_INLINED_ENTRIES 255 /* (PAGE_SIZE - 16) / 16 */

struct physmem_info {
	u32 range_count;
	u8 info_source;
	unsigned long usable;
	struct reserved_range reserved[RR_MAX];
	struct physmem_range online[MEM_INLINED_ENTRIES];
	struct physmem_range *online_extended;
};

extern struct physmem_info physmem_info;

void add_physmem_online_range(u64 start, u64 end);

static inline int __get_physmem_range(u32 n, unsigned long *start,
				      unsigned long *end, bool respect_usable_limit)
{
	if (n >= physmem_info.range_count) {
		*start = 0;
		*end = 0;
		return -1;
	}

	if (n < MEM_INLINED_ENTRIES) {
		*start = (unsigned long)physmem_info.online[n].start;
		*end = (unsigned long)physmem_info.online[n].end;
	} else {
		*start = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].start;
		*end = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].end;
	}

	if (respect_usable_limit && physmem_info.usable) {
		if (*start >= physmem_info.usable)
			return -1;
		if (*end > physmem_info.usable)
			*end = physmem_info.usable;
	}
	return 0;
}

/**
 * for_each_physmem_usable_range - early online memory range iterator
 * @i: an integer used as loop variable
 * @p_start: ptr to unsigned long for start address of the range
 * @p_end: ptr to unsigned long for end address of the range
 *
 * Walks over detected online memory ranges below usable limit.
 */
#define for_each_physmem_usable_range(i, p_start, p_end)		\
	for (i = 0; !__get_physmem_range(i, p_start, p_end, true); i++)

/* Walks over all detected online memory ranges disregarding usable limit. */
#define for_each_physmem_online_range(i, p_start, p_end)		\
	for (i = 0; !__get_physmem_range(i, p_start, p_end, false); i++)

static inline const char *get_physmem_info_source(void)
{
	switch (physmem_info.info_source) {
	case MEM_DETECT_SCLP_STOR_INFO:
		return "sclp storage info";
	case MEM_DETECT_DIAG260:
		return "diag260";
	case MEM_DETECT_SCLP_READ_INFO:
		return "sclp read info";
	case MEM_DETECT_BIN_SEARCH:
		return "binary search";
	}
	return "none";
}

#define RR_TYPE_NAME(t) case RR_ ## t: return #t
static inline const char *get_rr_type_name(enum reserved_range_type t)
{
	switch (t) {
	RR_TYPE_NAME(DECOMPRESSOR);
	RR_TYPE_NAME(INITRD);
	RR_TYPE_NAME(VMLINUX);
	RR_TYPE_NAME(AMODE31);
	RR_TYPE_NAME(IPLREPORT);
	RR_TYPE_NAME(CERT_COMP_LIST);
	RR_TYPE_NAME(MEM_DETECT_EXTENDED);
	RR_TYPE_NAME(VMEM);
	default:
		return "UNKNOWN";
	}
}

#define for_each_physmem_reserved_type_range(t, range, p_start, p_end)				\
	for (range = &physmem_info.reserved[t], *p_start = range->start, *p_end = range->end;	\
	     range && range->end; range = range->chain ? __va(range->chain) : NULL,		\
	     *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)

static inline struct reserved_range *__physmem_reserved_next(enum reserved_range_type *t,
							     struct reserved_range *range)
{
	if (!range) {
		range = &physmem_info.reserved[*t];
		if (range->end)
			return range;
	}
	if (range->chain)
		return __va(range->chain);
	while (++*t < RR_MAX) {
		range = &physmem_info.reserved[*t];
		if (range->end)
			return range;
	}
	return NULL;
}

#define for_each_physmem_reserved_range(t, range, p_start, p_end)			\
	for (t = 0, range = __physmem_reserved_next(&t, NULL),			\
	    *p_start = range ? range->start : 0, *p_end = range ? range->end : 0;	\
	     range; range = __physmem_reserved_next(&t, range),			\
	    *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)

static inline unsigned long get_physmem_reserved(enum reserved_range_type type,
						 unsigned long *addr, unsigned long *size)
{
	*addr = physmem_info.reserved[type].start;
	*size = physmem_info.reserved[type].end - physmem_info.reserved[type].start;
	return *size;
}

#endif