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