s390x/diag: Introduce DIAG 508 for secure IPL operations
In order to support secure IPL (aka secure boot) for the s390-ccw BIOS, a new s390 DIAGNOSE instruction is introduced to leverage QEMU for handling operations such as signature verification and certificate retrieval. Currently, only subcode 0 is supported with this patch, which is used to query a bitmap of which subcodes are supported. Signed-off-by: Collin Walling <walling@linux.ibm.com> Reviewed-by: Farhan Ali <alifm@linux.ibm.com> Reviewed-by: Thomas Huth <thuth@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260730214624.2328883-12-zycai@linux.ibm.com Signed-off-by: Eric Farman <farman@linux.ibm.com>
Collin Walling committed
Jul 30, 2026 at 17:46 UTC
06b8b50b057a08ed3680599866b69240dc006034
6 files changed
+83
docs/specs/s390x-secure-ipl.rst
+18
@@ -66,3 +66,21 @@ Subcode 2 - store verification certificates
66
contiguously in a VCE (with zero-padding). Following the header, the
67
key-id is immediately stored. The hash and certificate data follow and
68
may be accessed via the respective offset fields stored in the VCE.
69
+
70
+
71
+Secure IPL Data Structures, Facilities, and Functions
72
+-----------------------------------------------------
73
+
74
+DIAGNOSE function code 'X'508' - IPL extensions
75
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76
+
77
+DIAGNOSE 'X'508' is reserved for guest use in order to facilitate communication
78
+of additional IPL operations that cannot be handled by guest code, such as
79
+signature verification for secure IPL.
80
+
81
+If the function code specifies 0x508, IPL extension functions are performed.
82
+These functions are meant to provide extended functionality for s390 guest boot
83
+that requires assistance from QEMU.
84
+
85
+Subcode 0 - query installed subcodes
86
+ Returns a 64-bit mask indicating which subcodes are supported.
include/hw/s390x/ipl/diag508.h
new
+15
@@ -0,0 +1,15 @@
1
+/*
2
+ * S/390 DIAGNOSE 508 definitions and structures
3
+ *
4
+ * Copyright 2025 IBM Corp.
5
+ * Author(s): Collin Walling <walling@linux.ibm.com>
6
+ *
7
+ * SPDX-License-Identifier: GPL-2.0-or-later
8
+ */
9
+
10
+#ifndef S390X_DIAG508_H
11
+#define S390X_DIAG508_H
12
+
13
+#define DIAG_508_SUBC_QUERY_SUBC 0x0000
14
+
15
+#endif
target/s390x/diag.c
+27
@@ -20,6 +20,7 @@
20
#include "hw/s390x/cert-store.h"
21
#include "hw/s390x/ipl.h"
22
#include "hw/s390x/ipl/diag320.h"
23
+#include "hw/s390x/ipl/diag508.h"
24
#include "hw/s390x/s390-virtio-ccw.h"
25
#include "system/kvm.h"
26
#include "kvm/kvm_s390x.h"
@@ -618,3 +619,29 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
619
break;
620
}
621
}
622
+
623
+void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
624
+{
625
+ uint64_t subcode = env->regs[r3];
626
+ int rc;
627
+
628
+ if (env->psw.mask & PSW_MASK_PSTATE) {
629
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
630
+ return;
631
+ }
632
+
633
+ if ((subcode & ~0x0ffffULL) || (r1 & 1)) {
634
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
635
+ return;
636
+ }
637
+
638
+ switch (subcode) {
639
+ case DIAG_508_SUBC_QUERY_SUBC:
640
+ rc = 0;
641
+ break;
642
+ default:
643
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
644
+ return;
645
+ }
646
+ env->regs[r1 + 1] = rc;
647
+}
target/s390x/kvm/kvm.c
+14
@@ -102,6 +102,7 @@
102
#define DIAG_CERT_STORE 0x320
103
#define DIAG_KVM_HYPERCALL 0x500
104
#define DIAG_KVM_BREAKPOINT 0x501
105
+#define DIAG_SECURE_IPL 0x508
106
107
#define ICPT_INSTRUCTION 0x04
108
#define ICPT_PROGRAM 0x08
@@ -1542,6 +1543,16 @@ static void kvm_handle_diag_320(S390CPU *cpu, struct kvm_run *run)
1543
handle_diag_320(&cpu->env, r1, r3, RA_IGNORED);
1544
}
1545
1546
+static void kvm_handle_diag_508(S390CPU *cpu, struct kvm_run *run)
1547
+{
1548
+ uint64_t r1, r3;
1549
+
1550
+ r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
1551
+ r3 = run->s390_sieic.ipa & 0x000f;
1552
+
1553
+ handle_diag_508(&cpu->env, r1, r3, RA_IGNORED);
1554
+}
1555
+
1556
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
1557
1558
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
@@ -1575,6 +1586,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
1586
case DIAG_CERT_STORE:
1587
kvm_handle_diag_320(cpu, run);
1588
break;
1589
+ case DIAG_SECURE_IPL:
1590
+ kvm_handle_diag_508(cpu, run);
1591
+ break;
1592
default:
1593
trace_kvm_insn_diag(func_code);
1594
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
target/s390x/s390x-internal.h
+2
@@ -390,6 +390,8 @@ bool handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3,
390
uintptr_t ra);
391
void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3,
392
uintptr_t ra);
393
+void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3,
394
+ uintptr_t ra);
395
396
397
/* translate.c */
target/s390x/tcg/misc_helper.c
+7
@@ -154,6 +154,13 @@ void HELPER(diag)(CPUS390XState *env, uint32_t r1, uint32_t r3, uint32_t num)
154
bql_unlock();
155
r = 0;
156
break;
157
+ case 0x508:
158
+ /* secure ipl operations */
159
+ bql_lock();
160
+ handle_diag_508(env, r1, r3, GETPC());
161
+ bql_unlock();
162
+ r = 0;
163
+ break;
164
default:
165
r = -1;
166
break;