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