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>
68c37cb7dSVasily Gorbik #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 };
20*f913a660SVasily Gorbik
21*f913a660SVasily Gorbik enum reserved_range_type {
22*f913a660SVasily Gorbik RR_DECOMPRESSOR,
23*f913a660SVasily Gorbik RR_INITRD,
24*f913a660SVasily Gorbik RR_VMLINUX,
25*f913a660SVasily Gorbik RR_AMODE31,
26*f913a660SVasily Gorbik RR_IPLREPORT,
27*f913a660SVasily Gorbik RR_CERT_COMP_LIST,
28*f913a660SVasily Gorbik RR_MEM_DETECT_EXTENDED,
29*f913a660SVasily Gorbik RR_VMEM,
30*f913a660SVasily Gorbik RR_MAX
31*f913a660SVasily Gorbik };
32*f913a660SVasily Gorbik
33*f913a660SVasily Gorbik struct reserved_range {
34*f913a660SVasily Gorbik unsigned long start;
35*f913a660SVasily Gorbik unsigned long end;
36*f913a660SVasily Gorbik struct reserved_range *chain;
37*f913a660SVasily Gorbik };
388c37cb7dSVasily 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;
52*f913a660SVasily Gorbik unsigned long usable;
538c37cb7dSVasily 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++)
102*f913a660SVasily Gorbik
get_physmem_info_source(void)103*f913a660SVasily Gorbik static inline const char *get_physmem_info_source(void)
104*f913a660SVasily Gorbik {
105*f913a660SVasily Gorbik switch (physmem_info.info_source) {
106*f913a660SVasily Gorbik case MEM_DETECT_SCLP_STOR_INFO:
107*f913a660SVasily Gorbik return "sclp storage info";
108*f913a660SVasily Gorbik case MEM_DETECT_DIAG260:
109*f913a660SVasily Gorbik return "diag260";
110*f913a660SVasily Gorbik case MEM_DETECT_SCLP_READ_INFO:
111*f913a660SVasily Gorbik return "sclp read info";
112*f913a660SVasily Gorbik case MEM_DETECT_BIN_SEARCH:
113*f913a660SVasily Gorbik return "binary search";
114*f913a660SVasily Gorbik }
115*f913a660SVasily Gorbik return "none";
116*f913a660SVasily Gorbik }
117*f913a660SVasily Gorbik
118*f913a660SVasily Gorbik #define RR_TYPE_NAME(t) case RR_ ## t: return #t
get_rr_type_name(enum reserved_range_type t)119*f913a660SVasily Gorbik static inline const char *get_rr_type_name(enum reserved_range_type t)
120*f913a660SVasily Gorbik {
121*f913a660SVasily Gorbik switch (t) {
122*f913a660SVasily Gorbik RR_TYPE_NAME(DECOMPRESSOR);
123*f913a660SVasily Gorbik RR_TYPE_NAME(INITRD);
124*f913a660SVasily Gorbik RR_TYPE_NAME(VMLINUX);
125*f913a660SVasily Gorbik RR_TYPE_NAME(AMODE31);
126*f913a660SVasily Gorbik RR_TYPE_NAME(IPLREPORT);
127*f913a660SVasily Gorbik RR_TYPE_NAME(CERT_COMP_LIST);
128*f913a660SVasily Gorbik RR_TYPE_NAME(MEM_DETECT_EXTENDED);
129*f913a660SVasily Gorbik RR_TYPE_NAME(VMEM);
130*f913a660SVasily Gorbik default:
131*f913a660SVasily Gorbik return "UNKNOWN";
132*f913a660SVasily Gorbik }
133*f913a660SVasily Gorbik }
134*f913a660SVasily Gorbik
135*f913a660SVasily Gorbik #define for_each_physmem_reserved_type_range(t, range, p_start, p_end) \
136*f913a660SVasily Gorbik for (range = &physmem_info.reserved[t], *p_start = range->start, *p_end = range->end; \
137*f913a660SVasily Gorbik range && range->end; range = range->chain ? __va(range->chain) : NULL, \
138*f913a660SVasily Gorbik *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
139*f913a660SVasily Gorbik
__physmem_reserved_next(enum reserved_range_type * t,struct reserved_range * range)140*f913a660SVasily Gorbik static inline struct reserved_range *__physmem_reserved_next(enum reserved_range_type *t,
141*f913a660SVasily Gorbik struct reserved_range *range)
142*f913a660SVasily Gorbik {
143*f913a660SVasily Gorbik if (!range) {
144*f913a660SVasily Gorbik range = &physmem_info.reserved[*t];
145*f913a660SVasily Gorbik if (range->end)
146*f913a660SVasily Gorbik return range;
147*f913a660SVasily Gorbik }
148*f913a660SVasily Gorbik if (range->chain)
149*f913a660SVasily Gorbik return __va(range->chain);
150*f913a660SVasily Gorbik while (++*t < RR_MAX) {
151*f913a660SVasily Gorbik range = &physmem_info.reserved[*t];
152*f913a660SVasily Gorbik if (range->end)
153*f913a660SVasily Gorbik return range;
154*f913a660SVasily Gorbik }
155*f913a660SVasily Gorbik return NULL;
156*f913a660SVasily Gorbik }
157*f913a660SVasily Gorbik
158*f913a660SVasily Gorbik #define for_each_physmem_reserved_range(t, range, p_start, p_end) \
159*f913a660SVasily Gorbik for (t = 0, range = __physmem_reserved_next(&t, NULL), \
160*f913a660SVasily Gorbik *p_start = range ? range->start : 0, *p_end = range ? range->end : 0; \
161*f913a660SVasily Gorbik range; range = __physmem_reserved_next(&t, range), \
162*f913a660SVasily Gorbik *p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
163*f913a660SVasily Gorbik
get_physmem_reserved(enum reserved_range_type type,unsigned long * addr,unsigned long * size)164*f913a660SVasily Gorbik static inline unsigned long get_physmem_reserved(enum reserved_range_type type,
1658c37cb7dSVasily Gorbik unsigned long *addr, unsigned long *size)
166*f913a660SVasily Gorbik {
167*f913a660SVasily Gorbik *addr = physmem_info.reserved[type].start;
168*f913a660SVasily Gorbik *size = physmem_info.reserved[type].end - physmem_info.reserved[type].start;
1698c37cb7dSVasily Gorbik return *size;
1708c37cb7dSVasily Gorbik }
1718c37cb7dSVasily Gorbik
172 #endif
173