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

ipl_parm.c (6965B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/kernel.h>
      3#include <linux/init.h>
      4#include <linux/ctype.h>
      5#include <linux/pgtable.h>
      6#include <asm/ebcdic.h>
      7#include <asm/sclp.h>
      8#include <asm/sections.h>
      9#include <asm/boot_data.h>
     10#include <asm/facility.h>
     11#include <asm/setup.h>
     12#include <asm/uv.h>
     13#include "boot.h"
     14
     15struct parmarea parmarea __section(".parmarea") = {
     16	.kernel_version		= (unsigned long)kernel_version,
     17	.max_command_line_size	= COMMAND_LINE_SIZE,
     18	.command_line		= "root=/dev/ram0 ro",
     19};
     20
     21char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
     22int __bootdata(noexec_disabled);
     23
     24unsigned int __bootdata_preserved(zlib_dfltcc_support) = ZLIB_DFLTCC_FULL;
     25struct ipl_parameter_block __bootdata_preserved(ipl_block);
     26int __bootdata_preserved(ipl_block_valid);
     27
     28unsigned long vmalloc_size = VMALLOC_DEFAULT_SIZE;
     29unsigned long memory_limit;
     30int vmalloc_size_set;
     31int kaslr_enabled;
     32
     33static inline int __diag308(unsigned long subcode, void *addr)
     34{
     35	unsigned long reg1, reg2;
     36	union register_pair r1;
     37	psw_t old;
     38
     39	r1.even = (unsigned long) addr;
     40	r1.odd	= 0;
     41	asm volatile(
     42		"	mvc	0(16,%[psw_old]),0(%[psw_pgm])\n"
     43		"	epsw	%[reg1],%[reg2]\n"
     44		"	st	%[reg1],0(%[psw_pgm])\n"
     45		"	st	%[reg2],4(%[psw_pgm])\n"
     46		"	larl	%[reg1],1f\n"
     47		"	stg	%[reg1],8(%[psw_pgm])\n"
     48		"	diag	%[r1],%[subcode],0x308\n"
     49		"1:	mvc	0(16,%[psw_pgm]),0(%[psw_old])\n"
     50		: [r1] "+&d" (r1.pair),
     51		  [reg1] "=&d" (reg1),
     52		  [reg2] "=&a" (reg2),
     53		  "+Q" (S390_lowcore.program_new_psw),
     54		  "=Q" (old)
     55		: [subcode] "d" (subcode),
     56		  [psw_old] "a" (&old),
     57		  [psw_pgm] "a" (&S390_lowcore.program_new_psw)
     58		: "cc", "memory");
     59	return r1.odd;
     60}
     61
     62void store_ipl_parmblock(void)
     63{
     64	int rc;
     65
     66	rc = __diag308(DIAG308_STORE, &ipl_block);
     67	if (rc == DIAG308_RC_OK &&
     68	    ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
     69		ipl_block_valid = 1;
     70}
     71
     72bool is_ipl_block_dump(void)
     73{
     74	if (ipl_block.pb0_hdr.pbt == IPL_PBT_FCP &&
     75	    ipl_block.fcp.opt == IPL_PB0_FCP_OPT_DUMP)
     76		return true;
     77	if (ipl_block.pb0_hdr.pbt == IPL_PBT_NVME &&
     78	    ipl_block.nvme.opt == IPL_PB0_NVME_OPT_DUMP)
     79		return true;
     80	return false;
     81}
     82
     83static size_t scpdata_length(const u8 *buf, size_t count)
     84{
     85	while (count) {
     86		if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
     87			break;
     88		count--;
     89	}
     90	return count;
     91}
     92
     93static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
     94					  const struct ipl_parameter_block *ipb)
     95{
     96	const __u8 *scp_data;
     97	__u32 scp_data_len;
     98	int has_lowercase;
     99	size_t count = 0;
    100	size_t i;
    101
    102	switch (ipb->pb0_hdr.pbt) {
    103	case IPL_PBT_FCP:
    104		scp_data_len = ipb->fcp.scp_data_len;
    105		scp_data = ipb->fcp.scp_data;
    106		break;
    107	case IPL_PBT_NVME:
    108		scp_data_len = ipb->nvme.scp_data_len;
    109		scp_data = ipb->nvme.scp_data;
    110		break;
    111	default:
    112		goto out;
    113	}
    114
    115	count = min(size - 1, scpdata_length(scp_data, scp_data_len));
    116	if (!count)
    117		goto out;
    118
    119	has_lowercase = 0;
    120	for (i = 0; i < count; i++) {
    121		if (!isascii(scp_data[i])) {
    122			count = 0;
    123			goto out;
    124		}
    125		if (!has_lowercase && islower(scp_data[i]))
    126			has_lowercase = 1;
    127	}
    128
    129	if (has_lowercase)
    130		memcpy(dest, scp_data, count);
    131	else
    132		for (i = 0; i < count; i++)
    133			dest[i] = tolower(scp_data[i]);
    134out:
    135	dest[count] = '\0';
    136	return count;
    137}
    138
    139static void append_ipl_block_parm(void)
    140{
    141	char *parm, *delim;
    142	size_t len, rc = 0;
    143
    144	len = strlen(early_command_line);
    145
    146	delim = early_command_line + len;    /* '\0' character position */
    147	parm = early_command_line + len + 1; /* append right after '\0' */
    148
    149	switch (ipl_block.pb0_hdr.pbt) {
    150	case IPL_PBT_CCW:
    151		rc = ipl_block_get_ascii_vmparm(
    152			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
    153		break;
    154	case IPL_PBT_FCP:
    155	case IPL_PBT_NVME:
    156		rc = ipl_block_get_ascii_scpdata(
    157			parm, COMMAND_LINE_SIZE - len - 1, &ipl_block);
    158		break;
    159	}
    160	if (rc) {
    161		if (*parm == '=')
    162			memmove(early_command_line, parm + 1, rc);
    163		else
    164			*delim = ' '; /* replace '\0' with space */
    165	}
    166}
    167
    168static inline int has_ebcdic_char(const char *str)
    169{
    170	int i;
    171
    172	for (i = 0; str[i]; i++)
    173		if (str[i] & 0x80)
    174			return 1;
    175	return 0;
    176}
    177
    178void setup_boot_command_line(void)
    179{
    180	parmarea.command_line[COMMAND_LINE_SIZE - 1] = 0;
    181	/* convert arch command line to ascii if necessary */
    182	if (has_ebcdic_char(parmarea.command_line))
    183		EBCASC(parmarea.command_line, COMMAND_LINE_SIZE);
    184	/* copy arch command line */
    185	strcpy(early_command_line, strim(parmarea.command_line));
    186
    187	/* append IPL PARM data to the boot command line */
    188	if (!is_prot_virt_guest() && ipl_block_valid)
    189		append_ipl_block_parm();
    190}
    191
    192static void modify_facility(unsigned long nr, bool clear)
    193{
    194	if (clear)
    195		__clear_facility(nr, stfle_fac_list);
    196	else
    197		__set_facility(nr, stfle_fac_list);
    198}
    199
    200static void check_cleared_facilities(void)
    201{
    202	unsigned long als[] = { FACILITIES_ALS };
    203	int i;
    204
    205	for (i = 0; i < ARRAY_SIZE(als); i++) {
    206		if ((stfle_fac_list[i] & als[i]) != als[i]) {
    207			sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
    208			print_missing_facilities();
    209			break;
    210		}
    211	}
    212}
    213
    214static void modify_fac_list(char *str)
    215{
    216	unsigned long val, endval;
    217	char *endp;
    218	bool clear;
    219
    220	while (*str) {
    221		clear = false;
    222		if (*str == '!') {
    223			clear = true;
    224			str++;
    225		}
    226		val = simple_strtoull(str, &endp, 0);
    227		if (str == endp)
    228			break;
    229		str = endp;
    230		if (*str == '-') {
    231			str++;
    232			endval = simple_strtoull(str, &endp, 0);
    233			if (str == endp)
    234				break;
    235			str = endp;
    236			while (val <= endval) {
    237				modify_facility(val, clear);
    238				val++;
    239			}
    240		} else {
    241			modify_facility(val, clear);
    242		}
    243		if (*str != ',')
    244			break;
    245		str++;
    246	}
    247	check_cleared_facilities();
    248}
    249
    250static char command_line_buf[COMMAND_LINE_SIZE];
    251void parse_boot_command_line(void)
    252{
    253	char *param, *val;
    254	bool enabled;
    255	char *args;
    256	int rc;
    257
    258	kaslr_enabled = IS_ENABLED(CONFIG_RANDOMIZE_BASE);
    259	args = strcpy(command_line_buf, early_command_line);
    260	while (*args) {
    261		args = next_arg(args, &param, &val);
    262
    263		if (!strcmp(param, "mem") && val)
    264			memory_limit = round_down(memparse(val, NULL), PAGE_SIZE);
    265
    266		if (!strcmp(param, "vmalloc") && val) {
    267			vmalloc_size = round_up(memparse(val, NULL), PAGE_SIZE);
    268			vmalloc_size_set = 1;
    269		}
    270
    271		if (!strcmp(param, "dfltcc") && val) {
    272			if (!strcmp(val, "off"))
    273				zlib_dfltcc_support = ZLIB_DFLTCC_DISABLED;
    274			else if (!strcmp(val, "on"))
    275				zlib_dfltcc_support = ZLIB_DFLTCC_FULL;
    276			else if (!strcmp(val, "def_only"))
    277				zlib_dfltcc_support = ZLIB_DFLTCC_DEFLATE_ONLY;
    278			else if (!strcmp(val, "inf_only"))
    279				zlib_dfltcc_support = ZLIB_DFLTCC_INFLATE_ONLY;
    280			else if (!strcmp(val, "always"))
    281				zlib_dfltcc_support = ZLIB_DFLTCC_FULL_DEBUG;
    282		}
    283
    284		if (!strcmp(param, "noexec")) {
    285			rc = kstrtobool(val, &enabled);
    286			if (!rc && !enabled)
    287				noexec_disabled = 1;
    288		}
    289
    290		if (!strcmp(param, "facilities") && val)
    291			modify_fac_list(val);
    292
    293		if (!strcmp(param, "nokaslr"))
    294			kaslr_enabled = 0;
    295
    296#if IS_ENABLED(CONFIG_KVM)
    297		if (!strcmp(param, "prot_virt")) {
    298			rc = kstrtobool(val, &enabled);
    299			if (!rc && enabled)
    300				prot_virt_host = 1;
    301		}
    302#endif
    303	}
    304}