1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #ifndef _ASM_S390_MEM_DETECT_H 3 #define _ASM_S390_MEM_DETECT_H 4 5 #include <linux/types.h> 6 #include <asm/page.h> 7 8 enum physmem_info_source { 9 MEM_DETECT_NONE = 0, 10 MEM_DETECT_SCLP_STOR_INFO, 11 MEM_DETECT_DIAG260, 12 MEM_DETECT_DIAG500_STOR_LIMIT, 13 MEM_DETECT_SCLP_READ_INFO, 14 MEM_DETECT_BIN_SEARCH 15 }; 16 17 struct physmem_range { 18 u64 start; 19 u64 end; 20 }; 21 22 enum reserved_range_type { 23 RR_DECOMPRESSOR, 24 RR_INITRD, 25 RR_VMLINUX, 26 RR_AMODE31, 27 RR_IPLREPORT, 28 RR_CERT_COMP_LIST, 29 RR_MEM_DETECT_EXTENDED, 30 RR_VMEM, 31 RR_MAX 32 }; 33 34 struct reserved_range { 35 unsigned long start; 36 unsigned long end; 37 struct reserved_range *chain; 38 }; 39 40 /* 41 * Storage element id is defined as 1 byte (up to 256 storage elements). 42 * In practise only storage element id 0 and 1 are used). 43 * According to architecture one storage element could have as much as 44 * 1020 subincrements. 255 physmem_ranges are embedded in physmem_info. 45 * If more physmem_ranges are required, a block of memory from already 46 * known physmem_range is taken (online_extended points to it). 47 */ 48 #define MEM_INLINED_ENTRIES 255 /* (PAGE_SIZE - 16) / 16 */ 49 50 struct physmem_info { 51 u32 range_count; 52 u8 info_source; 53 unsigned long usable; 54 struct reserved_range reserved[RR_MAX]; 55 struct physmem_range online[MEM_INLINED_ENTRIES]; 56 struct physmem_range *online_extended; 57 }; 58 59 extern struct physmem_info physmem_info; 60 61 void add_physmem_online_range(u64 start, u64 end); 62 63 static inline int __get_physmem_range(u32 n, unsigned long *start, 64 unsigned long *end, bool respect_usable_limit) 65 { 66 if (n >= physmem_info.range_count) { 67 *start = 0; 68 *end = 0; 69 return -1; 70 } 71 72 if (n < MEM_INLINED_ENTRIES) { 73 *start = (unsigned long)physmem_info.online[n].start; 74 *end = (unsigned long)physmem_info.online[n].end; 75 } else { 76 *start = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].start; 77 *end = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].end; 78 } 79 80 if (respect_usable_limit && physmem_info.usable) { 81 if (*start >= physmem_info.usable) 82 return -1; 83 if (*end > physmem_info.usable) 84 *end = physmem_info.usable; 85 } 86 return 0; 87 } 88 89 /** 90 * for_each_physmem_usable_range - early online memory range iterator 91 * @i: an integer used as loop variable 92 * @p_start: ptr to unsigned long for start address of the range 93 * @p_end: ptr to unsigned long for end address of the range 94 * 95 * Walks over detected online memory ranges below usable limit. 96 */ 97 #define for_each_physmem_usable_range(i, p_start, p_end) \ 98 for (i = 0; !__get_physmem_range(i, p_start, p_end, true); i++) 99 100 /* Walks over all detected online memory ranges disregarding usable limit. */ 101 #define for_each_physmem_online_range(i, p_start, p_end) \ 102 for (i = 0; !__get_physmem_range(i, p_start, p_end, false); i++) 103 104 static inline const char *get_physmem_info_source(void) 105 { 106 switch (physmem_info.info_source) { 107 case MEM_DETECT_SCLP_STOR_INFO: 108 return "sclp storage info"; 109 case MEM_DETECT_DIAG260: 110 return "diag260"; 111 case MEM_DETECT_DIAG500_STOR_LIMIT: 112 return "diag500 storage limit"; 113 case MEM_DETECT_SCLP_READ_INFO: 114 return "sclp read info"; 115 case MEM_DETECT_BIN_SEARCH: 116 return "binary search"; 117 } 118 return "none"; 119 } 120 121 #define RR_TYPE_NAME(t) case RR_ ## t: return #t 122 static inline const char *get_rr_type_name(enum reserved_range_type t) 123 { 124 switch (t) { 125 RR_TYPE_NAME(DECOMPRESSOR); 126 RR_TYPE_NAME(INITRD); 127 RR_TYPE_NAME(VMLINUX); 128 RR_TYPE_NAME(AMODE31); 129 RR_TYPE_NAME(IPLREPORT); 130 RR_TYPE_NAME(CERT_COMP_LIST); 131 RR_TYPE_NAME(MEM_DETECT_EXTENDED); 132 RR_TYPE_NAME(VMEM); 133 default: 134 return "UNKNOWN"; 135 } 136 } 137 138 #define for_each_physmem_reserved_type_range(t, range, p_start, p_end) \ 139 for (range = &physmem_info.reserved[t], *p_start = range->start, *p_end = range->end; \ 140 range && range->end; range = range->chain ? __va(range->chain) : NULL, \ 141 *p_start = range ? range->start : 0, *p_end = range ? range->end : 0) 142 143 static inline struct reserved_range *__physmem_reserved_next(enum reserved_range_type *t, 144 struct reserved_range *range) 145 { 146 if (!range) { 147 range = &physmem_info.reserved[*t]; 148 if (range->end) 149 return range; 150 } 151 if (range->chain) 152 return __va(range->chain); 153 while (++*t < RR_MAX) { 154 range = &physmem_info.reserved[*t]; 155 if (range->end) 156 return range; 157 } 158 return NULL; 159 } 160 161 #define for_each_physmem_reserved_range(t, range, p_start, p_end) \ 162 for (t = 0, range = __physmem_reserved_next(&t, NULL), \ 163 *p_start = range ? range->start : 0, *p_end = range ? range->end : 0; \ 164 range; range = __physmem_reserved_next(&t, range), \ 165 *p_start = range ? range->start : 0, *p_end = range ? range->end : 0) 166 167 static inline unsigned long get_physmem_reserved(enum reserved_range_type type, 168 unsigned long *addr, unsigned long *size) 169 { 170 *addr = physmem_info.reserved[type].start; 171 *size = physmem_info.reserved[type].end - physmem_info.reserved[type].start; 172 return *size; 173 } 174 175 #define AMODE31_START (physmem_info.reserved[RR_AMODE31].start) 176 #define AMODE31_END (physmem_info.reserved[RR_AMODE31].end) 177 178 #endif 179