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 if (intersects(safe_addr, size, (unsigned long)comps, comps->len)) { 61 safe_addr = (unsigned long)comps + comps->len; 62 goto repeat; 63 } 64 for_each_rb_entry(comp, comps) 65 if (intersects(safe_addr, size, comp->addr, comp->len)) { 66 safe_addr = comp->addr + comp->len; 67 goto repeat; 68 } 69 if (intersects(safe_addr, size, (unsigned long)certs, certs->len)) { 70 safe_addr = (unsigned long)certs + certs->len; 71 goto repeat; 72 } 73 for_each_rb_entry(cert, certs) 74 if (intersects(safe_addr, size, cert->addr, cert->len)) { 75 safe_addr = cert->addr + cert->len; 76 goto repeat; 77 } 78 early_ipl_comp_list_addr = safe_addr; 79 ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size; 80 81 return safe_addr + size; 82 } 83 84 static void copy_components_bootdata(struct ipl_rb_components *comps) 85 { 86 struct ipl_rb_component_entry *comp, *ptr; 87 88 ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr; 89 for_each_rb_entry(comp, comps) 90 memcpy(ptr++, comp, sizeof(*ptr)); 91 } 92 93 static void copy_certificates_bootdata(struct ipl_rb_certificates *certs) 94 { 95 struct ipl_rb_certificate_entry *cert; 96 void *ptr; 97 98 ptr = (void *) ipl_cert_list_addr; 99 for_each_rb_entry(cert, certs) { 100 *(unsigned int *) ptr = cert->len; 101 ptr += sizeof(unsigned int); 102 memcpy(ptr, (void *) cert->addr, cert->len); 103 ptr += cert->len; 104 } 105 } 106 107 unsigned long read_ipl_report(unsigned long safe_addr) 108 { 109 struct ipl_rb_certificates *certs; 110 struct ipl_rb_components *comps; 111 struct ipl_pl_hdr *pl_hdr; 112 struct ipl_rl_hdr *rl_hdr; 113 struct ipl_rb_hdr *rb_hdr; 114 unsigned long tmp; 115 void *rl_end; 116 117 /* 118 * Check if there is a IPL report by looking at the copy 119 * of the IPL parameter information block. 120 */ 121 if (!ipl_block_valid || 122 !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)) 123 return safe_addr; 124 ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL); 125 /* 126 * There is an IPL report, to find it load the pointer to the 127 * IPL parameter information block from lowcore and skip past 128 * the IPL parameter list, then align the address to a double 129 * word boundary. 130 */ 131 tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; 132 pl_hdr = (struct ipl_pl_hdr *) tmp; 133 tmp = (tmp + pl_hdr->len + 7) & -8UL; 134 rl_hdr = (struct ipl_rl_hdr *) tmp; 135 /* Walk through the IPL report blocks in the IPL Report list */ 136 certs = NULL; 137 comps = NULL; 138 rl_end = (void *) rl_hdr + rl_hdr->len; 139 rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr); 140 while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end && 141 (void *) rb_hdr + rb_hdr->len <= rl_end) { 142 143 switch (rb_hdr->rbt) { 144 case IPL_RBT_CERTIFICATES: 145 certs = (struct ipl_rb_certificates *) rb_hdr; 146 break; 147 case IPL_RBT_COMPONENTS: 148 comps = (struct ipl_rb_components *) rb_hdr; 149 break; 150 default: 151 break; 152 } 153 154 rb_hdr = (void *) rb_hdr + rb_hdr->len; 155 } 156 157 /* 158 * With either the component list or the certificate list 159 * missing the kernel will stay ignorant of secure IPL. 160 */ 161 if (!comps || !certs) 162 return safe_addr; 163 164 /* 165 * Copy component and certificate list to a safe area 166 * where the decompressed kernel can find them. 167 */ 168 safe_addr = find_bootdata_space(comps, certs, safe_addr); 169 copy_components_bootdata(comps); 170 copy_certificates_bootdata(certs); 171 172 return safe_addr; 173 } 174