Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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 /* up to 256 storage elements, 1020 subincrements each */
0016 #define ENTRIES_EXTENDED_MAX                               \
0017     (256 * (1020 / 2) * sizeof(struct mem_detect_block))
0018 
0019 /*
0020  * To avoid corrupting old kernel memory during dump, find lowest memory
0021  * chunk possible either right after the kernel end (decompressed kernel) or
0022  * after initrd (if it is present and there is no hole between the kernel end
0023  * and initrd)
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  * sequential calls to add_mem_detect_block with adjacent memory areas
0047  * are merged together into single memory block.
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; /* storage configuration */
0077     rc = -1;   /* fail */
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); /* VM supports up to 8 extends */
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); /* in 1MB blocks */
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 }