diff options
author | Vasily Gorbik <gor@linux.ibm.com> | 2023-02-08 20:11:25 +0300 |
---|---|---|
committer | Heiko Carstens <hca@linux.ibm.com> | 2023-03-20 13:02:50 +0300 |
commit | 8c37cb7d4ffcc827a9484282691b018715a5ae1a (patch) | |
tree | e0c11f73e39a8a78ad3125ec34339639d62323b1 /arch/s390/boot/physmem_info.c | |
parent | 53fcc7dbf17691d8eac382ee315970a75286dd4b (diff) | |
download | linux-8c37cb7d4ffcc827a9484282691b018715a5ae1a.tar.xz |
s390/boot: rename mem_detect to physmem_info
In preparation to extending mem_detect with additional information like
reserved ranges rename it to more generic physmem_info. This new naming
also help to avoid confusion by using more exact terms like "physmem
online ranges", etc.
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Diffstat (limited to 'arch/s390/boot/physmem_info.c')
-rw-r--r-- | arch/s390/boot/physmem_info.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/arch/s390/boot/physmem_info.c b/arch/s390/boot/physmem_info.c new file mode 100644 index 000000000000..dc2e4d0abfab --- /dev/null +++ b/arch/s390/boot/physmem_info.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/errno.h> +#include <linux/init.h> +#include <asm/setup.h> +#include <asm/processor.h> +#include <asm/sclp.h> +#include <asm/sections.h> +#include <asm/physmem_info.h> +#include <asm/sparsemem.h> +#include "decompressor.h" +#include "boot.h" + +struct physmem_info __bootdata(physmem_info); + +/* up to 256 storage elements, 1020 subincrements each */ +#define ENTRIES_EXTENDED_MAX \ + (256 * (1020 / 2) * sizeof(struct physmem_range)) + +static struct physmem_range *__get_physmem_range_ptr(u32 n) +{ + if (n < MEM_INLINED_ENTRIES) + return &physmem_info.online[n]; + return &physmem_info.online_extended[n - MEM_INLINED_ENTRIES]; +} + +/* + * sequential calls to add_physmem_online_range with adjacent memory ranges + * are merged together into single memory range. + */ +void add_physmem_online_range(u64 start, u64 end) +{ + struct physmem_range *range; + + if (physmem_info.range_count) { + range = __get_physmem_range_ptr(physmem_info.range_count - 1); + if (range->end == start) { + range->end = end; + return; + } + } + + range = __get_physmem_range_ptr(physmem_info.range_count); + range->start = start; + range->end = end; + physmem_info.range_count++; +} + +static int __diag260(unsigned long rx1, unsigned long rx2) +{ + unsigned long reg1, reg2, ry; + union register_pair rx; + psw_t old; + int rc; + + rx.even = rx1; + rx.odd = rx2; + ry = 0x10; /* storage configuration */ + rc = -1; /* fail */ + asm volatile( + " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" + " epsw %[reg1],%[reg2]\n" + " st %[reg1],0(%[psw_pgm])\n" + " st %[reg2],4(%[psw_pgm])\n" + " larl %[reg1],1f\n" + " stg %[reg1],8(%[psw_pgm])\n" + " diag %[rx],%[ry],0x260\n" + " ipm %[rc]\n" + " srl %[rc],28\n" + "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" + : [reg1] "=&d" (reg1), + [reg2] "=&a" (reg2), + [rc] "+&d" (rc), + [ry] "+&d" (ry), + "+Q" (S390_lowcore.program_new_psw), + "=Q" (old) + : [rx] "d" (rx.pair), + [psw_old] "a" (&old), + [psw_pgm] "a" (&S390_lowcore.program_new_psw) + : "cc", "memory"); + return rc == 0 ? ry : -1; +} + +static int diag260(void) +{ + int rc, i; + + struct { + unsigned long start; + unsigned long end; + } storage_extents[8] __aligned(16); /* VM supports up to 8 extends */ + + memset(storage_extents, 0, sizeof(storage_extents)); + rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents)); + if (rc == -1) + return -1; + + for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++) + add_physmem_online_range(storage_extents[i].start, storage_extents[i].end + 1); + return 0; +} + +static int tprot(unsigned long addr) +{ + unsigned long reg1, reg2; + int rc = -EFAULT; + psw_t old; + + asm volatile( + " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" + " epsw %[reg1],%[reg2]\n" + " st %[reg1],0(%[psw_pgm])\n" + " st %[reg2],4(%[psw_pgm])\n" + " larl %[reg1],1f\n" + " stg %[reg1],8(%[psw_pgm])\n" + " tprot 0(%[addr]),0\n" + " ipm %[rc]\n" + " srl %[rc],28\n" + "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" + : [reg1] "=&d" (reg1), + [reg2] "=&a" (reg2), + [rc] "+&d" (rc), + "=Q" (S390_lowcore.program_new_psw.addr), + "=Q" (old) + : [psw_old] "a" (&old), + [psw_pgm] "a" (&S390_lowcore.program_new_psw), + [addr] "a" (addr) + : "cc", "memory"); + return rc; +} + +static unsigned long search_mem_end(void) +{ + unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */ + unsigned long offset = 0; + unsigned long pivot; + + while (range > 1) { + range >>= 1; + pivot = offset + range; + if (!tprot(pivot << 20)) + offset = pivot; + } + return (offset + 1) << 20; +} + +unsigned long detect_memory(unsigned long *safe_addr) +{ + unsigned long max_physmem_end = 0; + + sclp_early_get_memsize(&max_physmem_end); + physmem_info.online_extended = (struct physmem_range *)ALIGN(*safe_addr, sizeof(u64)); + + if (!sclp_early_read_storage_info()) { + physmem_info.info_source = MEM_DETECT_SCLP_STOR_INFO; + } else if (!diag260()) { + physmem_info.info_source = MEM_DETECT_DIAG260; + max_physmem_end = max_physmem_end ?: get_physmem_usable_end(); + } else if (max_physmem_end) { + add_physmem_online_range(0, max_physmem_end); + physmem_info.info_source = MEM_DETECT_SCLP_READ_INFO; + } else { + max_physmem_end = search_mem_end(); + add_physmem_online_range(0, max_physmem_end); + physmem_info.info_source = MEM_DETECT_BIN_SEARCH; + } + + if (physmem_info.range_count > MEM_INLINED_ENTRIES) { + *safe_addr += (physmem_info.range_count - MEM_INLINED_ENTRIES) * + sizeof(struct physmem_range); + } + + return max_physmem_end; +} + +void physmem_set_usable_limit(unsigned long limit) +{ + struct physmem_range *range; + int i; + + /* make sure mem_detect.usable ends up within online memory block */ + for (i = 0; i < physmem_info.range_count; i++) { + range = __get_physmem_range_ptr(i); + if (range->start >= limit) + break; + if (range->end >= limit) { + physmem_info.usable = limit; + break; + } + physmem_info.usable = range->end; + } +} |