1*8c37cb7dSVasily Gorbik // SPDX-License-Identifier: GPL-2.0 2*8c37cb7dSVasily Gorbik #include <linux/errno.h> 3*8c37cb7dSVasily Gorbik #include <linux/init.h> 4*8c37cb7dSVasily Gorbik #include <asm/setup.h> 5*8c37cb7dSVasily Gorbik #include <asm/processor.h> 6*8c37cb7dSVasily Gorbik #include <asm/sclp.h> 7*8c37cb7dSVasily Gorbik #include <asm/sections.h> 8*8c37cb7dSVasily Gorbik #include <asm/physmem_info.h> 9*8c37cb7dSVasily Gorbik #include <asm/sparsemem.h> 10*8c37cb7dSVasily Gorbik #include "decompressor.h" 11*8c37cb7dSVasily Gorbik #include "boot.h" 12*8c37cb7dSVasily Gorbik 13*8c37cb7dSVasily Gorbik struct physmem_info __bootdata(physmem_info); 14*8c37cb7dSVasily Gorbik 15*8c37cb7dSVasily Gorbik /* up to 256 storage elements, 1020 subincrements each */ 16*8c37cb7dSVasily Gorbik #define ENTRIES_EXTENDED_MAX \ 17*8c37cb7dSVasily Gorbik (256 * (1020 / 2) * sizeof(struct physmem_range)) 18*8c37cb7dSVasily Gorbik 19*8c37cb7dSVasily Gorbik static struct physmem_range *__get_physmem_range_ptr(u32 n) 20*8c37cb7dSVasily Gorbik { 21*8c37cb7dSVasily Gorbik if (n < MEM_INLINED_ENTRIES) 22*8c37cb7dSVasily Gorbik return &physmem_info.online[n]; 23*8c37cb7dSVasily Gorbik return &physmem_info.online_extended[n - MEM_INLINED_ENTRIES]; 24*8c37cb7dSVasily Gorbik } 25*8c37cb7dSVasily Gorbik 26*8c37cb7dSVasily Gorbik /* 27*8c37cb7dSVasily Gorbik * sequential calls to add_physmem_online_range with adjacent memory ranges 28*8c37cb7dSVasily Gorbik * are merged together into single memory range. 29*8c37cb7dSVasily Gorbik */ 30*8c37cb7dSVasily Gorbik void add_physmem_online_range(u64 start, u64 end) 31*8c37cb7dSVasily Gorbik { 32*8c37cb7dSVasily Gorbik struct physmem_range *range; 33*8c37cb7dSVasily Gorbik 34*8c37cb7dSVasily Gorbik if (physmem_info.range_count) { 35*8c37cb7dSVasily Gorbik range = __get_physmem_range_ptr(physmem_info.range_count - 1); 36*8c37cb7dSVasily Gorbik if (range->end == start) { 37*8c37cb7dSVasily Gorbik range->end = end; 38*8c37cb7dSVasily Gorbik return; 39*8c37cb7dSVasily Gorbik } 40*8c37cb7dSVasily Gorbik } 41*8c37cb7dSVasily Gorbik 42*8c37cb7dSVasily Gorbik range = __get_physmem_range_ptr(physmem_info.range_count); 43*8c37cb7dSVasily Gorbik range->start = start; 44*8c37cb7dSVasily Gorbik range->end = end; 45*8c37cb7dSVasily Gorbik physmem_info.range_count++; 46*8c37cb7dSVasily Gorbik } 47*8c37cb7dSVasily Gorbik 48*8c37cb7dSVasily Gorbik static int __diag260(unsigned long rx1, unsigned long rx2) 49*8c37cb7dSVasily Gorbik { 50*8c37cb7dSVasily Gorbik unsigned long reg1, reg2, ry; 51*8c37cb7dSVasily Gorbik union register_pair rx; 52*8c37cb7dSVasily Gorbik psw_t old; 53*8c37cb7dSVasily Gorbik int rc; 54*8c37cb7dSVasily Gorbik 55*8c37cb7dSVasily Gorbik rx.even = rx1; 56*8c37cb7dSVasily Gorbik rx.odd = rx2; 57*8c37cb7dSVasily Gorbik ry = 0x10; /* storage configuration */ 58*8c37cb7dSVasily Gorbik rc = -1; /* fail */ 59*8c37cb7dSVasily Gorbik asm volatile( 60*8c37cb7dSVasily Gorbik " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" 61*8c37cb7dSVasily Gorbik " epsw %[reg1],%[reg2]\n" 62*8c37cb7dSVasily Gorbik " st %[reg1],0(%[psw_pgm])\n" 63*8c37cb7dSVasily Gorbik " st %[reg2],4(%[psw_pgm])\n" 64*8c37cb7dSVasily Gorbik " larl %[reg1],1f\n" 65*8c37cb7dSVasily Gorbik " stg %[reg1],8(%[psw_pgm])\n" 66*8c37cb7dSVasily Gorbik " diag %[rx],%[ry],0x260\n" 67*8c37cb7dSVasily Gorbik " ipm %[rc]\n" 68*8c37cb7dSVasily Gorbik " srl %[rc],28\n" 69*8c37cb7dSVasily Gorbik "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" 70*8c37cb7dSVasily Gorbik : [reg1] "=&d" (reg1), 71*8c37cb7dSVasily Gorbik [reg2] "=&a" (reg2), 72*8c37cb7dSVasily Gorbik [rc] "+&d" (rc), 73*8c37cb7dSVasily Gorbik [ry] "+&d" (ry), 74*8c37cb7dSVasily Gorbik "+Q" (S390_lowcore.program_new_psw), 75*8c37cb7dSVasily Gorbik "=Q" (old) 76*8c37cb7dSVasily Gorbik : [rx] "d" (rx.pair), 77*8c37cb7dSVasily Gorbik [psw_old] "a" (&old), 78*8c37cb7dSVasily Gorbik [psw_pgm] "a" (&S390_lowcore.program_new_psw) 79*8c37cb7dSVasily Gorbik : "cc", "memory"); 80*8c37cb7dSVasily Gorbik return rc == 0 ? ry : -1; 81*8c37cb7dSVasily Gorbik } 82*8c37cb7dSVasily Gorbik 83*8c37cb7dSVasily Gorbik static int diag260(void) 84*8c37cb7dSVasily Gorbik { 85*8c37cb7dSVasily Gorbik int rc, i; 86*8c37cb7dSVasily Gorbik 87*8c37cb7dSVasily Gorbik struct { 88*8c37cb7dSVasily Gorbik unsigned long start; 89*8c37cb7dSVasily Gorbik unsigned long end; 90*8c37cb7dSVasily Gorbik } storage_extents[8] __aligned(16); /* VM supports up to 8 extends */ 91*8c37cb7dSVasily Gorbik 92*8c37cb7dSVasily Gorbik memset(storage_extents, 0, sizeof(storage_extents)); 93*8c37cb7dSVasily Gorbik rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents)); 94*8c37cb7dSVasily Gorbik if (rc == -1) 95*8c37cb7dSVasily Gorbik return -1; 96*8c37cb7dSVasily Gorbik 97*8c37cb7dSVasily Gorbik for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++) 98*8c37cb7dSVasily Gorbik add_physmem_online_range(storage_extents[i].start, storage_extents[i].end + 1); 99*8c37cb7dSVasily Gorbik return 0; 100*8c37cb7dSVasily Gorbik } 101*8c37cb7dSVasily Gorbik 102*8c37cb7dSVasily Gorbik static int tprot(unsigned long addr) 103*8c37cb7dSVasily Gorbik { 104*8c37cb7dSVasily Gorbik unsigned long reg1, reg2; 105*8c37cb7dSVasily Gorbik int rc = -EFAULT; 106*8c37cb7dSVasily Gorbik psw_t old; 107*8c37cb7dSVasily Gorbik 108*8c37cb7dSVasily Gorbik asm volatile( 109*8c37cb7dSVasily Gorbik " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n" 110*8c37cb7dSVasily Gorbik " epsw %[reg1],%[reg2]\n" 111*8c37cb7dSVasily Gorbik " st %[reg1],0(%[psw_pgm])\n" 112*8c37cb7dSVasily Gorbik " st %[reg2],4(%[psw_pgm])\n" 113*8c37cb7dSVasily Gorbik " larl %[reg1],1f\n" 114*8c37cb7dSVasily Gorbik " stg %[reg1],8(%[psw_pgm])\n" 115*8c37cb7dSVasily Gorbik " tprot 0(%[addr]),0\n" 116*8c37cb7dSVasily Gorbik " ipm %[rc]\n" 117*8c37cb7dSVasily Gorbik " srl %[rc],28\n" 118*8c37cb7dSVasily Gorbik "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n" 119*8c37cb7dSVasily Gorbik : [reg1] "=&d" (reg1), 120*8c37cb7dSVasily Gorbik [reg2] "=&a" (reg2), 121*8c37cb7dSVasily Gorbik [rc] "+&d" (rc), 122*8c37cb7dSVasily Gorbik "=Q" (S390_lowcore.program_new_psw.addr), 123*8c37cb7dSVasily Gorbik "=Q" (old) 124*8c37cb7dSVasily Gorbik : [psw_old] "a" (&old), 125*8c37cb7dSVasily Gorbik [psw_pgm] "a" (&S390_lowcore.program_new_psw), 126*8c37cb7dSVasily Gorbik [addr] "a" (addr) 127*8c37cb7dSVasily Gorbik : "cc", "memory"); 128*8c37cb7dSVasily Gorbik return rc; 129*8c37cb7dSVasily Gorbik } 130*8c37cb7dSVasily Gorbik 131*8c37cb7dSVasily Gorbik static unsigned long search_mem_end(void) 132*8c37cb7dSVasily Gorbik { 133*8c37cb7dSVasily Gorbik unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20); /* in 1MB blocks */ 134*8c37cb7dSVasily Gorbik unsigned long offset = 0; 135*8c37cb7dSVasily Gorbik unsigned long pivot; 136*8c37cb7dSVasily Gorbik 137*8c37cb7dSVasily Gorbik while (range > 1) { 138*8c37cb7dSVasily Gorbik range >>= 1; 139*8c37cb7dSVasily Gorbik pivot = offset + range; 140*8c37cb7dSVasily Gorbik if (!tprot(pivot << 20)) 141*8c37cb7dSVasily Gorbik offset = pivot; 142*8c37cb7dSVasily Gorbik } 143*8c37cb7dSVasily Gorbik return (offset + 1) << 20; 144*8c37cb7dSVasily Gorbik } 145*8c37cb7dSVasily Gorbik 146*8c37cb7dSVasily Gorbik unsigned long detect_memory(unsigned long *safe_addr) 147*8c37cb7dSVasily Gorbik { 148*8c37cb7dSVasily Gorbik unsigned long max_physmem_end = 0; 149*8c37cb7dSVasily Gorbik 150*8c37cb7dSVasily Gorbik sclp_early_get_memsize(&max_physmem_end); 151*8c37cb7dSVasily Gorbik physmem_info.online_extended = (struct physmem_range *)ALIGN(*safe_addr, sizeof(u64)); 152*8c37cb7dSVasily Gorbik 153*8c37cb7dSVasily Gorbik if (!sclp_early_read_storage_info()) { 154*8c37cb7dSVasily Gorbik physmem_info.info_source = MEM_DETECT_SCLP_STOR_INFO; 155*8c37cb7dSVasily Gorbik } else if (!diag260()) { 156*8c37cb7dSVasily Gorbik physmem_info.info_source = MEM_DETECT_DIAG260; 157*8c37cb7dSVasily Gorbik max_physmem_end = max_physmem_end ?: get_physmem_usable_end(); 158*8c37cb7dSVasily Gorbik } else if (max_physmem_end) { 159*8c37cb7dSVasily Gorbik add_physmem_online_range(0, max_physmem_end); 160*8c37cb7dSVasily Gorbik physmem_info.info_source = MEM_DETECT_SCLP_READ_INFO; 161*8c37cb7dSVasily Gorbik } else { 162*8c37cb7dSVasily Gorbik max_physmem_end = search_mem_end(); 163*8c37cb7dSVasily Gorbik add_physmem_online_range(0, max_physmem_end); 164*8c37cb7dSVasily Gorbik physmem_info.info_source = MEM_DETECT_BIN_SEARCH; 165*8c37cb7dSVasily Gorbik } 166*8c37cb7dSVasily Gorbik 167*8c37cb7dSVasily Gorbik if (physmem_info.range_count > MEM_INLINED_ENTRIES) { 168*8c37cb7dSVasily Gorbik *safe_addr += (physmem_info.range_count - MEM_INLINED_ENTRIES) * 169*8c37cb7dSVasily Gorbik sizeof(struct physmem_range); 170*8c37cb7dSVasily Gorbik } 171*8c37cb7dSVasily Gorbik 172*8c37cb7dSVasily Gorbik return max_physmem_end; 173*8c37cb7dSVasily Gorbik } 174*8c37cb7dSVasily Gorbik 175*8c37cb7dSVasily Gorbik void physmem_set_usable_limit(unsigned long limit) 176*8c37cb7dSVasily Gorbik { 177*8c37cb7dSVasily Gorbik struct physmem_range *range; 178*8c37cb7dSVasily Gorbik int i; 179*8c37cb7dSVasily Gorbik 180*8c37cb7dSVasily Gorbik /* make sure mem_detect.usable ends up within online memory block */ 181*8c37cb7dSVasily Gorbik for (i = 0; i < physmem_info.range_count; i++) { 182*8c37cb7dSVasily Gorbik range = __get_physmem_range_ptr(i); 183*8c37cb7dSVasily Gorbik if (range->start >= limit) 184*8c37cb7dSVasily Gorbik break; 185*8c37cb7dSVasily Gorbik if (range->end >= limit) { 186*8c37cb7dSVasily Gorbik physmem_info.usable = limit; 187*8c37cb7dSVasily Gorbik break; 188*8c37cb7dSVasily Gorbik } 189*8c37cb7dSVasily Gorbik physmem_info.usable = range->end; 190*8c37cb7dSVasily Gorbik } 191*8c37cb7dSVasily Gorbik } 192