1
+/*
2
+ * S/390 Secure IPL
3
+ *
4
+ * Functions to support IPL in secure boot mode (DIAG 320, DIAG 508,
5
+ * signature verification, and certificate handling).
6
+ *
7
+ * For secure IPL overview: docs/system/s390x/secure-ipl.rst
8
+ * For secure IPL technical: docs/specs/s390x-secure-ipl.rst
9
+ *
10
+ * Copyright 2025 IBM Corp.
11
+ * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
12
+ *
13
+ * SPDX-License-Identifier: GPL-2.0-or-later
14
+ */
15
+
16
+#include <stdlib.h>
17
+#include <string.h>
18
+#include <stdio.h>
19
+#include "s390-ccw.h"
20
+#include "sclp.h"
21
+#include "secure-ipl.h"
22
+
23
+static VCStorageSizeBlock vcssb __attribute__((__aligned__(8)));
24
+
25
+#define for_each_rb_entry(entry, list) \
26
+ for (entry = (void *)(list) + sizeof((list)->ipl_info_header); \
27
+ (void *)(entry) + sizeof(*(entry)) <= \
28
+ (void *)(list) + (list)->ipl_info_header.len; \
29
+ entry++)
30
+
31
+int zipl_secure_get_vcssb(void)
32
+{
33
+ /* avoid retrieving vcssb multiple times */
34
+ if (vcssb.length == VCSSB_LEN_VALID) {
35
+ goto out;
36
+ }
37
+
38
+ vcssb.length = VCSSB_LEN_VALID;
39
+ if (_diag320(&vcssb, DIAG_320_SUBC_QUERY_VCSI) != DIAG_320_RC_OK) {
40
+ vcssb.length = 0;
41
+ }
42
+
43
+out:
44
+ return vcssb.length;
45
+}
46
+
47
+static uint32_t request_certificate(uint8_t *cert_buf, uint8_t index)
48
+{
49
+ VCEntryHeader *vce_hdr;
50
+ struct vcb {
51
+ VCBlockHeader vcb_hdr;
52
+ struct vce {
53
+ VCEntryHeader vce_hdr;
54
+ uint8_t cert_buf[CERT_BUF_MAX_LEN];
55
+ } vce;
56
+ } __attribute__((__aligned__(PAGE_SIZE))) vcb = { 0 };
57
+
58
+ /*
59
+ * Request single entry
60
+ * Fill input fields of single-entry VCB
61
+ *
62
+ * First and last index must be equal because only one
63
+ * VCE per VCB is currently supported
64
+ */
65
+ vcb.vcb_hdr.in_len = ROUND_UP(vcssb.max_single_vcb_len, PAGE_SIZE);
66
+ vcb.vcb_hdr.first_vc_index = index;
67
+ vcb.vcb_hdr.last_vc_index = index;
68
+
69
+ if (_diag320(&vcb, DIAG_320_SUBC_STORE_VC) != DIAG_320_RC_OK) {
70
+ puts("Could not get certificate");
71
+ return 0;
72
+ }
73
+
74
+ if (vcb.vcb_hdr.out_len == sizeof(VCBlockHeader)) {
75
+ puts("No certificate entry");
76
+ return 0;
77
+ }
78
+
79
+ if (vcb.vcb_hdr.remain_ct != 0) {
80
+ panic("Not enough memory to store requested certificate");
81
+ }
82
+
83
+ vce_hdr = &vcb.vce.vce_hdr;
84
+ if (!(vce_hdr->flags & DIAG_320_VCE_FLAGS_VALID)) {
85
+ puts("Invalid certificate");
86
+ return 0;
87
+ }
88
+
89
+ memcpy(cert_buf, (uint8_t *)&vcb.vce + vce_hdr->cert_offset, vce_hdr->cert_len);
90
+
91
+ return vce_hdr->cert_len;
92
+}
93
+
94
+static int cert_list_add(IplSignatureCertificateList *cert_list,
95
+ IplSignatureCertificateEntry cert_entry)
96
+{
97
+ int cert_entry_idx;
98
+
99
+ cert_entry_idx = (cert_list->ipl_info_header.len - sizeof(IplInfoBlockHeader)) /
100
+ sizeof(IplSignatureCertificateEntry);
101
+
102
+ cert_list->cert_entries[cert_entry_idx] = cert_entry;
103
+ cert_list->ipl_info_header.len += sizeof(IplSignatureCertificateEntry);
104
+
105
+ return cert_entry_idx;
106
+}
107
+
108
+static void comp_list_add(IplDeviceComponentList *comp_list,
109
+ IplDeviceComponentEntry comp_entry)
110
+{
111
+ int comp_entry_idx;
112
+
113
+ comp_entry_idx = (comp_list->ipl_info_header.len - sizeof(IplInfoBlockHeader)) /
114
+ sizeof(IplDeviceComponentEntry);
115
+ if (comp_entry_idx > MAX_COMP_ENTRIES - 1) {
116
+ printf("Warning: only %d component entries are supported\n",
117
+ MAX_COMP_ENTRIES);
118
+ panic("The device component list has reached its maximum capacity");
119
+ }
120
+
121
+ comp_list->device_entries[comp_entry_idx] = comp_entry;
122
+ comp_list->ipl_info_header.len += sizeof(IplDeviceComponentEntry);
123
+}
124
+
125
+void update_iirb(IplDeviceComponentList *comp_list,
126
+ IplSignatureCertificateList *cert_list)
127
+{
128
+ IplInfoReportBlock *iirb;
129
+ IplDeviceComponentList *iirb_comps;
130
+ IplSignatureCertificateList *iirb_certs;
131
+ uint32_t iirb_hdr_len;
132
+ uint32_t comps_len;
133
+ uint32_t certs_len;
134
+
135
+ if (iplb->len % 8 != 0) {
136
+ panic("IPL parameter block length field value is not multiple of 8 bytes");
137
+ }
138
+
139
+ iirb_hdr_len = sizeof(IplInfoReportBlockHeader);
140
+ comps_len = comp_list->ipl_info_header.len;
141
+ certs_len = cert_list->ipl_info_header.len;
142
+ if ((comps_len + certs_len + iirb_hdr_len) > sizeof(IplInfoReportBlock)) {
143
+ panic("Not enough space to hold all components and certificates in IIRB");
144
+ }
145
+
146
+ /* IIRB immediately follows IPLB */
147
+ iirb = &ipl_blocks.iirb;
148
+ iirb->hdr.len = iirb_hdr_len;
149
+
150
+ /* Copy IPL device component list after IIRB Header */
151
+ iirb_comps = (IplDeviceComponentList *) iirb->info_blks;
152
+ memcpy(iirb_comps, comp_list, comps_len);
153
+
154
+ /* Update IIRB length */
155
+ iirb->hdr.len += comps_len;
156
+
157
+ /* Copy IPL sig cert list after IPL device component list */
158
+ iirb_certs = (IplSignatureCertificateList *) (iirb->info_blks +
159
+ iirb_comps->ipl_info_header.len);
160
+ memcpy(iirb_certs, cert_list, certs_len);
161
+
162
+ /* Update IIRB length */
163
+ iirb->hdr.len += certs_len;
164
+}
165
+
166
+bool secure_ipl_supported(void)
167
+{
168
+ if (!sclp_is_fac_ipl_flag_on(SCCB_FAC_IPL_SIPL_BIT)) {
169
+ puts("Secure IPL Facility is not supported by the hypervisor!");
170
+ return false;
171
+ }
172
+
173
+ if (!is_signature_verif_supported()) {
174
+ puts("Secure IPL extensions are not supported by the hypervisor!");
175
+ return false;
176
+ }
177
+
178
+ if (!is_cert_store_facility_supported()) {
179
+ puts("Certificate Store Facility is not supported by the hypervisor!");
180
+ return false;
181
+ }
182
+
183
+ return true;
184
+}
185
+
186
+static void init_lists(IplDeviceComponentList *comp_list,
187
+ IplSignatureCertificateList *cert_list)
188
+{
189
+ comp_list->ipl_info_header.type = IPL_INFO_BLOCK_TYPE_COMPONENTS;
190
+ comp_list->ipl_info_header.len = sizeof(IplInfoBlockHeader);
191
+
192
+ cert_list->ipl_info_header.type = IPL_INFO_BLOCK_TYPE_CERTIFICATES;
193
+ cert_list->ipl_info_header.len = sizeof(IplInfoBlockHeader);
194
+}
195
+
196
+static int zipl_load_signature(ComponentEntry *entry, uint64_t sig)
197
+{
198
+ if (entry->compdat.sig_info.format != DER_SIGNATURE_FORMAT) {
199
+ puts("Signature is not in DER format");
200
+ return -1;
201
+ }
202
+
203
+ if (zipl_load_segment(entry->data.blockno, sig) < 0) {
204
+ return -1;
205
+ }
206
+
207
+ return entry->compdat.sig_info.sig_len;
208
+}
209
+
210
+void update_cert_list(IplSignatureCertificateList *cert_list)
211
+{
212
+ IplSignatureCertificateEntry *cert_entry;
213
+ uint8_t *cert_buf;
214
+
215
+ /*
216
+ * Recover the original base address of ipl_data for cert storage.
217
+ *
218
+ * The IplParameterBlocks stored in ipl_data will no longer be needed
219
+ * after this point. Reuse this region to store certificates from the
220
+ * BIOS heap into stable memory.
221
+ */
222
+ cert_buf = (uint8_t *)qipl.ipl_data - qipl.index * sizeof(IplParameterBlock);
223
+
224
+ for_each_rb_entry(cert_entry, cert_list) {
225
+ memcpy(cert_buf, (uint8_t *)cert_entry->addr, cert_entry->len);
226
+ cert_entry->addr = (uint64_t)cert_buf;
227
+ cert_buf += cert_entry->len;
228
+ }
229
+}
230
+
231
+int zipl_run_secure(ComponentEntry **entry_ptr, const uint8_t *tmp_sec,
232
+ IplDeviceComponentList *comp_list,
233
+ IplSignatureCertificateList *cert_list,
234
+ uint8_t **tmp_cert_buf)
235
+{
236
+ /*
237
+ * Keep track of which certificate store indices correspond to the
238
+ * certificate data entries within the IplSignatureCertificateList to
239
+ * prevent allocating space for the same certificate multiple times.
240
+ *
241
+ * The array index corresponds to the certificate's cert-store index.
242
+ *
243
+ * The array value corresponds to the certificate's entry within the
244
+ * IplSignatureCertificateList (with a value of -1 denoting no entry
245
+ * exists for the certificate).
246
+ */
247
+ int cert_list_table[vcssb.total_vc_ct + 1];
248
+ IplSignatureCertificateEntry sig_entry = { 0 };
249
+ IplSignatureCertificateEntry cert_entry;
250
+ IplDeviceComponentEntry comp_entry;
251
+ ComponentEntry *entry = *entry_ptr;
252
+ int rc = -1;
253
+ int sig_len = 0;
254
+ int comp_len;
255
+ int cert_entry_idx;
256
+ uint64_t comp_addr;
257
+ uint8_t cert_table_idx;
258
+ uint8_t *tmp_buf;
259
+ bool verified;
260
+ bool signed_found = false;
261
+
262
+ if ((MAX_SIGNED_COMP * CERT_BUF_MAX_LEN) > CERT_BUF_SIZE) {
263
+ panic("Not enough memory to store certificates");
264
+ }
265
+ *tmp_cert_buf = malloc(CERT_BUF_SIZE);
266
+ tmp_buf = *tmp_cert_buf;
267
+
268
+ init_lists(comp_list, cert_list);
269
+ sig_entry.addr = (uint64_t)malloc(MAX_SECTOR_SIZE);
270
+ memset(cert_list_table, -1, sizeof(cert_list_table));
271
+
272
+ while (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
273
+ switch (entry->component_type) {
274
+ case ZIPL_COMP_ENTRY_SIGNATURE:
275
+ if (sig_entry.len) {
276
+ goto error;
277
+ }
278
+
279
+ sig_len = zipl_load_signature(entry, sig_entry.addr);
280
+ if (sig_len < 0) {
281
+ goto error;
282
+ }
283
+
284
+ sig_entry.len = sig_len;
285
+ break;
286
+ case ZIPL_COMP_ENTRY_LOAD:
287
+ comp_addr = entry->compdat.load_addr;
288
+ comp_len = zipl_load_segment(entry->data.blockno, comp_addr);
289
+ if (comp_len < 0) {
290
+ goto error;
291
+ }
292
+
293
+ comp_entry = (IplDeviceComponentEntry){ 0 };
294
+ comp_entry.addr = comp_addr;
295
+ comp_entry.len = (uint64_t)comp_len;
296
+
297
+ /* no signature present (unsigned component) */
298
+ if (!sig_entry.len) {
299
+ comp_list_add(comp_list, comp_entry);
300
+ break;
301
+ }
302
+
303
+ /*
304
+ * Initialize with SC flag (signed component)
305
+ * CSV flag set upon successful verification
306
+ */
307
+ comp_entry.flags = S390_IPL_DEV_COMP_FLAG_SC;
308
+ signed_found = true;
309
+
310
+ cert_entry = (IplSignatureCertificateEntry) { 0 };
311
+ verified = verify_signature(comp_entry, sig_entry,
312
+ &cert_entry.len, &cert_table_idx);
313
+
314
+ if (verified) {
315
+ if (cert_list_table[cert_table_idx] == -1) {
316
+ if (!request_certificate(tmp_buf, cert_table_idx)) {
317
+ puts("Could not get certificate");
318
+ goto error;
319
+ }
320
+
321
+ cert_entry.addr = (uint64_t)tmp_buf;
322
+ cert_entry_idx = cert_list_add(cert_list, cert_entry);
323
+ /* map cert-store index to cert-list entry index */
324
+ cert_list_table[cert_table_idx] = cert_entry_idx;
325
+ /* increment for the next certificate */
326
+ tmp_buf += cert_entry.len;
327
+ }
328
+
329
+ comp_entry.cert_index = cert_list_table[cert_table_idx];
330
+ comp_entry.flags |= S390_IPL_DEV_COMP_FLAG_CSV;
331
+ puts("Verified component");
332
+ } else {
333
+ zipl_secure_error("Could not verify component");
334
+ }
335
+
336
+ comp_list_add(comp_list, comp_entry);
337
+
338
+ /* After a signature is used another new one can be accepted */
339
+ sig_entry.len = 0;
340
+ break;
341
+ default:
342
+ puts("Unknown component entry type");
343
+ goto error;
344
+ }
345
+
346
+ entry++;
347
+
348
+ if ((uint8_t *)(&entry[1]) > tmp_sec + MAX_SECTOR_SIZE) {
349
+ puts("Wrong entry value");
350
+ rc = -EINVAL;
351
+ goto error;
352
+ }
353
+ }
354
+
355
+ if (!signed_found) {
356
+ zipl_secure_error("Secure boot is on, but components are not signed");
357
+ }
358
+
359
+ *entry_ptr = entry;
360
+ free((void *)sig_entry.addr);
361
+
362
+ return 0;
363
+error:
364
+ free(*tmp_cert_buf);
365
+ *tmp_cert_buf = NULL;
366
+ free((void *)sig_entry.addr);
367
+
368
+ return rc;
369
+}