diff --git a/zipl/boot/error.h b/zipl/boot/error.h index 93fd748e..715973ec 100644 --- a/zipl/boot/error.h +++ b/zipl/boot/error.h @@ -31,6 +31,9 @@ /* Internal error */ #define EINTERNAL 0x00004511 +/* Secure IPL error */ +#define ESECUREBOOT 0x00004512 + /* kdump: No operating system information was found */ #define EOS_INFO_MISSING 0x00004520 diff --git a/zipl/boot/s390.h b/zipl/boot/s390.h index cd507870..fac19042 100644 --- a/zipl/boot/s390.h +++ b/zipl/boot/s390.h @@ -35,6 +35,11 @@ struct psw_t { uint64_t addr; } __aligned(8); +struct psw32_t { + uint32_t mask; + uint32_t addr; +} __aligned(8); + void load_wait_psw(uint64_t, struct psw_t *); struct _lowcore { diff --git a/zipl/boot/stage3.c b/zipl/boot/stage3.c index dda17338..050da297 100644 --- a/zipl/boot/stage3.c +++ b/zipl/boot/stage3.c @@ -12,6 +12,15 @@ #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 */ @@ -192,6 +201,64 @@ start_kernel(void) : [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; @@ -200,8 +267,20 @@ void start(void) unsigned int begin = 0, end = 0, length = 0; /* - * Relocate the kernel image to its actual load address while stripping - * away the kernel IPL header to not overwrite the stage3 loader. + * 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, diff --git a/zipl/boot/stage3.h b/zipl/boot/stage3.h index 9bc4f860..91477b17 100644 --- a/zipl/boot/stage3.h +++ b/zipl/boot/stage3.h @@ -27,10 +27,148 @@ #define STAGE3_FLAG_SCSI 0x0001000000000000ULL #define STAGE3_FLAG_KDUMP 0x0002000000000000ULL +#define IPL_FLAG_SECURE 0x40 + +#define DEFAULT_IMAGE_ADDR 0x10000 +#define DEFAULT_PSW_LOAD 0x0008000080010000L +#define PSW_ADDR_MASK 0x000000007FFFFFFFL #define KERNEL_HEADER_SIZE 65536 #define UNSPECIFIED_ADDRESS -1ULL + +/* IPL Parameter List header */ +struct ipl_pl_hdr { + uint32_t len; + uint8_t flags; + uint8_t reserved1[2]; + uint8_t version; +} __packed; + +/* IPL Parameter Block header */ +struct ipl_pb_hdr { + uint32_t len; + uint8_t pbt; +} __packed; + +/* IPL Parameter Block 0 with common fields */ +struct ipl_pb0_common { + uint32_t len; + uint8_t pbt; + uint8_t flags; + uint8_t reserved1[2]; + uint8_t loadparm[8]; + uint8_t reserved2[84]; +} __packed; + +/* IPL Parameter Block 0 for FCP */ +struct ipl_pb0_fcp { + uint32_t len; + uint8_t pbt; + uint8_t reserved1[3]; + uint8_t loadparm[8]; + uint8_t reserved2[304]; + uint8_t opt; + uint8_t reserved3[3]; + uint8_t cssid; + uint8_t reserved4[1]; + uint8_t devno; + uint8_t reserved5[4]; + uint64_t wwpn; + uint64_t lun; + uint32_t bootprog; + uint8_t reserved6[12]; + uint64_t br_lba; + uint32_t scp_data_len; + uint8_t reserved7[260]; + uint8_t scp_data[]; +} __packed; + +/* IPL Parameter Block 0 for CCW */ +struct ipl_pb0_ccw { + uint32_t len; + uint8_t pbt; + uint8_t flags; + uint8_t reserved1[2]; + uint8_t loadparm[8]; + uint8_t reserved2[84]; + uint16_t reserved3 : 13; + uint8_t ssid : 3; + uint16_t devno; + uint8_t vm_flags; + uint8_t reserved4[3]; + uint32_t vm_parm_len; + uint8_t nss_name[8]; + uint8_t vm_parm[64]; + uint8_t reserved5[8]; +} __packed; + +struct ipl_parameter_block { + struct ipl_pl_hdr hdr; + union { + struct ipl_pb_hdr pb0_hdr; + struct ipl_pb0_common common; + struct ipl_pb0_fcp fcp; + struct ipl_pb0_ccw ccw; + char raw[PAGE_SIZE - sizeof(struct ipl_pl_hdr)]; + }; +} __packed __aligned(PAGE_SIZE); + +/* IPL Report List header */ +struct ipl_rl_hdr { + uint32_t len; + uint8_t flags; + uint8_t reserved1[2]; + uint8_t version; + uint8_t reserved2[8]; +} __packed; + +/* IPL Report Block header */ +struct ipl_rb_hdr { + uint32_t len; + uint8_t rbt; + uint8_t reserved1[11]; +} __packed; + +/* IPL Report Block types */ +enum ipl_rbt { + IPL_RBT_CERTIFICATES = 1, + IPL_RBT_COMPONENTS = 2, +}; + +/* IPL Report Block for the certificate list */ +struct ipl_rb_certificate_entry { + uint64_t addr; + uint64_t len; +} __packed; + +struct ipl_rb_certificates { + uint32_t len; + uint8_t rbt; + uint8_t reserved1[11]; + struct ipl_rb_certificate_entry entries[]; +} __packed; + +/* IPL Report Block for the component list */ +struct ipl_rb_component_entry { + uint64_t addr; + uint64_t len; + uint8_t flags; + uint8_t reserved1[5]; + uint16_t certificate_index; + uint8_t reserved2[8]; +}; + +#define IPL_RB_COMPONENT_FLAG_SIGNED 0x80 +#define IPL_RB_COMPONENT_FLAG_VERIFIED 0x40 + +struct ipl_rb_components { + uint32_t len; + uint8_t rbt; + uint8_t reserved1[11]; + struct ipl_rb_component_entry entries[]; +} __packed; + extern unsigned long long _parm_addr; /* address of parmline */ extern unsigned long long _initrd_addr; /* address of initrd */ extern unsigned long long _initrd_len; /* length of initrd */