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