19641b8ccSMartin Schwidefsky // SPDX-License-Identifier: GPL-2.0 29641b8ccSMartin Schwidefsky #include <linux/init.h> 39641b8ccSMartin Schwidefsky #include <linux/ctype.h> 49641b8ccSMartin Schwidefsky #include <asm/ebcdic.h> 59641b8ccSMartin Schwidefsky #include <asm/sclp.h> 69641b8ccSMartin Schwidefsky #include <asm/sections.h> 79641b8ccSMartin Schwidefsky #include <asm/boot_data.h> 8f913a660SVasily Gorbik #include <asm/physmem_info.h> 99641b8ccSMartin Schwidefsky #include <uapi/asm/ipl.h> 109641b8ccSMartin Schwidefsky #include "boot.h" 119641b8ccSMartin Schwidefsky 129641b8ccSMartin Schwidefsky int __bootdata_preserved(ipl_secure_flag); 139641b8ccSMartin Schwidefsky 149641b8ccSMartin Schwidefsky unsigned long __bootdata_preserved(ipl_cert_list_addr); 159641b8ccSMartin Schwidefsky unsigned long __bootdata_preserved(ipl_cert_list_size); 169641b8ccSMartin Schwidefsky 179641b8ccSMartin Schwidefsky unsigned long __bootdata(early_ipl_comp_list_addr); 189641b8ccSMartin Schwidefsky unsigned long __bootdata(early_ipl_comp_list_size); 199641b8ccSMartin Schwidefsky 20f913a660SVasily Gorbik static struct ipl_rb_certificates *certs; 21f913a660SVasily Gorbik static struct ipl_rb_components *comps; 22f913a660SVasily Gorbik static bool ipl_report_needs_saving; 23f913a660SVasily Gorbik 249641b8ccSMartin Schwidefsky #define for_each_rb_entry(entry, rb) \ 259641b8ccSMartin Schwidefsky for (entry = rb->entries; \ 269641b8ccSMartin Schwidefsky (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \ 279641b8ccSMartin Schwidefsky entry++) 289641b8ccSMartin Schwidefsky 29f913a660SVasily Gorbik static unsigned long get_cert_comp_list_size(void) 309641b8ccSMartin Schwidefsky { 319641b8ccSMartin Schwidefsky struct ipl_rb_certificate_entry *cert; 329641b8ccSMartin Schwidefsky struct ipl_rb_component_entry *comp; 339641b8ccSMartin Schwidefsky size_t size; 349641b8ccSMartin Schwidefsky 359641b8ccSMartin Schwidefsky /* 369641b8ccSMartin Schwidefsky * Find the length for the IPL report boot data 379641b8ccSMartin Schwidefsky */ 389641b8ccSMartin Schwidefsky early_ipl_comp_list_size = 0; 399641b8ccSMartin Schwidefsky for_each_rb_entry(comp, comps) 409641b8ccSMartin Schwidefsky early_ipl_comp_list_size += sizeof(*comp); 419641b8ccSMartin Schwidefsky ipl_cert_list_size = 0; 429641b8ccSMartin Schwidefsky for_each_rb_entry(cert, certs) 439641b8ccSMartin Schwidefsky ipl_cert_list_size += sizeof(unsigned int) + cert->len; 44f913a660SVasily Gorbik return ipl_cert_list_size + early_ipl_comp_list_size; 459641b8ccSMartin Schwidefsky } 469641b8ccSMartin Schwidefsky 47f913a660SVasily Gorbik bool ipl_report_certs_intersects(unsigned long addr, unsigned long size, 48f913a660SVasily Gorbik unsigned long *intersection_start) 49f913a660SVasily Gorbik { 50f913a660SVasily Gorbik struct ipl_rb_certificate_entry *cert; 51f913a660SVasily Gorbik 52f913a660SVasily Gorbik if (!ipl_report_needs_saving) 53f913a660SVasily Gorbik return false; 54f913a660SVasily Gorbik 55f913a660SVasily Gorbik for_each_rb_entry(cert, certs) { 56f913a660SVasily Gorbik if (intersects(addr, size, cert->addr, cert->len)) { 57f913a660SVasily Gorbik *intersection_start = cert->addr; 58f913a660SVasily Gorbik return true; 59f913a660SVasily Gorbik } 60f913a660SVasily Gorbik } 61f913a660SVasily Gorbik return false; 62f913a660SVasily Gorbik } 63f913a660SVasily Gorbik 64f913a660SVasily Gorbik static void copy_components_bootdata(void) 659641b8ccSMartin Schwidefsky { 669641b8ccSMartin Schwidefsky struct ipl_rb_component_entry *comp, *ptr; 679641b8ccSMartin Schwidefsky 689641b8ccSMartin Schwidefsky ptr = (struct ipl_rb_component_entry *) early_ipl_comp_list_addr; 699641b8ccSMartin Schwidefsky for_each_rb_entry(comp, comps) 709641b8ccSMartin Schwidefsky memcpy(ptr++, comp, sizeof(*ptr)); 719641b8ccSMartin Schwidefsky } 729641b8ccSMartin Schwidefsky 73f913a660SVasily Gorbik static void copy_certificates_bootdata(void) 749641b8ccSMartin Schwidefsky { 759641b8ccSMartin Schwidefsky struct ipl_rb_certificate_entry *cert; 769641b8ccSMartin Schwidefsky void *ptr; 779641b8ccSMartin Schwidefsky 789641b8ccSMartin Schwidefsky ptr = (void *) ipl_cert_list_addr; 799641b8ccSMartin Schwidefsky for_each_rb_entry(cert, certs) { 809641b8ccSMartin Schwidefsky *(unsigned int *) ptr = cert->len; 819641b8ccSMartin Schwidefsky ptr += sizeof(unsigned int); 829641b8ccSMartin Schwidefsky memcpy(ptr, (void *) cert->addr, cert->len); 839641b8ccSMartin Schwidefsky ptr += cert->len; 849641b8ccSMartin Schwidefsky } 859641b8ccSMartin Schwidefsky } 869641b8ccSMartin Schwidefsky 87f913a660SVasily Gorbik int read_ipl_report(void) 889641b8ccSMartin Schwidefsky { 899641b8ccSMartin Schwidefsky struct ipl_pl_hdr *pl_hdr; 909641b8ccSMartin Schwidefsky struct ipl_rl_hdr *rl_hdr; 919641b8ccSMartin Schwidefsky struct ipl_rb_hdr *rb_hdr; 929641b8ccSMartin Schwidefsky unsigned long tmp; 939641b8ccSMartin Schwidefsky void *rl_end; 949641b8ccSMartin Schwidefsky 959641b8ccSMartin Schwidefsky /* 969641b8ccSMartin Schwidefsky * Check if there is a IPL report by looking at the copy 979641b8ccSMartin Schwidefsky * of the IPL parameter information block. 989641b8ccSMartin Schwidefsky */ 999641b8ccSMartin Schwidefsky if (!ipl_block_valid || 1009641b8ccSMartin Schwidefsky !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)) 101f913a660SVasily Gorbik return -1; 1029641b8ccSMartin Schwidefsky ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL); 1039641b8ccSMartin Schwidefsky /* 1049641b8ccSMartin Schwidefsky * There is an IPL report, to find it load the pointer to the 1059641b8ccSMartin Schwidefsky * IPL parameter information block from lowcore and skip past 1069641b8ccSMartin Schwidefsky * the IPL parameter list, then align the address to a double 1079641b8ccSMartin Schwidefsky * word boundary. 1089641b8ccSMartin Schwidefsky */ 109*bbf78606SSven Schnelle tmp = (unsigned long)get_lowcore()->ipl_parmblock_ptr; 1109641b8ccSMartin Schwidefsky pl_hdr = (struct ipl_pl_hdr *) tmp; 1119641b8ccSMartin Schwidefsky tmp = (tmp + pl_hdr->len + 7) & -8UL; 1129641b8ccSMartin Schwidefsky rl_hdr = (struct ipl_rl_hdr *) tmp; 1139641b8ccSMartin Schwidefsky /* Walk through the IPL report blocks in the IPL Report list */ 1149641b8ccSMartin Schwidefsky certs = NULL; 1159641b8ccSMartin Schwidefsky comps = NULL; 1169641b8ccSMartin Schwidefsky rl_end = (void *) rl_hdr + rl_hdr->len; 1179641b8ccSMartin Schwidefsky rb_hdr = (void *) rl_hdr + sizeof(*rl_hdr); 1189641b8ccSMartin Schwidefsky while ((void *) rb_hdr + sizeof(*rb_hdr) < rl_end && 1199641b8ccSMartin Schwidefsky (void *) rb_hdr + rb_hdr->len <= rl_end) { 1209641b8ccSMartin Schwidefsky 1219641b8ccSMartin Schwidefsky switch (rb_hdr->rbt) { 1229641b8ccSMartin Schwidefsky case IPL_RBT_CERTIFICATES: 1239641b8ccSMartin Schwidefsky certs = (struct ipl_rb_certificates *) rb_hdr; 1249641b8ccSMartin Schwidefsky break; 1259641b8ccSMartin Schwidefsky case IPL_RBT_COMPONENTS: 1269641b8ccSMartin Schwidefsky comps = (struct ipl_rb_components *) rb_hdr; 1279641b8ccSMartin Schwidefsky break; 1289641b8ccSMartin Schwidefsky default: 1299641b8ccSMartin Schwidefsky break; 1309641b8ccSMartin Schwidefsky } 1319641b8ccSMartin Schwidefsky 1329641b8ccSMartin Schwidefsky rb_hdr = (void *) rb_hdr + rb_hdr->len; 1339641b8ccSMartin Schwidefsky } 1349641b8ccSMartin Schwidefsky 1359641b8ccSMartin Schwidefsky /* 1369641b8ccSMartin Schwidefsky * With either the component list or the certificate list 1379641b8ccSMartin Schwidefsky * missing the kernel will stay ignorant of secure IPL. 1389641b8ccSMartin Schwidefsky */ 139f913a660SVasily Gorbik if (!comps || !certs) { 140f913a660SVasily Gorbik certs = NULL; 141f913a660SVasily Gorbik return -1; 142f913a660SVasily Gorbik } 1439641b8ccSMartin Schwidefsky 144f913a660SVasily Gorbik ipl_report_needs_saving = true; 145f913a660SVasily Gorbik physmem_reserve(RR_IPLREPORT, (unsigned long)pl_hdr, 146f913a660SVasily Gorbik (unsigned long)rl_end - (unsigned long)pl_hdr); 147f913a660SVasily Gorbik return 0; 148f913a660SVasily Gorbik } 1499641b8ccSMartin Schwidefsky 150f913a660SVasily Gorbik void save_ipl_cert_comp_list(void) 151f913a660SVasily Gorbik { 152f913a660SVasily Gorbik unsigned long size; 153f913a660SVasily Gorbik 154f913a660SVasily Gorbik if (!ipl_report_needs_saving) 155f913a660SVasily Gorbik return; 156f913a660SVasily Gorbik 157f913a660SVasily Gorbik size = get_cert_comp_list_size(); 158f913a660SVasily Gorbik early_ipl_comp_list_addr = physmem_alloc_top_down(RR_CERT_COMP_LIST, size, sizeof(int)); 159f913a660SVasily Gorbik ipl_cert_list_addr = early_ipl_comp_list_addr + early_ipl_comp_list_size; 160f913a660SVasily Gorbik 161f913a660SVasily Gorbik copy_components_bootdata(); 162f913a660SVasily Gorbik copy_certificates_bootdata(); 163f913a660SVasily Gorbik physmem_free(RR_IPLREPORT); 164f913a660SVasily Gorbik ipl_report_needs_saving = false; 1659641b8ccSMartin Schwidefsky } 166