]>
Commit | Line | Data |
---|---|---|
9641b8cc MS |
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 <uapi/asm/ipl.h> | |
9 | #include "boot.h" | |
10 | ||
11 | int __bootdata_preserved(ipl_secure_flag); | |
12 | ||
13 | unsigned long __bootdata_preserved(ipl_cert_list_addr); | |
14 | unsigned long __bootdata_preserved(ipl_cert_list_size); | |
15 | ||
16 | unsigned long __bootdata(early_ipl_comp_list_addr); | |
17 | unsigned long __bootdata(early_ipl_comp_list_size); | |
18 | ||
19 | #define for_each_rb_entry(entry, rb) \ | |
20 | for (entry = rb->entries; \ | |
21 | (void *) entry + sizeof(*entry) <= (void *) rb + rb->len; \ | |
22 | entry++) | |
23 | ||
24 | static inline bool intersects(unsigned long addr0, unsigned long size0, | |
25 | unsigned long addr1, unsigned long size1) | |
26 | { | |
27 | return addr0 + size0 > addr1 && addr1 + size1 > addr0; | |
28 | } | |
29 | ||
30 | static unsigned long find_bootdata_space(struct ipl_rb_components *comps, | |
31 | struct ipl_rb_certificates *certs, | |
32 | unsigned long safe_addr) | |
33 | { | |
34 | struct ipl_rb_certificate_entry *cert; | |
35 | struct ipl_rb_component_entry *comp; | |
36 | size_t size; | |
37 | ||
38 | /* | |
39 | * Find the length for the IPL report boot data | |
40 | */ | |
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; | |
48 | ||
49 | /* | |
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. | |
55 | */ | |
56 | repeat: | |
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; | |
63 | goto repeat; | |
64 | } | |
65 | for_each_rb_entry(cert, certs) | |
66 | if (intersects(safe_addr, size, cert->addr, cert->len)) { | |
67 | safe_addr = cert->addr + cert->len; | |
68 | goto repeat; | |
69 | } | |
70 | early_ipl_comp_list_addr = safe_addr; | |
71 | ipl_cert_list_addr = safe_addr + early_ipl_comp_list_size; | |
72 | ||
73 | return safe_addr + size; | |
74 | } | |
75 | ||
76 | static void copy_components_bootdata(struct ipl_rb_components *comps) | |
77 | { | |
78 | struct ipl_rb_component_entry *comp, *ptr; | |
79 | ||
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)); | |
83 | } | |
84 | ||
85 | static void copy_certificates_bootdata(struct ipl_rb_certificates *certs) | |
86 | { | |
87 | struct ipl_rb_certificate_entry *cert; | |
88 | void *ptr; | |
89 | ||
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); | |
95 | ptr += cert->len; | |
96 | } | |
97 | } | |
98 | ||
99 | unsigned long read_ipl_report(unsigned long safe_addr) | |
100 | { | |
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; | |
106 | unsigned long tmp; | |
107 | void *rl_end; | |
108 | ||
109 | /* | |
110 | * Check if there is a IPL report by looking at the copy | |
111 | * of the IPL parameter information block. | |
112 | */ | |
113 | if (!ipl_block_valid || | |
114 | !(ipl_block.hdr.flags & IPL_PL_FLAG_IPLSR)) | |
115 | return safe_addr; | |
116 | ipl_secure_flag = !!(ipl_block.hdr.flags & IPL_PL_FLAG_SIPL); | |
117 | /* | |
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 | |
121 | * word boundary. | |
122 | */ | |
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 */ | |
128 | certs = NULL; | |
129 | comps = NULL; | |
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) { | |
134 | ||
135 | switch (rb_hdr->rbt) { | |
136 | case IPL_RBT_CERTIFICATES: | |
137 | certs = (struct ipl_rb_certificates *) rb_hdr; | |
138 | break; | |
139 | case IPL_RBT_COMPONENTS: | |
140 | comps = (struct ipl_rb_components *) rb_hdr; | |
141 | break; | |
142 | default: | |
143 | break; | |
144 | } | |
145 | ||
146 | rb_hdr = (void *) rb_hdr + rb_hdr->len; | |
147 | } | |
148 | ||
149 | /* | |
150 | * With either the component list or the certificate list | |
151 | * missing the kernel will stay ignorant of secure IPL. | |
152 | */ | |
153 | if (!comps || !certs) | |
154 | return safe_addr; | |
155 | ||
156 | /* | |
157 | * Copy component and certificate list to a safe area | |
158 | * where the decompressed kernel can find them. | |
159 | */ | |
160 | safe_addr = find_bootdata_space(comps, certs, safe_addr); | |
161 | copy_components_bootdata(comps); | |
162 | copy_certificates_bootdata(certs); | |
163 | ||
164 | return safe_addr; | |
165 | } |