Commit 49698745 authored by Vasily Gorbik's avatar Vasily Gorbik Committed by Martin Schwidefsky

s390: move ipl block and cmd line handling to early boot phase

To distinguish zfcpdump case and to be able to parse some of the kernel
command line arguments early (e.g. mem=) ipl block retrieval and command
line construction code is moved to the early boot phase.

"memory_end" is set up correctly respecting "mem=" and hsa_size in case
of the zfcpdump.

arch/s390/boot/string.c is introduced to provide string handling and
command line parsing functions to early boot phase code for the compressed
kernel image case.
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent b09decfd
...@@ -27,7 +27,8 @@ endif ...@@ -27,7 +27,8 @@ endif
CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
obj-y := head.o als.o startup.o mem_detect.o ebcdic.o sclp_early_core.o mem.o obj-y := head.o als.o startup.o mem_detect.o ipl_parm.o string.o ebcdic.o
obj-y += sclp_early_core.o mem.o ipl_vmparm.o cmdline.o ctype.o
targets := bzImage startup.a section_cmp.boot.data $(obj-y) targets := bzImage startup.a section_cmp.boot.data $(obj-y)
subdir- := compressed subdir- := compressed
......
...@@ -4,5 +4,8 @@ ...@@ -4,5 +4,8 @@
void startup_kernel(void); void startup_kernel(void);
void detect_memory(void); void detect_memory(void);
void store_ipl_parmblock(void);
void setup_boot_command_line(void);
void setup_memory_end(void);
#endif /* BOOT_BOOT_H */ #endif /* BOOT_BOOT_H */
// SPDX-License-Identifier: GPL-2.0
#include "../../../lib/cmdline.c"
// SPDX-License-Identifier: GPL-2.0
#include "../../../lib/ctype.c"
// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/ctype.h>
#include <asm/ebcdic.h>
#include <asm/sclp.h>
#include <asm/sections.h>
#include <asm/boot_data.h>
#include "boot.h"
char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
struct ipl_parameter_block __bootdata(early_ipl_block);
int __bootdata(early_ipl_block_valid);
unsigned long __bootdata(memory_end);
int __bootdata(memory_end_set);
static inline int __diag308(unsigned long subcode, void *addr)
{
register unsigned long _addr asm("0") = (unsigned long)addr;
register unsigned long _rc asm("1") = 0;
unsigned long reg1, reg2;
psw_t old = S390_lowcore.program_new_psw;
asm volatile(
" epsw %0,%1\n"
" st %0,%[psw_pgm]\n"
" st %1,%[psw_pgm]+4\n"
" larl %0,1f\n"
" stg %0,%[psw_pgm]+8\n"
" diag %[addr],%[subcode],0x308\n"
"1: nopr %%r7\n"
: "=&d" (reg1), "=&a" (reg2),
[psw_pgm] "=Q" (S390_lowcore.program_new_psw),
[addr] "+d" (_addr), "+d" (_rc)
: [subcode] "d" (subcode)
: "cc", "memory");
S390_lowcore.program_new_psw = old;
return _rc;
}
void store_ipl_parmblock(void)
{
int rc;
rc = __diag308(DIAG308_STORE, &early_ipl_block);
if (rc == DIAG308_RC_OK &&
early_ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
early_ipl_block_valid = 1;
}
static size_t scpdata_length(const char *buf, size_t count)
{
while (count) {
if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
break;
count--;
}
return count;
}
static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
const struct ipl_parameter_block *ipb)
{
size_t count;
size_t i;
int has_lowercase;
count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
ipb->ipl_info.fcp.scp_data_len));
if (!count)
goto out;
has_lowercase = 0;
for (i = 0; i < count; i++) {
if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
count = 0;
goto out;
}
if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
has_lowercase = 1;
}
if (has_lowercase)
memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
else
for (i = 0; i < count; i++)
dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
out:
dest[count] = '\0';
return count;
}
static void append_ipl_block_parm(void)
{
char *parm, *delim;
size_t len, rc = 0;
len = strlen(early_command_line);
delim = early_command_line + len; /* '\0' character position */
parm = early_command_line + len + 1; /* append right after '\0' */
switch (early_ipl_block.hdr.pbt) {
case DIAG308_IPL_TYPE_CCW:
rc = ipl_block_get_ascii_vmparm(
parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
break;
case DIAG308_IPL_TYPE_FCP:
rc = ipl_block_get_ascii_scpdata(
parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
break;
}
if (rc) {
if (*parm == '=')
memmove(early_command_line, parm + 1, rc);
else
*delim = ' '; /* replace '\0' with space */
}
}
static inline int has_ebcdic_char(const char *str)
{
int i;
for (i = 0; str[i]; i++)
if (str[i] & 0x80)
return 1;
return 0;
}
void setup_boot_command_line(void)
{
COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
/* convert arch command line to ascii if necessary */
if (has_ebcdic_char(COMMAND_LINE))
EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
/* copy arch command line */
strcpy(early_command_line, strim(COMMAND_LINE));
/* append IPL PARM data to the boot command line */
if (early_ipl_block_valid)
append_ipl_block_parm();
}
static char command_line_buf[COMMAND_LINE_SIZE] __section(.data);
static void parse_mem_opt(void)
{
char *args;
char *param, *val;
args = strcpy(command_line_buf, early_command_line);
while (*args) {
args = next_arg(args, &param, &val);
if (!strcmp(param, "mem")) {
memory_end = memparse(val, NULL);
memory_end_set = 1;
}
}
}
void setup_memory_end(void)
{
parse_mem_opt();
#ifdef CONFIG_CRASH_DUMP
if (!OLDMEM_BASE && early_ipl_block_valid &&
early_ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP &&
early_ipl_block.ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) {
if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
memory_end_set = 1;
}
#endif
}
// SPDX-License-Identifier: GPL-2.0
#include "../kernel/ipl_vmparm.c"
...@@ -51,6 +51,9 @@ void startup_kernel(void) ...@@ -51,6 +51,9 @@ void startup_kernel(void)
rescue_initrd(); rescue_initrd();
sclp_early_read_info(); sclp_early_read_info();
store_ipl_parmblock();
setup_boot_command_line();
setup_memory_end();
detect_memory(); detect_memory();
if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) { if (!IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED)) {
img = decompress_kernel(); img = decompress_kernel();
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/ctype.h>
#include <linux/kernel.h>
#include "../lib/string.c"
int strncmp(const char *cs, const char *ct, size_t count)
{
unsigned char c1, c2;
while (count) {
c1 = *cs++;
c2 = *ct++;
if (c1 != c2)
return c1 < c2 ? -1 : 1;
if (!c1)
break;
count--;
}
return 0;
}
char *skip_spaces(const char *str)
{
while (isspace(*str))
++str;
return (char *)str;
}
char *strim(char *s)
{
size_t size;
char *end;
size = strlen(s);
if (!size)
return s;
end = s + size - 1;
while (end >= s && isspace(*end))
end--;
*(end + 1) = '\0';
return skip_spaces(s);
}
/* Works only for digits and letters, but small and fast */
#define TOLOWER(x) ((x) | 0x20)
static unsigned int simple_guess_base(const char *cp)
{
if (cp[0] == '0') {
if (TOLOWER(cp[1]) == 'x' && isxdigit(cp[2]))
return 16;
else
return 8;
} else {
return 10;
}
}
/**
* simple_strtoull - convert a string to an unsigned long long
* @cp: The start of the string
* @endp: A pointer to the end of the parsed string will be placed here
* @base: The number base to use
*/
unsigned long long simple_strtoull(const char *cp, char **endp,
unsigned int base)
{
unsigned long long result = 0;
if (!base)
base = simple_guess_base(cp);
if (base == 16 && cp[0] == '0' && TOLOWER(cp[1]) == 'x')
cp += 2;
while (isxdigit(*cp)) {
unsigned int value;
value = isdigit(*cp) ? *cp - '0' : TOLOWER(*cp) - 'a' + 10;
if (value >= base)
break;
result = result * base + value;
cp++;
}
if (endp)
*endp = (char *)cp;
return result;
}
long simple_strtol(const char *cp, char **endp, unsigned int base)
{
if (*cp == '-')
return -simple_strtoull(cp + 1, endp, base);
return simple_strtoull(cp, endp, base);
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_BOOT_DATA_H
#include <asm/setup.h>
#include <asm/ipl.h>
extern char early_command_line[COMMAND_LINE_SIZE];
extern struct ipl_parameter_block early_ipl_block;
extern int early_ipl_block_valid;
#endif /* _ASM_S390_BOOT_DATA_H */
...@@ -89,8 +89,8 @@ void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs); ...@@ -89,8 +89,8 @@ void __init save_area_add_vxrs(struct save_area *, __vector128 *vxrs);
extern void s390_reset_system(void); extern void s390_reset_system(void);
extern void ipl_store_parameters(void); extern void ipl_store_parameters(void);
extern size_t append_ipl_vmparm(char *, size_t); extern size_t ipl_block_get_ascii_vmparm(char *dest, size_t size,
extern size_t append_ipl_scpdata(char *, size_t); const struct ipl_parameter_block *ipb);
enum ipl_type { enum ipl_type {
IPL_TYPE_UNKNOWN = 1, IPL_TYPE_UNKNOWN = 1,
......
...@@ -47,7 +47,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o ...@@ -47,7 +47,7 @@ obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o early_nobss.o
obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o obj-y += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
obj-y += nospec-branch.o obj-y += nospec-branch.o ipl_vmparm.o
extra-y += head64.o vmlinux.lds extra-y += head64.o vmlinux.lds
......
...@@ -29,10 +29,9 @@ ...@@ -29,10 +29,9 @@
#include <asm/cpcmd.h> #include <asm/cpcmd.h>
#include <asm/sclp.h> #include <asm/sclp.h>
#include <asm/facility.h> #include <asm/facility.h>
#include <asm/boot_data.h>
#include "entry.h" #include "entry.h"
static void __init setup_boot_command_line(void);
/* /*
* Initialize storage key for kernel pages * Initialize storage key for kernel pages
*/ */
...@@ -284,51 +283,11 @@ static int __init cad_setup(char *str) ...@@ -284,51 +283,11 @@ static int __init cad_setup(char *str)
} }
early_param("cad", cad_setup); early_param("cad", cad_setup);
/* Set up boot command line */ char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t))
{
char *parm, *delim;
size_t rc, len;
len = strlen(boot_command_line);
delim = boot_command_line + len; /* '\0' character position */
parm = boot_command_line + len + 1; /* append right after '\0' */
rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1);
if (rc) {
if (*parm == '=')
memmove(boot_command_line, parm + 1, rc);
else
*delim = ' '; /* replace '\0' with space */
}
}
static inline int has_ebcdic_char(const char *str)
{
int i;
for (i = 0; str[i]; i++)
if (str[i] & 0x80)
return 1;
return 0;
}
static void __init setup_boot_command_line(void) static void __init setup_boot_command_line(void)
{ {
COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
/* convert arch command line to ascii if necessary */
if (has_ebcdic_char(COMMAND_LINE))
EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
/* copy arch command line */ /* copy arch command line */
strlcpy(boot_command_line, strstrip(COMMAND_LINE), strlcpy(boot_command_line, early_command_line, ARCH_COMMAND_LINE_SIZE);
ARCH_COMMAND_LINE_SIZE);
/* append IPL PARM data to the boot command line */
if (MACHINE_IS_VM)
append_to_cmdline(append_ipl_vmparm);
append_to_cmdline(append_ipl_scpdata);
} }
static void __init check_image_bootable(void) static void __init check_image_bootable(void)
......
...@@ -29,6 +29,8 @@ ...@@ -29,6 +29,8 @@
#include <asm/checksum.h> #include <asm/checksum.h>
#include <asm/debug.h> #include <asm/debug.h>
#include <asm/os_info.h> #include <asm/os_info.h>
#include <asm/sections.h>
#include <asm/boot_data.h>
#include "entry.h" #include "entry.h"
#define IPL_PARM_BLOCK_VERSION 0 #define IPL_PARM_BLOCK_VERSION 0
...@@ -117,6 +119,9 @@ static char *dump_type_str(enum dump_type type) ...@@ -117,6 +119,9 @@ static char *dump_type_str(enum dump_type type)
} }
} }
struct ipl_parameter_block __bootdata(early_ipl_block);
int __bootdata(early_ipl_block_valid);
static int ipl_block_valid; static int ipl_block_valid;
static struct ipl_parameter_block ipl_block; static struct ipl_parameter_block ipl_block;
...@@ -262,115 +267,16 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, ...@@ -262,115 +267,16 @@ static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr,
static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
/* VM IPL PARM routines */
static size_t reipl_get_ascii_vmparm(char *dest, size_t size,
const struct ipl_parameter_block *ipb)
{
int i;
size_t len;
char has_lowercase = 0;
len = 0;
if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
(ipb->ipl_info.ccw.vm_parm_len > 0)) {
len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len);
memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
/* If at least one character is lowercase, we assume mixed
* case; otherwise we convert everything to lowercase.
*/
for (i = 0; i < len; i++)
if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */
(dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */
(dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */
has_lowercase = 1;
break;
}
if (!has_lowercase)
EBC_TOLOWER(dest, len);
EBCASC(dest, len);
}
dest[len] = 0;
return len;
}
size_t append_ipl_vmparm(char *dest, size_t size)
{
size_t rc;
rc = 0;
if (ipl_block_valid && ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW)
rc = reipl_get_ascii_vmparm(dest, size, &ipl_block);
else
dest[0] = 0;
return rc;
}
static ssize_t ipl_vm_parm_show(struct kobject *kobj, static ssize_t ipl_vm_parm_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page) struct kobj_attribute *attr, char *page)
{ {
char parm[DIAG308_VMPARM_SIZE + 1] = {}; char parm[DIAG308_VMPARM_SIZE + 1] = {};
append_ipl_vmparm(parm, sizeof(parm)); if (ipl_block_valid && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW))
ipl_block_get_ascii_vmparm(parm, sizeof(parm), &ipl_block);
return sprintf(page, "%s\n", parm); return sprintf(page, "%s\n", parm);
} }
static size_t scpdata_length(const char* buf, size_t count)
{
while (count) {
if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
break;
count--;
}
return count;
}
static size_t reipl_append_ascii_scpdata(char *dest, size_t size,
const struct ipl_parameter_block *ipb)
{
size_t count;
size_t i;
int has_lowercase;
count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
ipb->ipl_info.fcp.scp_data_len));
if (!count)
goto out;
has_lowercase = 0;
for (i = 0; i < count; i++) {
if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
count = 0;
goto out;
}
if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
has_lowercase = 1;
}
if (has_lowercase)
memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
else
for (i = 0; i < count; i++)
dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
out:
dest[count] = '\0';
return count;
}
size_t append_ipl_scpdata(char *dest, size_t len)
{
size_t rc;
rc = 0;
if (ipl_block_valid && ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP)
rc = reipl_append_ascii_scpdata(dest, len, &ipl_block);
else
dest[0] = 0;
return rc;
}
static struct kobj_attribute sys_ipl_vm_parm_attr = static struct kobj_attribute sys_ipl_vm_parm_attr =
__ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL); __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL);
...@@ -564,7 +470,7 @@ static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb, ...@@ -564,7 +470,7 @@ static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb,
{ {
char vmparm[DIAG308_VMPARM_SIZE + 1] = {}; char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb); ipl_block_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb);
return sprintf(page, "%s\n", vmparm); return sprintf(page, "%s\n", vmparm);
} }
...@@ -1769,11 +1675,10 @@ void __init setup_ipl(void) ...@@ -1769,11 +1675,10 @@ void __init setup_ipl(void)
void __init ipl_store_parameters(void) void __init ipl_store_parameters(void)
{ {
int rc; if (early_ipl_block_valid) {
memcpy(&ipl_block, &early_ipl_block, sizeof(ipl_block));
rc = diag308(DIAG308_STORE, &ipl_block);
if (rc == DIAG308_RC_OK && ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
ipl_block_valid = 1; ipl_block_valid = 1;
}
} }
void s390_reset_system(void) void s390_reset_system(void)
......
// SPDX-License-Identifier: GPL-2.0
#include <asm/ebcdic.h>
#include <asm/ipl.h>
/* VM IPL PARM routines */
size_t ipl_block_get_ascii_vmparm(char *dest, size_t size,
const struct ipl_parameter_block *ipb)
{
int i;
size_t len;
char has_lowercase = 0;
len = 0;
if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
(ipb->ipl_info.ccw.vm_parm_len > 0)) {
len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len);
memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
/* If at least one character is lowercase, we assume mixed
* case; otherwise we convert everything to lowercase.
*/
for (i = 0; i < len; i++)
if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */
(dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */
(dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */
has_lowercase = 1;
break;
}
if (!has_lowercase)
EBC_TOLOWER(dest, len);
EBCASC(dest, len);
}
dest[len] = 0;
return len;
}
...@@ -90,8 +90,8 @@ char elf_platform[ELF_PLATFORM_SIZE]; ...@@ -90,8 +90,8 @@ char elf_platform[ELF_PLATFORM_SIZE];
unsigned long int_hwcap = 0; unsigned long int_hwcap = 0;
int __initdata memory_end_set; int __bootdata(memory_end_set);
unsigned long __initdata memory_end; unsigned long __bootdata(memory_end);
unsigned long __bootdata(max_physmem_end); unsigned long __bootdata(max_physmem_end);
struct mem_detect_info __bootdata(mem_detect); struct mem_detect_info __bootdata(mem_detect);
...@@ -286,15 +286,6 @@ void machine_power_off(void) ...@@ -286,15 +286,6 @@ void machine_power_off(void)
void (*pm_power_off)(void) = machine_power_off; void (*pm_power_off)(void) = machine_power_off;
EXPORT_SYMBOL_GPL(pm_power_off); EXPORT_SYMBOL_GPL(pm_power_off);
static int __init early_parse_mem(char *p)
{
memory_end = memparse(p, &p);
memory_end &= PAGE_MASK;
memory_end_set = 1;
return 0;
}
early_param("mem", early_parse_mem);
static int __init parse_vmalloc(char *arg) static int __init parse_vmalloc(char *arg)
{ {
if (!arg) if (!arg)
...@@ -605,16 +596,7 @@ static struct notifier_block kdump_mem_nb = { ...@@ -605,16 +596,7 @@ static struct notifier_block kdump_mem_nb = {
*/ */
static void reserve_memory_end(void) static void reserve_memory_end(void)
{ {
#ifdef CONFIG_CRASH_DUMP if (memory_end_set)
if (ipl_info.type == IPL_TYPE_FCP_DUMP &&
!OLDMEM_BASE && sclp.hsa_size) {
memory_end = sclp.hsa_size;
memory_end &= PAGE_MASK;
memory_end_set = 1;
}
#endif
if (!memory_end_set)
return;
memblock_reserve(memory_end, ULONG_MAX); memblock_reserve(memory_end, ULONG_MAX);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment