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 | |
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 | 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 | |
47 | bool 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 | |
64 | static 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 | |
73 | static 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 | |
87 | int 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 | |
150 | void 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 | |