cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

mem_detect.c (4782B)


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