1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/errno.h>
3 #include <linux/init.h>
5 #include <asm/processor.h>
7 #include <asm/sections.h>
8 #include <asm/mem_detect.h>
9 #include <asm/sparsemem.h>
10 #include "decompressor.h"
13 struct mem_detect_info
__bootdata(mem_detect
);
15 /* up to 256 storage elements, 1020 subincrements each */
16 #define ENTRIES_EXTENDED_MAX \
17 (256 * (1020 / 2) * sizeof(struct mem_detect_block))
19 static struct mem_detect_block
*__get_mem_detect_block_ptr(u32 n
)
21 if (n
< MEM_INLINED_ENTRIES
)
22 return &mem_detect
.entries
[n
];
23 return &mem_detect
.entries_extended
[n
- MEM_INLINED_ENTRIES
];
27 * sequential calls to add_mem_detect_block with adjacent memory areas
28 * are merged together into single memory block.
30 void add_mem_detect_block(u64 start
, u64 end
)
32 struct mem_detect_block
*block
;
34 if (mem_detect
.count
) {
35 block
= __get_mem_detect_block_ptr(mem_detect
.count
- 1);
36 if (block
->end
== start
) {
42 block
= __get_mem_detect_block_ptr(mem_detect
.count
);
48 static int __diag260(unsigned long rx1
, unsigned long rx2
)
50 unsigned long reg1
, reg2
, ry
;
51 union register_pair rx
;
57 ry
= 0x10; /* storage configuration */
60 " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
61 " epsw %[reg1],%[reg2]\n"
62 " st %[reg1],0(%[psw_pgm])\n"
63 " st %[reg2],4(%[psw_pgm])\n"
65 " stg %[reg1],8(%[psw_pgm])\n"
66 " diag %[rx],%[ry],0x260\n"
69 "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
70 : [reg1
] "=&d" (reg1
),
74 "+Q" (S390_lowcore
.program_new_psw
),
78 [psw_pgm
] "a" (&S390_lowcore
.program_new_psw
)
80 return rc
== 0 ? ry
: -1;
83 static int diag260(void)
90 } storage_extents
[8] __aligned(16); /* VM supports up to 8 extends */
92 memset(storage_extents
, 0, sizeof(storage_extents
));
93 rc
= __diag260((unsigned long)storage_extents
, sizeof(storage_extents
));
97 for (i
= 0; i
< min_t(int, rc
, ARRAY_SIZE(storage_extents
)); i
++)
98 add_mem_detect_block(storage_extents
[i
].start
, storage_extents
[i
].end
+ 1);
102 static int tprot(unsigned long addr
)
104 unsigned long reg1
, reg2
;
109 " mvc 0(16,%[psw_old]),0(%[psw_pgm])\n"
110 " epsw %[reg1],%[reg2]\n"
111 " st %[reg1],0(%[psw_pgm])\n"
112 " st %[reg2],4(%[psw_pgm])\n"
114 " stg %[reg1],8(%[psw_pgm])\n"
115 " tprot 0(%[addr]),0\n"
118 "1: mvc 0(16,%[psw_pgm]),0(%[psw_old])\n"
119 : [reg1
] "=&d" (reg1
),
122 "=Q" (S390_lowcore
.program_new_psw
.addr
),
124 : [psw_old
] "a" (&old
),
125 [psw_pgm
] "a" (&S390_lowcore
.program_new_psw
),
131 static unsigned long search_mem_end(void)
133 unsigned long range
= 1 << (MAX_PHYSMEM_BITS
- 20); /* in 1MB blocks */
134 unsigned long offset
= 0;
139 pivot
= offset
+ range
;
140 if (!tprot(pivot
<< 20))
143 return (offset
+ 1) << 20;
146 unsigned long detect_memory(unsigned long *safe_addr
)
148 unsigned long max_physmem_end
= 0;
150 sclp_early_get_memsize(&max_physmem_end
);
151 mem_detect
.entries_extended
= (struct mem_detect_block
*)ALIGN(*safe_addr
, sizeof(u64
));
153 if (!sclp_early_read_storage_info()) {
154 mem_detect
.info_source
= MEM_DETECT_SCLP_STOR_INFO
;
155 } else if (!diag260()) {
156 mem_detect
.info_source
= MEM_DETECT_DIAG260
;
157 max_physmem_end
= max_physmem_end
?: get_mem_detect_end();
158 } else if (max_physmem_end
) {
159 add_mem_detect_block(0, max_physmem_end
);
160 mem_detect
.info_source
= MEM_DETECT_SCLP_READ_INFO
;
162 max_physmem_end
= search_mem_end();
163 add_mem_detect_block(0, max_physmem_end
);
164 mem_detect
.info_source
= MEM_DETECT_BIN_SEARCH
;
167 if (mem_detect
.count
> MEM_INLINED_ENTRIES
) {
168 *safe_addr
+= (mem_detect
.count
- MEM_INLINED_ENTRIES
) *
169 sizeof(struct mem_detect_block
);
172 return max_physmem_end
;
175 void mem_detect_set_usable_limit(unsigned long limit
)
177 struct mem_detect_block
*block
;
180 /* make sure mem_detect.usable ends up within online memory block */
181 for (i
= 0; i
< mem_detect
.count
; i
++) {
182 block
= __get_mem_detect_block_ptr(i
);
183 if (block
->start
>= limit
)
185 if (block
->end
>= limit
) {
186 mem_detect
.usable
= limit
;
189 mem_detect
.usable
= block
->end
;