diff options
Diffstat (limited to 'arch/s390/include/asm/physmem_info.h')
| -rw-r--r-- | arch/s390/include/asm/physmem_info.h | 171 | 
1 files changed, 171 insertions, 0 deletions
| diff --git a/arch/s390/include/asm/physmem_info.h b/arch/s390/include/asm/physmem_info.h new file mode 100644 index 000000000000..8e9c582592b3 --- /dev/null +++ b/arch/s390/include/asm/physmem_info.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_S390_MEM_DETECT_H +#define _ASM_S390_MEM_DETECT_H + +#include <linux/types.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_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,						\ +	     *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 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 | 
