mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
In case diag308 gives a response code 0x102 the stage3 loader can safely assume that no secure IPL is required since no IPL report block exists. Suggested-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Stefan Haberland <sth@linux.ibm.com> Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Reviewed-by: Philipp Rudo <prudo@linux.ibm.com> Tested-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
235 lines
6.3 KiB
C
235 lines
6.3 KiB
C
/*
|
|
* zipl - zSeries Initial Program Loader tool
|
|
*
|
|
* Main program for stage3 bootloader
|
|
*
|
|
* Copyright IBM Corp. 2013, 2018
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include "libc.h"
|
|
#include "boot/sigp.h"
|
|
#include "boot/s390.h"
|
|
#include "boot/sigp.h"
|
|
#include "boot/linux_layout.h"
|
|
#include "boot/loaders_layout.h"
|
|
|
|
#include "stage3.h"
|
|
#include "error.h"
|
|
#include "ebcdic.h"
|
|
#include "ebcdic_conv.h"
|
|
|
|
#define for_each_rb_entry(entry, rb) \
|
|
for (entry = rb->entries; \
|
|
(void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
|
|
entry++)
|
|
|
|
static const char *msg_sipl_inval = "Secure boot failure: invalid load address";
|
|
static const char *msg_sipl_unverified = "Secure boot failure: unverified load address";
|
|
static const char *msg_sipl_noparm = "Secure boot failure: unable to load ipl parameter";
|
|
|
|
static inline void __noreturn start_kernel(void)
|
|
{
|
|
struct psw_t *psw = &S390_lowcore.program_new_psw;
|
|
unsigned long addr, code;
|
|
|
|
/* Setup program check handler */
|
|
psw->mask = 0x000000180000000ULL;
|
|
code = 1;
|
|
|
|
asm volatile(
|
|
/* Setup program check handler */
|
|
" larl %[addr],.no_diag308\n"
|
|
" stg %[addr],8(%[psw])\n"
|
|
" diag %[code],%[code],0x308\n"
|
|
".no_diag308:\n"
|
|
" sam31\n"
|
|
" sr %r1,%r1\n"
|
|
" sr %r2,%r2\n"
|
|
" sigp %r1,%r2,%[order]\n"
|
|
" lpsw 0\n"
|
|
: [addr] "=&d" (addr),
|
|
[code] "+&d" (code)
|
|
: [psw] "a" (psw),
|
|
[order] "L" (SIGP_SET_ARCHITECTURE));
|
|
while (1);
|
|
}
|
|
|
|
unsigned int
|
|
is_verified_address(unsigned long image_addr)
|
|
{
|
|
struct ipl_rb_component_entry *comp;
|
|
struct ipl_rb_components *comps;
|
|
struct ipl_pl_hdr *pl_hdr;
|
|
struct ipl_rl_hdr *rl_hdr;
|
|
struct ipl_rb_hdr *rb_hdr;
|
|
unsigned long tmp;
|
|
void *rl_end;
|
|
|
|
/*
|
|
* There is an IPL report, to find it load the pointer to the
|
|
* IPL parameter information block from lowcore and skip past
|
|
* the IPL parameter list, then align the address to a double
|
|
* word boundary.
|
|
*/
|
|
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
|
|
pl_hdr = (struct ipl_pl_hdr *) tmp;
|
|
tmp = (tmp + pl_hdr->len + 7) & -8UL;
|
|
rl_hdr = (struct ipl_rl_hdr *) tmp;
|
|
/* Walk through the IPL report blocks in the IPL Report list */
|
|
comps = NULL;
|
|
rl_end = (void *) rl_hdr + rl_hdr->len;
|
|
rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr);
|
|
while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end &&
|
|
(void *) rb_hdr + rb_hdr->len <= rl_end) {
|
|
switch (rb_hdr->rbt) {
|
|
case IPL_RBT_COMPONENTS:
|
|
comps = (struct ipl_rb_components *) rb_hdr;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rb_hdr = (void *) rb_hdr + rb_hdr->len;
|
|
}
|
|
|
|
if (!comps)
|
|
return 0;
|
|
|
|
for_each_rb_entry(comp, comps) {
|
|
if (image_addr == comp->addr &&
|
|
comp->flags & IPL_RB_COMPONENT_FLAG_SIGNED &&
|
|
comp->flags & IPL_RB_COMPONENT_FLAG_VERIFIED)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int
|
|
secure_boot_enabled()
|
|
{
|
|
struct ipl_pl_hdr *pl_hdr;
|
|
unsigned int rc;
|
|
|
|
pl_hdr = (void *)get_zeroed_page();
|
|
switch (diag308(DIAG308_STORE, pl_hdr)) {
|
|
case DIAG308_RC_OK:
|
|
rc = pl_hdr->version <= IPL_MAX_SUPPORTED_VERSION &&
|
|
!!(pl_hdr->flags & IPL_FLAG_SECURE);
|
|
break;
|
|
case DIAG308_RC_NO_CONF:
|
|
rc = 0;
|
|
break;
|
|
default:
|
|
panic(ESECUREBOOT, "%s", msg_sipl_noparm);
|
|
break;
|
|
}
|
|
free_page((unsigned long) pl_hdr);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void start(void)
|
|
{
|
|
unsigned int subchannel_id;
|
|
unsigned char *cextra = (unsigned char *)COMMAND_LINE_EXTRA;
|
|
unsigned char *cmdline = (unsigned char *)COMMAND_LINE;
|
|
unsigned int cmdline_len = 0, cextra_len = 0;
|
|
|
|
/*
|
|
* IPL process is secure we have to use default IPL values and
|
|
* check if the psw jump address is within at the start of a
|
|
* verified component. If it is not IPL is aborted.
|
|
*/
|
|
if (secure_boot_enabled()) {
|
|
if (_stage3_parms.image_addr != IMAGE_LOAD_ADDRESS ||
|
|
_stage3_parms.load_psw != DEFAULT_PSW_LOAD)
|
|
panic(ESECUREBOOT, "%s", msg_sipl_inval);
|
|
|
|
if (!is_verified_address(_stage3_parms.load_psw & PSW32_ADDR_MASK))
|
|
panic(ESECUREBOOT, "%s", msg_sipl_unverified);
|
|
}
|
|
/*
|
|
* cut the kernel header
|
|
*/
|
|
memmove((void *)_stage3_parms.image_addr,
|
|
(void *)_stage3_parms.image_addr + IMAGE_LOAD_ADDRESS,
|
|
_stage3_parms.image_len - IMAGE_LOAD_ADDRESS);
|
|
|
|
/* store subchannel ID into low core and into new kernel space */
|
|
subchannel_id = S390_lowcore.subchannel_id;
|
|
*(unsigned int *)__LC_IPLDEV = subchannel_id;
|
|
*(unsigned long long *)IPL_DEVICE = subchannel_id;
|
|
|
|
/* if valid command line is given, copy it into new kernel space */
|
|
if (_stage3_parms.parm_addr != UNSPECIFIED_ADDRESS) {
|
|
memcpy(cmdline,
|
|
(void *)(unsigned long *)_stage3_parms.parm_addr,
|
|
COMMAND_LINE_SIZE);
|
|
/* terminate \0 */
|
|
cmdline[COMMAND_LINE_SIZE - 1] = 0;
|
|
}
|
|
|
|
/* convert extra parameter to ascii */
|
|
if (!_stage3_parms.extra_parm || !*cextra)
|
|
goto noextra;
|
|
|
|
/* Handle extra kernel parameters specified in DASD boot menu. */
|
|
ebcdic_to_ascii(cextra, cextra, COMMAND_LINE_SIZE);
|
|
|
|
/* determine length of extra parameter */
|
|
cextra_len = MIN(strlen((const char *)cextra), COMMAND_LINE_SIZE - 1);
|
|
|
|
/* remove leading whitespace of extra parameter */
|
|
while (cextra_len > 0 && *cextra == 0x20) {
|
|
cextra++;
|
|
cextra_len--;
|
|
}
|
|
|
|
/* determine length of original parm line */
|
|
cmdline_len = MIN(strlen((const char *)cmdline),
|
|
COMMAND_LINE_SIZE - 1);
|
|
|
|
/*
|
|
* if extra parm string starts with '=' replace original string,
|
|
* else append
|
|
*/
|
|
if (*cextra == 0x3d && cextra_len >= 1) {
|
|
/* skip '=' */
|
|
cextra++;
|
|
cextra_len--;
|
|
memcpy(cmdline, cextra, cextra_len);
|
|
cmdline[cextra_len] = 0;
|
|
} else if (cmdline_len + 1 <= COMMAND_LINE_SIZE - 1) {
|
|
/* add blank */
|
|
cmdline[cmdline_len] = 0x20;
|
|
cmdline_len++;
|
|
/* check if length is within max value */
|
|
cextra_len = (cmdline_len + cextra_len <= COMMAND_LINE_SIZE - 1) ?
|
|
cextra_len : (COMMAND_LINE_SIZE - 1 - cmdline_len);
|
|
/* append string */
|
|
memcpy(cmdline + cmdline_len, cextra, cextra_len);
|
|
/* terminate 0 */
|
|
cmdline[cmdline_len + cextra_len] = 0;
|
|
}
|
|
|
|
noextra:
|
|
/* copy initrd start address and size intop new kernle space */
|
|
*(unsigned long long *)INITRD_START = _stage3_parms.initrd_addr;
|
|
*(unsigned long long *)INITRD_SIZE = _stage3_parms.initrd_len;
|
|
|
|
/* store address of new kernel to 0 to be able to start it */
|
|
*(unsigned long long *)0 = _stage3_parms.load_psw;
|
|
|
|
kdump_stage3();
|
|
|
|
/* start new kernel */
|
|
start_kernel();
|
|
}
|
|
|
|
void panic_notify(unsigned long UNUSED(rc))
|
|
{
|
|
}
|