1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/init.h>
3 #include <linux/ctype.h>
4 #include <asm/ebcdic.h>
6 #include <asm/sections.h>
7 #include <asm/boot_data.h>
8 #include <uapi/asm/ipl.h>
11 int __bootdata_preserved(ipl_secure_flag
);
13 unsigned long __bootdata_preserved(ipl_cert_list_addr
);
14 unsigned long __bootdata_preserved(ipl_cert_list_size
);
16 unsigned long __bootdata(early_ipl_comp_list_addr
);
17 unsigned long __bootdata(early_ipl_comp_list_size
);
19 #define for_each_rb_entry(entry, rb) \
20 for (entry = rb->entries; \
21 (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \
24 static inline bool intersects(unsigned long addr0
, unsigned long size0
,
25 unsigned long addr1
, unsigned long size1
)
27 return addr0
+ size0
> addr1
&& addr1
+ size1
> addr0
;
30 static unsigned long find_bootdata_space(struct ipl_rb_components
*comps
,
31 struct ipl_rb_certificates
*certs
,
32 unsigned long safe_addr
)
34 struct ipl_rb_certificate_entry
*cert
;
35 struct ipl_rb_component_entry
*comp
;
39 * Find the length for the IPL report boot data
41 early_ipl_comp_list_size
= 0;
42 for_each_rb_entry(comp
, comps
)
43 early_ipl_comp_list_size
+= sizeof(*comp
);
44 ipl_cert_list_size
= 0;
45 for_each_rb_entry(cert
, certs
)
46 ipl_cert_list_size
+= sizeof(unsigned int) + cert
->len
;
47 size
= ipl_cert_list_size
+ early_ipl_comp_list_size
;
50 * Start from safe_addr to find a free memory area large
51 * enough for the IPL report boot data. This area is used
52 * for ipl_cert_list_addr/ipl_cert_list_size and
53 * early_ipl_comp_list_addr/early_ipl_comp_list_size. It must
54 * not overlap with any component or any certificate.
57 if (IS_ENABLED(CONFIG_BLK_DEV_INITRD
) && INITRD_START
&& INITRD_SIZE
&&
58 intersects(INITRD_START
, INITRD_SIZE
, safe_addr
, size
))
59 safe_addr
= INITRD_START
+ INITRD_SIZE
;
60 for_each_rb_entry(comp
, comps
)
61 if (intersects(safe_addr
, size
, comp
->addr
, comp
->len
)) {
62 safe_addr
= comp
->addr
+ comp
->len
;
65 for_each_rb_entry(cert
, certs
)
66 if (intersects(safe_addr
, size
, cert
->addr
, cert
->len
)) {
67 safe_addr
= cert
->addr
+ cert
->len
;
70 early_ipl_comp_list_addr
= safe_addr
;
71 ipl_cert_list_addr
= safe_addr
+ early_ipl_comp_list_size
;
73 return safe_addr
+ size
;
76 static void copy_components_bootdata(struct ipl_rb_components
*comps
)
78 struct ipl_rb_component_entry
*comp
, *ptr
;
80 ptr
= (struct ipl_rb_component_entry
*) early_ipl_comp_list_addr
;
81 for_each_rb_entry(comp
, comps
)
82 memcpy(ptr
++, comp
, sizeof(*ptr
));
85 static void copy_certificates_bootdata(struct ipl_rb_certificates
*certs
)
87 struct ipl_rb_certificate_entry
*cert
;
90 ptr
= (void *) ipl_cert_list_addr
;
91 for_each_rb_entry(cert
, certs
) {
92 *(unsigned int *) ptr
= cert
->len
;
93 ptr
+= sizeof(unsigned int);
94 memcpy(ptr
, (void *) cert
->addr
, cert
->len
);
99 unsigned long read_ipl_report(unsigned long safe_addr
)
101 struct ipl_rb_certificates
*certs
;
102 struct ipl_rb_components
*comps
;
103 struct ipl_pl_hdr
*pl_hdr
;
104 struct ipl_rl_hdr
*rl_hdr
;
105 struct ipl_rb_hdr
*rb_hdr
;
110 * Check if there is a IPL report by looking at the copy
111 * of the IPL parameter information block.
113 if (!ipl_block_valid
||
114 !(ipl_block
.hdr
.flags
& IPL_PL_FLAG_IPLSR
))
116 ipl_secure_flag
= !!(ipl_block
.hdr
.flags
& IPL_PL_FLAG_SIPL
);
118 * There is an IPL report, to find it load the pointer to the
119 * IPL parameter information block from lowcore and skip past
120 * the IPL parameter list, then align the address to a double
123 tmp
= (unsigned long) S390_lowcore
.ipl_parmblock_ptr
;
124 pl_hdr
= (struct ipl_pl_hdr
*) tmp
;
125 tmp
= (tmp
+ pl_hdr
->len
+ 7) & -8UL;
126 rl_hdr
= (struct ipl_rl_hdr
*) tmp
;
127 /* Walk through the IPL report blocks in the IPL Report list */
130 rl_end
= (void *) rl_hdr
+ rl_hdr
->len
;
131 rb_hdr
= (void *) rl_hdr
+ sizeof(*rl_hdr
);
132 while ((void *) rb_hdr
+ sizeof(*rb_hdr
) < rl_end
&&
133 (void *) rb_hdr
+ rb_hdr
->len
<= rl_end
) {
135 switch (rb_hdr
->rbt
) {
136 case IPL_RBT_CERTIFICATES
:
137 certs
= (struct ipl_rb_certificates
*) rb_hdr
;
139 case IPL_RBT_COMPONENTS
:
140 comps
= (struct ipl_rb_components
*) rb_hdr
;
146 rb_hdr
= (void *) rb_hdr
+ rb_hdr
->len
;
150 * With either the component list or the certificate list
151 * missing the kernel will stay ignorant of secure IPL.
153 if (!comps
|| !certs
)
157 * Copy component and certificate list to a safe area
158 * where the decompressed kernel can find them.
160 safe_addr
= find_bootdata_space(comps
, certs
, safe_addr
);
161 copy_components_bootdata(comps
);
162 copy_certificates_bootdata(certs
);