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

source code of linux/arch/s390/boot/ipl_report.c