xref: /openbmc/linux/arch/s390/boot/ipl_report.c (revision f913a660)
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