^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) #include <linux/errno.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) #include <asm/sclp.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) #include <asm/sections.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) #include <asm/mem_detect.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) #include <asm/sparsemem.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include "compressed/decompressor.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include "boot.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) unsigned long __bootdata(max_physmem_end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) struct mem_detect_info __bootdata(mem_detect);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) /* up to 256 storage elements, 1020 subincrements each */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #define ENTRIES_EXTENDED_MAX \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) (256 * (1020 / 2) * sizeof(struct mem_detect_block))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) * To avoid corrupting old kernel memory during dump, find lowest memory
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * chunk possible either right after the kernel end (decompressed kernel) or
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * after initrd (if it is present and there is no hole between the kernel end
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * and initrd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static void *mem_detect_alloc_extended(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) unsigned long offset = ALIGN(mem_safe_offset(), sizeof(u64));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && INITRD_START && INITRD_SIZE &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) INITRD_START < offset + ENTRIES_EXTENDED_MAX)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) offset = ALIGN(INITRD_START + INITRD_SIZE, sizeof(u64));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) return (void *)offset;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static struct mem_detect_block *__get_mem_detect_block_ptr(u32 n)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) if (n < MEM_INLINED_ENTRIES)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) return &mem_detect.entries[n];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) if (unlikely(!mem_detect.entries_extended))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) mem_detect.entries_extended = mem_detect_alloc_extended();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) return &mem_detect.entries_extended[n - MEM_INLINED_ENTRIES];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * sequential calls to add_mem_detect_block with adjacent memory areas
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) * are merged together into single memory block.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) void add_mem_detect_block(u64 start, u64 end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) struct mem_detect_block *block;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) if (mem_detect.count) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) block = __get_mem_detect_block_ptr(mem_detect.count - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (block->end == start) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) block->end = end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) block = __get_mem_detect_block_ptr(mem_detect.count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) block->start = start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) block->end = end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) mem_detect.count++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) static int __diag260(unsigned long rx1, unsigned long rx2)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) register unsigned long _rx1 asm("2") = rx1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) register unsigned long _rx2 asm("3") = rx2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) register unsigned long _ry asm("4") = 0x10; /* storage configuration */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) int rc = -1; /* fail */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) unsigned long reg1, reg2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) psw_t old;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) asm volatile(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) " epsw %0,%1\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) " st %0,0(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) " st %1,4(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) " larl %0,1f\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) " stg %0,8(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) " diag %[rx],%[ry],0x260\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) " ipm %[rc]\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) " srl %[rc],28\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) : "=&d" (reg1), "=&a" (reg2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) "+Q" (S390_lowcore.program_new_psw),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) "=Q" (old),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) [rc] "+&d" (rc), [ry] "+d" (_ry)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) : [rx] "d" (_rx1), "d" (_rx2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) [psw_old] "a" (&old),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) [psw_pgm] "a" (&S390_lowcore.program_new_psw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) : "cc", "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) return rc == 0 ? _ry : -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) static int diag260(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) int rc, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) struct {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) unsigned long start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) unsigned long end;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) } storage_extents[8] __aligned(16); /* VM supports up to 8 extends */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) memset(storage_extents, 0, sizeof(storage_extents));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (rc == -1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) return -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) add_mem_detect_block(storage_extents[i].start, storage_extents[i].end + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) static int tprot(unsigned long addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) unsigned long reg1, reg2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) int rc = -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) psw_t old;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) asm volatile(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) " epsw %[reg1],%[reg2]\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) " st %[reg1],0(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) " st %[reg2],4(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) " larl %[reg1],1f\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) " stg %[reg1],8(%[psw_pgm])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) " tprot 0(%[addr]),0\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) " ipm %[rc]\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) " srl %[rc],28\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) : [reg1] "=&d" (reg1),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) [reg2] "=&a" (reg2),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) [rc] "+&d" (rc),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) "=Q" (S390_lowcore.program_new_psw.addr),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) "=Q" (old)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) : [psw_old] "a" (&old),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) [psw_pgm] "a" (&S390_lowcore.program_new_psw),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) [addr] "a" (addr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) : "cc", "memory");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) return rc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) static void search_mem_end(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) unsigned long offset = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) unsigned long pivot;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) while (range > 1) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) range >>= 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) pivot = offset + range;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (!tprot(pivot << 20))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) offset = pivot;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) add_mem_detect_block(0, (offset + 1) << 20);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) void detect_memory(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) sclp_early_get_memsize(&max_physmem_end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) if (!sclp_early_read_storage_info()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) mem_detect.info_source = MEM_DETECT_SCLP_STOR_INFO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (!diag260()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) mem_detect.info_source = MEM_DETECT_DIAG260;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (max_physmem_end) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) add_mem_detect_block(0, max_physmem_end);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) mem_detect.info_source = MEM_DETECT_SCLP_READ_INFO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) search_mem_end();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) mem_detect.info_source = MEM_DETECT_BIN_SEARCH;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) max_physmem_end = get_mem_detect_end();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }