Merge patch series "riscv: some CMO alternative related clean up"
[linux-block.git] / arch / s390 / boot / ipl_report.c
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(RR_IPLREPORT, (unsigned long)pl_hdr,
146                         (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(RR_IPLREPORT);
164         ipl_report_needs_saving = false;
165 }