0001
0002 #include <linux/errno.h>
0003 #include <linux/init.h>
0004 #include <asm/setup.h>
0005 #include <asm/processor.h>
0006 #include <asm/sclp.h>
0007 #include <asm/sections.h>
0008 #include <asm/mem_detect.h>
0009 #include <asm/sparsemem.h>
0010 #include "decompressor.h"
0011 #include "boot.h"
0012
0013 struct mem_detect_info __bootdata(mem_detect);
0014
0015
0016 #define ENTRIES_EXTENDED_MAX \
0017 (256 * (1020 / 2) * sizeof(struct mem_detect_block))
0018
0019
0020
0021
0022
0023
0024
0025 static void *mem_detect_alloc_extended(void)
0026 {
0027 unsigned long offset = ALIGN(mem_safe_offset(), sizeof(u64));
0028
0029 if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_data.start && initrd_data.size &&
0030 initrd_data.start < offset + ENTRIES_EXTENDED_MAX)
0031 offset = ALIGN(initrd_data.start + initrd_data.size, sizeof(u64));
0032
0033 return (void *)offset;
0034 }
0035
0036 static struct mem_detect_block *__get_mem_detect_block_ptr(u32 n)
0037 {
0038 if (n < MEM_INLINED_ENTRIES)
0039 return &mem_detect.entries[n];
0040 if (unlikely(!mem_detect.entries_extended))
0041 mem_detect.entries_extended = mem_detect_alloc_extended();
0042 return &mem_detect.entries_extended[n - MEM_INLINED_ENTRIES];
0043 }
0044
0045
0046
0047
0048
0049 void add_mem_detect_block(u64 start, u64 end)
0050 {
0051 struct mem_detect_block *block;
0052
0053 if (mem_detect.count) {
0054 block = __get_mem_detect_block_ptr(mem_detect.count - 1);
0055 if (block->end == start) {
0056 block->end = end;
0057 return;
0058 }
0059 }
0060
0061 block = __get_mem_detect_block_ptr(mem_detect.count);
0062 block->start = start;
0063 block->end = end;
0064 mem_detect.count++;
0065 }
0066
0067 static int __diag260(unsigned long rx1, unsigned long rx2)
0068 {
0069 unsigned long reg1, reg2, ry;
0070 union register_pair rx;
0071 psw_t old;
0072 int rc;
0073
0074 rx.even = rx1;
0075 rx.odd = rx2;
0076 ry = 0x10;
0077 rc = -1;
0078 asm volatile(
0079 " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
0080 " epsw %[reg1],%[reg2]\n"
0081 " st %[reg1],0(%[psw_pgm])\n"
0082 " st %[reg2],4(%[psw_pgm])\n"
0083 " larl %[reg1],1f\n"
0084 " stg %[reg1],8(%[psw_pgm])\n"
0085 " diag %[rx],%[ry],0x260\n"
0086 " ipm %[rc]\n"
0087 " srl %[rc],28\n"
0088 "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
0089 : [reg1] "=&d" (reg1),
0090 [reg2] "=&a" (reg2),
0091 [rc] "+&d" (rc),
0092 [ry] "+&d" (ry),
0093 "+Q" (S390_lowcore.program_new_psw),
0094 "=Q" (old)
0095 : [rx] "d" (rx.pair),
0096 [psw_old] "a" (&old),
0097 [psw_pgm] "a" (&S390_lowcore.program_new_psw)
0098 : "cc", "memory");
0099 return rc == 0 ? ry : -1;
0100 }
0101
0102 static int diag260(void)
0103 {
0104 int rc, i;
0105
0106 struct {
0107 unsigned long start;
0108 unsigned long end;
0109 } storage_extents[8] __aligned(16);
0110
0111 memset(storage_extents, 0, sizeof(storage_extents));
0112 rc = __diag260((unsigned long)storage_extents, sizeof(storage_extents));
0113 if (rc == -1)
0114 return -1;
0115
0116 for (i = 0; i < min_t(int, rc, ARRAY_SIZE(storage_extents)); i++)
0117 add_mem_detect_block(storage_extents[i].start, storage_extents[i].end + 1);
0118 return 0;
0119 }
0120
0121 static int tprot(unsigned long addr)
0122 {
0123 unsigned long reg1, reg2;
0124 int rc = -EFAULT;
0125 psw_t old;
0126
0127 asm volatile(
0128 " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
0129 " epsw %[reg1],%[reg2]\n"
0130 " st %[reg1],0(%[psw_pgm])\n"
0131 " st %[reg2],4(%[psw_pgm])\n"
0132 " larl %[reg1],1f\n"
0133 " stg %[reg1],8(%[psw_pgm])\n"
0134 " tprot 0(%[addr]),0\n"
0135 " ipm %[rc]\n"
0136 " srl %[rc],28\n"
0137 "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
0138 : [reg1] "=&d" (reg1),
0139 [reg2] "=&a" (reg2),
0140 [rc] "+&d" (rc),
0141 "=Q" (S390_lowcore.program_new_psw.addr),
0142 "=Q" (old)
0143 : [psw_old] "a" (&old),
0144 [psw_pgm] "a" (&S390_lowcore.program_new_psw),
0145 [addr] "a" (addr)
0146 : "cc", "memory");
0147 return rc;
0148 }
0149
0150 static void search_mem_end(void)
0151 {
0152 unsigned long range = 1 << (MAX_PHYSMEM_BITS - 20);
0153 unsigned long offset = 0;
0154 unsigned long pivot;
0155
0156 while (range > 1) {
0157 range >>= 1;
0158 pivot = offset + range;
0159 if (!tprot(pivot << 20))
0160 offset = pivot;
0161 }
0162
0163 add_mem_detect_block(0, (offset + 1) << 20);
0164 }
0165
0166 unsigned long detect_memory(void)
0167 {
0168 unsigned long max_physmem_end;
0169
0170 sclp_early_get_memsize(&max_physmem_end);
0171
0172 if (!sclp_early_read_storage_info()) {
0173 mem_detect.info_source = MEM_DETECT_SCLP_STOR_INFO;
0174 return max_physmem_end;
0175 }
0176
0177 if (!diag260()) {
0178 mem_detect.info_source = MEM_DETECT_DIAG260;
0179 return max_physmem_end;
0180 }
0181
0182 if (max_physmem_end) {
0183 add_mem_detect_block(0, max_physmem_end);
0184 mem_detect.info_source = MEM_DETECT_SCLP_READ_INFO;
0185 return max_physmem_end;
0186 }
0187
0188 search_mem_end();
0189 mem_detect.info_source = MEM_DETECT_BIN_SEARCH;
0190 return get_mem_detect_end();
0191 }