18c37cb7dSVasily Gorbik /* SPDX-License-Identifier: GPL-2.0 */
28c37cb7dSVasily Gorbik #ifndef _ASM_S390_MEM_DETECT_H
38c37cb7dSVasily Gorbik #define _ASM_S390_MEM_DETECT_H
48c37cb7dSVasily Gorbik 
58c37cb7dSVasily Gorbik #include <linux/types.h>
6*c7050543SAlexander Gordeev #include <asm/page.h>
78c37cb7dSVasily Gorbik 
88c37cb7dSVasily Gorbik enum physmem_info_source {
98c37cb7dSVasily Gorbik 	MEM_DETECT_NONE = 0,
108c37cb7dSVasily Gorbik 	MEM_DETECT_SCLP_STOR_INFO,
118c37cb7dSVasily Gorbik 	MEM_DETECT_DIAG260,
128c37cb7dSVasily Gorbik 	MEM_DETECT_SCLP_READ_INFO,
138c37cb7dSVasily Gorbik 	MEM_DETECT_BIN_SEARCH
148c37cb7dSVasily Gorbik };
158c37cb7dSVasily Gorbik 
168c37cb7dSVasily Gorbik struct physmem_range {
178c37cb7dSVasily Gorbik 	u64 start;
188c37cb7dSVasily Gorbik 	u64 end;
198c37cb7dSVasily Gorbik };
208c37cb7dSVasily Gorbik 
21f913a660SVasily Gorbik enum reserved_range_type {
22f913a660SVasily Gorbik 	RR_DECOMPRESSOR,
23f913a660SVasily Gorbik 	RR_INITRD,
24f913a660SVasily Gorbik 	RR_VMLINUX,
25f913a660SVasily Gorbik 	RR_AMODE31,
26f913a660SVasily Gorbik 	RR_IPLREPORT,
27f913a660SVasily Gorbik 	RR_CERT_COMP_LIST,
28f913a660SVasily Gorbik 	RR_MEM_DETECT_EXTENDED,
29f913a660SVasily Gorbik 	RR_VMEM,
30f913a660SVasily Gorbik 	RR_MAX
31f913a660SVasily Gorbik };
32f913a660SVasily Gorbik 
33f913a660SVasily Gorbik struct reserved_range {
34f913a660SVasily Gorbik 	unsigned long start;
35f913a660SVasily Gorbik 	unsigned long end;
36f913a660SVasily Gorbik 	struct reserved_range *chain;
37f913a660SVasily Gorbik };
38f913a660SVasily Gorbik 
398c37cb7dSVasily Gorbik /*
408c37cb7dSVasily Gorbik  * Storage element id is defined as 1 byte (up to 256 storage elements).
418c37cb7dSVasily Gorbik  * In practise only storage element id 0 and 1 are used).
428c37cb7dSVasily Gorbik  * According to architecture one storage element could have as much as
438c37cb7dSVasily Gorbik  * 1020 subincrements. 255 physmem_ranges are embedded in physmem_info.
448c37cb7dSVasily Gorbik  * If more physmem_ranges are required, a block of memory from already
458c37cb7dSVasily Gorbik  * known physmem_range is taken (online_extended points to it).
468c37cb7dSVasily Gorbik  */
478c37cb7dSVasily Gorbik #define MEM_INLINED_ENTRIES 255 /* (PAGE_SIZE - 16) / 16 */
488c37cb7dSVasily Gorbik 
498c37cb7dSVasily Gorbik struct physmem_info {
508c37cb7dSVasily Gorbik 	u32 range_count;
518c37cb7dSVasily Gorbik 	u8 info_source;
528c37cb7dSVasily Gorbik 	unsigned long usable;
53f913a660SVasily Gorbik 	struct reserved_range reserved[RR_MAX];
548c37cb7dSVasily Gorbik 	struct physmem_range online[MEM_INLINED_ENTRIES];
558c37cb7dSVasily Gorbik 	struct physmem_range *online_extended;
568c37cb7dSVasily Gorbik };
578c37cb7dSVasily Gorbik 
588c37cb7dSVasily Gorbik extern struct physmem_info physmem_info;
598c37cb7dSVasily Gorbik 
608c37cb7dSVasily Gorbik void add_physmem_online_range(u64 start, u64 end);
618c37cb7dSVasily Gorbik 
__get_physmem_range(u32 n,unsigned long * start,unsigned long * end,bool respect_usable_limit)628c37cb7dSVasily Gorbik static inline int __get_physmem_range(u32 n, unsigned long *start,
638c37cb7dSVasily Gorbik 				      unsigned long *end, bool respect_usable_limit)
648c37cb7dSVasily Gorbik {
658c37cb7dSVasily Gorbik 	if (n >= physmem_info.range_count) {
668c37cb7dSVasily Gorbik 		*start = 0;
678c37cb7dSVasily Gorbik 		*end = 0;
688c37cb7dSVasily Gorbik 		return -1;
698c37cb7dSVasily Gorbik 	}
708c37cb7dSVasily Gorbik 
718c37cb7dSVasily Gorbik 	if (n < MEM_INLINED_ENTRIES) {
728c37cb7dSVasily Gorbik 		*start = (unsigned long)physmem_info.online[n].start;
738c37cb7dSVasily Gorbik 		*end = (unsigned long)physmem_info.online[n].end;
748c37cb7dSVasily Gorbik 	} else {
758c37cb7dSVasily Gorbik 		*start = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].start;
768c37cb7dSVasily Gorbik 		*end = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].end;
778c37cb7dSVasily Gorbik 	}
788c37cb7dSVasily Gorbik 
798c37cb7dSVasily Gorbik 	if (respect_usable_limit && physmem_info.usable) {
808c37cb7dSVasily Gorbik 		if (*start >= physmem_info.usable)
818c37cb7dSVasily Gorbik 			return -1;
828c37cb7dSVasily Gorbik 		if (*end > physmem_info.usable)
838c37cb7dSVasily Gorbik 			*end = physmem_info.usable;
848c37cb7dSVasily Gorbik 	}
858c37cb7dSVasily Gorbik 	return 0;
868c37cb7dSVasily Gorbik }
878c37cb7dSVasily Gorbik 
888c37cb7dSVasily Gorbik /**
898c37cb7dSVasily Gorbik  * for_each_physmem_usable_range - early online memory range iterator
908c37cb7dSVasily Gorbik  * @i: an integer used as loop variable
918c37cb7dSVasily Gorbik  * @p_start: ptr to unsigned long for start address of the range
928c37cb7dSVasily Gorbik  * @p_end: ptr to unsigned long for end address of the range
938c37cb7dSVasily Gorbik  *
948c37cb7dSVasily Gorbik  * Walks over detected online memory ranges below usable limit.
958c37cb7dSVasily Gorbik  */
968c37cb7dSVasily Gorbik #define for_each_physmem_usable_range(i, p_start, p_end)		\
978c37cb7dSVasily Gorbik 	for (i = 0; !__get_physmem_range(i, p_start, p_end, true); i++)
988c37cb7dSVasily Gorbik 
998c37cb7dSVasily Gorbik /* Walks over all detected online memory ranges disregarding usable limit. */
1008c37cb7dSVasily Gorbik #define for_each_physmem_online_range(i, p_start, p_end)		\
1018c37cb7dSVasily Gorbik 	for (i = 0; !__get_physmem_range(i, p_start, p_end, false); i++)
1028c37cb7dSVasily Gorbik 
get_physmem_info_source(void)103f913a660SVasily Gorbik static inline const char *get_physmem_info_source(void)
104f913a660SVasily Gorbik {
105f913a660SVasily Gorbik 	switch (physmem_info.info_source) {
106f913a660SVasily Gorbik 	case MEM_DETECT_SCLP_STOR_INFO:
107f913a660SVasily Gorbik 		return "sclp storage info";
108f913a660SVasily Gorbik 	case MEM_DETECT_DIAG260:
109f913a660SVasily Gorbik 		return "diag260";
110f913a660SVasily Gorbik 	case MEM_DETECT_SCLP_READ_INFO:
111f913a660SVasily Gorbik 		return "sclp read info";
112f913a660SVasily Gorbik 	case MEM_DETECT_BIN_SEARCH:
113f913a660SVasily Gorbik 		return "binary search";
114f913a660SVasily Gorbik 	}
115f913a660SVasily Gorbik 	return "none";
116f913a660SVasily Gorbik }
117f913a660SVasily Gorbik 
118f913a660SVasily Gorbik #define RR_TYPE_NAME(t) case RR_ ## t: return #t
get_rr_type_name(enum reserved_range_type t)119f913a660SVasily Gorbik static inline const char *get_rr_type_name(enum reserved_range_type t)
120f913a660SVasily Gorbik {
121f913a660SVasily Gorbik 	switch (t) {
122f913a660SVasily Gorbik 	RR_TYPE_NAME(DECOMPRESSOR);
123f913a660SVasily Gorbik 	RR_TYPE_NAME(INITRD);
124f913a660SVasily Gorbik 	RR_TYPE_NAME(VMLINUX);
125f913a660SVasily Gorbik 	RR_TYPE_NAME(AMODE31);
126f913a660SVasily Gorbik 	RR_TYPE_NAME(IPLREPORT);
127f913a660SVasily Gorbik 	RR_TYPE_NAME(CERT_COMP_LIST);
128f913a660SVasily Gorbik 	RR_TYPE_NAME(MEM_DETECT_EXTENDED);
129f913a660SVasily Gorbik 	RR_TYPE_NAME(VMEM);
130f913a660SVasily Gorbik 	default:
131f913a660SVasily Gorbik 		return "UNKNOWN";
132f913a660SVasily Gorbik 	}
133f913a660SVasily Gorbik }
134f913a660SVasily Gorbik 
135f913a660SVasily Gorbik #define for_each_physmem_reserved_type_range(t, range, p_start, p_end)				\
136f913a660SVasily Gorbik 	for (range = &physmem_info.reserved[t], *p_start = range->start, *p_end = range->end;	\
137*c7050543SAlexander Gordeev 	     range && range->end; range = range->chain ? __va(range->chain) : NULL,		\
138f913a660SVasily Gorbik 	     *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
139f913a660SVasily Gorbik 
__physmem_reserved_next(enum reserved_range_type * t,struct reserved_range * range)140f913a660SVasily Gorbik static inline struct reserved_range *__physmem_reserved_next(enum reserved_range_type *t,
141f913a660SVasily Gorbik 							     struct reserved_range *range)
142f913a660SVasily Gorbik {
143f913a660SVasily Gorbik 	if (!range) {
144f913a660SVasily Gorbik 		range = &physmem_info.reserved[*t];
145f913a660SVasily Gorbik 		if (range->end)
146f913a660SVasily Gorbik 			return range;
147f913a660SVasily Gorbik 	}
148f913a660SVasily Gorbik 	if (range->chain)
149*c7050543SAlexander Gordeev 		return __va(range->chain);
150f913a660SVasily Gorbik 	while (++*t < RR_MAX) {
151f913a660SVasily Gorbik 		range = &physmem_info.reserved[*t];
152f913a660SVasily Gorbik 		if (range->end)
153f913a660SVasily Gorbik 			return range;
154f913a660SVasily Gorbik 	}
155f913a660SVasily Gorbik 	return NULL;
156f913a660SVasily Gorbik }
157f913a660SVasily Gorbik 
158f913a660SVasily Gorbik #define for_each_physmem_reserved_range(t, range, p_start, p_end)			\
159f913a660SVasily Gorbik 	for (t = 0, range = __physmem_reserved_next(&t, NULL),			\
160f913a660SVasily Gorbik 	    *p_start = range ? range->start : 0, *p_end = range ? range->end : 0;	\
161f913a660SVasily Gorbik 	     range; range = __physmem_reserved_next(&t, range),			\
162f913a660SVasily Gorbik 	    *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
163f913a660SVasily Gorbik 
get_physmem_reserved(enum reserved_range_type type,unsigned long * addr,unsigned long * size)164f913a660SVasily Gorbik static inline unsigned long get_physmem_reserved(enum reserved_range_type type,
165f913a660SVasily Gorbik 						 unsigned long *addr, unsigned long *size)
1668c37cb7dSVasily Gorbik {
167f913a660SVasily Gorbik 	*addr = physmem_info.reserved[type].start;
168f913a660SVasily Gorbik 	*size = physmem_info.reserved[type].end - physmem_info.reserved[type].start;
169f913a660SVasily Gorbik 	return *size;
1708c37cb7dSVasily Gorbik }
1718c37cb7dSVasily Gorbik 
1728c37cb7dSVasily Gorbik #endif
173