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
get_cert_comp_list_size(void)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
ipl_report_certs_intersects(unsigned long addr,unsigned long size,unsigned long * intersection_start)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
copy_components_bootdata(void)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
copy_certificates_bootdata(void)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
read_ipl_report(void)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
save_ipl_cert_comp_list(void)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