mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
To ensure that only verified code is executed the stage 3 loader has to check if the load psw points to an address that has previously been verified by the machine loader. If secure boot is enabled the IPL process will only continue if the verification was successful. Signed-off-by: Stefan Haberland <sth@linux.ibm.com> Acked-by: Peter Oberparleiter <oberpar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
359 lines
13 KiB
C
359 lines
13 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 "s390.h"
|
|
#include "stage3.h"
|
|
#include "error.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 unsigned char ebc_037[256] = {
|
|
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
|
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
|
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
|
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
|
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
|
|
-ENP ->LF */
|
|
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
|
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
|
|
-IUS */
|
|
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
|
|
-INP */
|
|
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
|
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
|
|
-SW */
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
|
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
|
|
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
|
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
|
|
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
|
/* 0x40 SP RSP ä ---- */
|
|
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
|
/* 0x48 . < ( + | */
|
|
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
|
/* 0x50 & ---- */
|
|
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
|
/* 0x58 ß ! $ * ) ; */
|
|
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
|
|
/* 0x60 - / ---- Ä ---- ---- ---- */
|
|
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
|
/* 0x68 ---- , % _ > ? */
|
|
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
|
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
|
|
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0x78 * ` : # @ ' = " */
|
|
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
|
/* 0x80 * a b c d e f g */
|
|
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
/* 0x88 h i ---- ---- ---- */
|
|
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
|
/* 0x90 ° j k l m n o p */
|
|
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
|
/* 0x98 q r ---- ---- */
|
|
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
|
/* 0xA0 ~ s t u v w x */
|
|
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
|
/* 0xA8 y z ---- ---- ---- ---- */
|
|
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0xB0 ^ ---- § ---- */
|
|
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
|
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
|
|
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0xC0 { A B C D E F G */
|
|
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
/* 0xC8 H I ---- ö ---- */
|
|
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
|
/* 0xD0 } J K L M N O P */
|
|
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
|
/* 0xD8 Q R ---- ü */
|
|
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
|
/* 0xE0 \ S T U V W X */
|
|
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
|
/* 0xE8 Y Z ---- Ö ---- ---- ---- */
|
|
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
|
/* 0xF0 0 1 2 3 4 5 6 7 */
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
|
|
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
|
};
|
|
|
|
static unsigned char ebc_500[256] = {
|
|
/* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
|
|
0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
|
|
/* 0x08 -GE -SPS -RPT VT FF CR SO SI */
|
|
0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
|
/* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
|
|
-ENP ->LF */
|
|
0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
|
|
/* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
|
|
-IUS */
|
|
0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
|
|
-INP */
|
|
0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
|
|
/* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
|
|
-SW */
|
|
0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
|
|
/* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
|
|
0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
|
|
/* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
|
|
0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
|
|
/* 0x40 SP RSP ä ---- */
|
|
0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
|
|
/* 0x48 . < ( + | */
|
|
0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
|
|
/* 0x50 & ---- */
|
|
0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
|
|
/* 0x58 ß ! $ * ) ; */
|
|
0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
|
|
/* 0x60 - / ---- Ä ---- ---- ---- */
|
|
0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
|
|
/* 0x68 ---- , % _ > ? */
|
|
0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
|
|
/* 0x70 --- ---- ---- ---- ---- ---- ---- */
|
|
0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0x78 * ` : # @ ' = " */
|
|
0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
|
|
/* 0x80 * a b c d e f g */
|
|
0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
|
|
/* 0x88 h i ---- ---- ---- */
|
|
0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
|
|
/* 0x90 ° j k l m n o p */
|
|
0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
|
|
/* 0x98 q r ---- ---- */
|
|
0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
|
|
/* 0xA0 ~ s t u v w x */
|
|
0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
|
|
/* 0xA8 y z ---- ---- ---- ---- */
|
|
0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0xB0 ^ ---- § ---- */
|
|
0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
|
|
/* 0xB8 ---- [ ] ---- ---- ---- ---- */
|
|
0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
|
|
/* 0xC0 { A B C D E F G */
|
|
0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
/* 0xC8 H I ---- ö ---- */
|
|
0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
|
|
/* 0xD0 } J K L M N O P */
|
|
0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
|
|
/* 0xD8 Q R ---- ü */
|
|
0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
|
|
/* 0xE0 \ S T U V W X */
|
|
0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
|
|
/* 0xE8 Y Z ---- Ö ---- ---- ---- */
|
|
0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
|
|
/* 0xF0 0 1 2 3 4 5 6 7 */
|
|
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
|
/* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
|
|
0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
|
|
};
|
|
|
|
static void ebcdic_to_ascii(unsigned char *target, unsigned char *source,
|
|
unsigned int l)
|
|
{
|
|
unsigned char *ebc;
|
|
unsigned int i;
|
|
|
|
ebc = is_zvm() ? ebc_037 : ebc_500;
|
|
for (i = 0; i < l; i++)
|
|
target[i] = ebc[source[i]];
|
|
}
|
|
|
|
static void
|
|
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,0x12\n"
|
|
" lpsw 0\n"
|
|
: [addr] "=&d" (addr),
|
|
[code] "+&d" (code)
|
|
: [psw] "a" (psw) );
|
|
}
|
|
|
|
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;
|
|
}
|
|
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 long tmp;
|
|
|
|
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr;
|
|
pl_hdr = (struct ipl_pl_hdr *) tmp;
|
|
|
|
return pl_hdr->flags & IPL_FLAG_SECURE;
|
|
}
|
|
|
|
void start(void)
|
|
{
|
|
unsigned int subchannel_id;
|
|
unsigned char *cextra = (unsigned char *)COMMAND_LINE_EXTRA;
|
|
unsigned char *command_line = (unsigned char *)COMMAND_LINE;
|
|
unsigned int begin = 0, end = 0, length = 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 (_image_addr != DEFAULT_IMAGE_ADDR ||
|
|
_load_psw != DEFAULT_PSW_LOAD)
|
|
panic(ESECUREBOOT, "%s", msg_sipl_inval);
|
|
|
|
if (!is_verified_address(_load_psw & PSW_ADDR_MASK))
|
|
panic(ESECUREBOOT, "%s", msg_sipl_unverified);
|
|
}
|
|
/*
|
|
* cut the kernel header
|
|
*/
|
|
memmove((void *)_image_addr,
|
|
(void *)_image_addr + KERNEL_HEADER_SIZE,
|
|
_image_len - KERNEL_HEADER_SIZE);
|
|
|
|
/* store subchannel ID into low core and into new kernel space */
|
|
subchannel_id = S390_lowcore.subchannel_id;
|
|
*(unsigned int *)__LC_IPLDEV = subchannel_id;
|
|
*(unsigned int *)IPL_DEVICE = subchannel_id;
|
|
|
|
/* if valid command line is given, copy it into new kernel space */
|
|
if (_parm_addr != UNSPECIFIED_ADDRESS) {
|
|
memcpy((void *)COMMAND_LINE,
|
|
(void *)(unsigned long *)_parm_addr, COMMAND_LINE_SIZE);
|
|
/* terminate \0 */
|
|
*(char *)(COMMAND_LINE + COMMAND_LINE_SIZE - 1) = 0;
|
|
}
|
|
|
|
/* convert extra parameter to ascii */
|
|
if (!_extra_parm || !*cextra)
|
|
goto noextra;
|
|
|
|
/* Handle extra kernel parameters specified in DASD boot menu. */
|
|
ebcdic_to_ascii(cextra, cextra, COMMAND_LINE_SIZE);
|
|
|
|
/* remove leading whitespace */
|
|
while (begin <= COMMAND_LINE_SIZE && cextra[begin] == 0x20)
|
|
begin++;
|
|
|
|
/* determine length of extra parameter */
|
|
while (length <= COMMAND_LINE_SIZE && cextra[length] != 0)
|
|
length++;
|
|
|
|
/* find end of original parm line */
|
|
while (command_line[end] != 0)
|
|
end++;
|
|
|
|
/*
|
|
* if extra parm string starts with '=' replace original string,
|
|
* else append
|
|
*/
|
|
if (cextra[begin] == 0x3d) {
|
|
memcpy((void *)COMMAND_LINE, (void *)(cextra + begin),
|
|
length);
|
|
} else {
|
|
/* check if length is within max value */
|
|
length = (end + 1 + length <= COMMAND_LINE_SIZE) ? length :
|
|
(COMMAND_LINE_SIZE - end - 1);
|
|
/* add blank */
|
|
command_line[end] = 0x20;
|
|
end++;
|
|
/* append string */
|
|
memcpy((void *)(command_line + end),
|
|
(void *)(cextra + begin), length);
|
|
/* terminate 0 */
|
|
command_line[end + length] = 0;
|
|
}
|
|
|
|
noextra:
|
|
/* copy initrd start address and size intop new kernle space */
|
|
*(unsigned long long *)INITRD_START = _initrd_addr;
|
|
*(unsigned long long *)INITRD_SIZE = _initrd_len;
|
|
|
|
/* store address of new kernel to 0 to be able to start it */
|
|
*(unsigned long long *)0 = _load_psw;
|
|
|
|
kdump_stage3();
|
|
|
|
/* start new kernel */
|
|
start_kernel();
|
|
}
|
|
|
|
void panic_notify(unsigned long UNUSED(rc))
|
|
{
|
|
}
|