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>
8*f913a660SVasily 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
20*f913a660SVasily Gorbik static struct ipl_rb_certificates *certs;
21*f913a660SVasily Gorbik static struct ipl_rb_components *comps;
22*f913a660SVasily Gorbik static bool ipl_report_needs_saving;
23*f913a660SVasily 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)29*f913a660SVasily 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;
44*f913a660SVasily 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)47*f913a660SVasily Gorbik bool ipl_report_certs_intersects(unsigned long addr, unsigned long size,
48*f913a660SVasily Gorbik unsigned long *intersection_start)
49*f913a660SVasily Gorbik {
50*f913a660SVasily Gorbik struct ipl_rb_certificate_entry *cert;
51*f913a660SVasily Gorbik
52*f913a660SVasily Gorbik if (!ipl_report_needs_saving)
53*f913a660SVasily Gorbik return false;
54*f913a660SVasily Gorbik
55*f913a660SVasily Gorbik for_each_rb_entry(cert, certs) {
56*f913a660SVasily Gorbik if (intersects(addr, size, cert->addr, cert->len)) {
57*f913a660SVasily Gorbik *intersection_start = cert->addr;
58*f913a660SVasily Gorbik return true;
59*f913a660SVasily Gorbik }
60*f913a660SVasily Gorbik }
61*f913a660SVasily Gorbik return false;
62*f913a660SVasily Gorbik }
63*f913a660SVasily Gorbik
copy_components_bootdata(void)64*f913a660SVasily 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)73*f913a660SVasily 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)87*f913a660SVasily 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))
101*f913a660SVasily 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 */
1099641b8ccSMartin Schwidefsky tmp = (unsigned long) S390_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 */
139*f913a660SVasily Gorbik if (!comps || !certs) {
140*f913a660SVasily Gorbik certs = NULL;
141*f913a660SVasily Gorbik return -1;
142*f913a660SVasily Gorbik }
1439641b8ccSMartin Schwidefsky
144*f913a660SVasily Gorbik ipl_report_needs_saving = true;
145*f913a660SVasily Gorbik physmem_reserve(RR_IPLREPORT, (unsigned long)pl_hdr,
146*f913a660SVasily Gorbik (unsigned long)rl_end - (unsigned long)pl_hdr);
147*f913a660SVasily Gorbik return 0;
148*f913a660SVasily Gorbik }
1499641b8ccSMartin Schwidefsky
save_ipl_cert_comp_list(void)150*f913a660SVasily Gorbik void save_ipl_cert_comp_list(void)
151*f913a660SVasily Gorbik {
152*f913a660SVasily Gorbik unsigned long size;
153*f913a660SVasily Gorbik
154*f913a660SVasily Gorbik if (!ipl_report_needs_saving)
155*f913a660SVasily Gorbik return;
156*f913a660SVasily Gorbik
157*f913a660SVasily Gorbik size = get_cert_comp_list_size();
158*f913a660SVasily Gorbik early_ipl_comp_list_addr = physmem_alloc_top_down(RR_CERT_COMP_LIST, size, sizeof(int));
159*f913a660SVasily Gorbik ipl_cert_list_addr = early_ipl_comp_list_addr + early_ipl_comp_list_size;
160*f913a660SVasily Gorbik
161*f913a660SVasily Gorbik copy_components_bootdata();
162*f913a660SVasily Gorbik copy_certificates_bootdata();
163*f913a660SVasily Gorbik physmem_free(RR_IPLREPORT);
164*f913a660SVasily Gorbik ipl_report_needs_saving = false;
1659641b8ccSMartin Schwidefsky }
166