1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/init.h> 3 #include <linux/ctype.h> 4 #include <asm/ebcdic.h> 5 #include <asm/sclp.h> 6 #include <asm/sections.h> 7 #include <asm/boot_data.h> 8 #include <uapi/asm/ipl.h> 9 #include "boot.h" 10 11 int __bootdata_preserved(ipl_secure_flag); 12 13 unsigned long __bootdata_preserved(ipl_cert_list_addr); 14 unsigned long __bootdata_preserved(ipl_cert_list_size); 15 16 unsigned long __bootdata(early_ipl_comp_list_addr); 17 unsigned long __bootdata(early_ipl_comp_list_size); 18 19 #define for_each_rb_entry(entry, rb) \ 20 for (entry = rb->entries; \ 21 (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \ 22 entry++) 23 24 static inline bool intersects(unsigned long addr0, unsigned long size0, 25 unsigned long addr1, unsigned long size1) 26 { 27 return addr0 + size0 > addr1 && addr1 + size1 > addr0; 28 } 29 30 static unsigned long find_bootdata_space(struct ipl_rb_components *comps, 31 struct ipl_rb_certificates *certs, 32 unsigned long safe_addr) 33 { 34 struct ipl_rb_certificate_entry *cert; 35 struct ipl_rb_component_entry *comp; 36 size_t size; 37 38 /* 39 * Find the length for the IPL report boot data 40 */ 41 early_ipl_comp_list_size = 0; 42 for_each_rb_entry(comp, comps) 43 early_ipl_comp_list_size += sizeof(*comp); 44 ipl_cert_list_size = 0; 45 for_each_rb_entry(cert, certs) 46 ipl_cert_list_size += sizeof(unsigned int) + cert->len; 47 size = ipl_cert_list_size + early_ipl_comp_list_size; 48 49 /* 50 * Start from safe_addr to find a free memory area large 51 * enough for the IPL report boot data. This area is used 52 * for ipl_cert_list_addr/ipl_cert_list_size and 53 * early_ipl_comp_list_addr/early_ipl_comp_list_size. It must 54 * not overlap with any component or any certificate. 55 */ 56 repeat: 57 if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_data.start && initrd_data.size && 58 intersects(initrd_data.start, initrd_data.size, safe_addr, size)) 59 safe_addr = initrd_data.start + initrd_data.size; 60 for_each_rb_entry(comp, comps) 61 if (intersects(safe_addr, size, comp->addr, comp->len)) { 62 safe_addr = comp->addr + comp->len; 63 goto repeat; 64 } 65 for_each_rb_entry(cert, certs) 66 if (intersects(safe_addr, size, cert->addr, cert->len)) { 67 safe_addr = cert->addr + cert->len; 68 goto repeat; 69 } 70 early_ipl_comp_list_addr = safe_addr; 71 ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size; 72 73 return safe_addr + size; 74 } 75 76 static void copy_components_bootdata(struct ipl_rb_components *comps) 77 { 78 struct ipl_rb_component_entry *comp, *ptr; 79 80 ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr; 81 for_each_rb_entry(comp, comps) 82 memcpy(ptr++, comp, sizeof(*ptr)); 83 } 84 85 static void copy_certificates_bootdata(struct ipl_rb_certificates *certs) 86 { 87 struct ipl_rb_certificate_entry *cert; 88 void *ptr; 89 90 ptr = (void *) ipl_cert_list_addr; 91 for_each_rb_entry(cert, certs) { 92 *(unsigned int *) ptr = cert->len; 93 ptr += sizeof(unsigned int); 94 memcpy(ptr, (void *) cert->addr, cert->len); 95 ptr += cert->len; 96 } 97 } 98 99 unsigned long read_ipl_report(unsigned long safe_addr) 100 { 101 struct ipl_rb_certificates *certs; 102 struct ipl_rb_components *comps; 103 struct ipl_pl_hdr *pl_hdr; 104 struct ipl_rl_hdr *rl_hdr; 105 struct ipl_rb_hdr *rb_hdr; 106 unsigned long tmp; 107 void *rl_end; 108 109 /* 110 * Check if there is a IPL report by looking at the copy 111 * of the IPL parameter information block. 112 */ 113 if (!ipl_block_valid || 114 !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)) 115 return safe_addr; 116 ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL); 117 /* 118 * There is an IPL report, to find it load the pointer to the 119 * IPL parameter information block from lowcore and skip past 120 * the IPL parameter list, then align the address to a double 121 * word boundary. 122 */ 123 tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; 124 pl_hdr = (struct ipl_pl_hdr *) tmp; 125 tmp = (tmp + pl_hdr->len + 7) & -8UL; 126 rl_hdr = (struct ipl_rl_hdr *) tmp; 127 /* Walk through the IPL report blocks in the IPL Report list */ 128 certs = NULL; 129 comps = NULL; 130 rl_end = (void *) rl_hdr + rl_hdr->len; 131 rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr); 132 while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end && 133 (void *) rb_hdr + rb_hdr->len <= rl_end) { 134 135 switch (rb_hdr->rbt) { 136 case IPL_RBT_CERTIFICATES: 137 certs = (struct ipl_rb_certificates *) rb_hdr; 138 break; 139 case IPL_RBT_COMPONENTS: 140 comps = (struct ipl_rb_components *) rb_hdr; 141 break; 142 default: 143 break; 144 } 145 146 rb_hdr = (void *) rb_hdr + rb_hdr->len; 147 } 148 149 /* 150 * With either the component list or the certificate list 151 * missing the kernel will stay ignorant of secure IPL. 152 */ 153 if (!comps || !certs) 154 return safe_addr; 155 156 /* 157 * Copy component and certificate list to a safe area 158 * where the decompressed kernel can find them. 159 */ 160 safe_addr = find_bootdata_space(comps, certs, safe_addr); 161 copy_components_bootdata(comps); 162 copy_certificates_bootdata(certs); 163 164 return safe_addr; 165 } 166