* [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 10:44 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 02/24] hw/s390x/ipl: Create certificate store Zhuoying Cai
` (22 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The `-boot-certificates /path/dir:/path/file` option is implemented
to provide path to either a directory or a single certificate.
Multiple paths can be delineated using a colon.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
qemu-options.hx | 11 +++++++++++
system/vl.c | 22 ++++++++++++++++++++++
2 files changed, 33 insertions(+)
diff --git a/qemu-options.hx b/qemu-options.hx
index dc694a99a3..b460c63490 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1251,6 +1251,17 @@ SRST
Set system UUID.
ERST
+DEF("boot-certificates", HAS_ARG, QEMU_OPTION_boot_certificates,
+ "-boot-certificates /path/directory:/path/file\n"
+ " Provide a path to a directory or a boot certificate.\n"
+ " A colon may be used to delineate multiple paths.\n",
+ QEMU_ARCH_S390X)
+SRST
+``-boot-certificates /path/directory:/path/file``
+ Provide a path to a directory or a boot certificate.
+ A colon may be used to delineate multiple paths.
+ERST
+
DEFHEADING()
DEFHEADING(Block device options:)
diff --git a/system/vl.c b/system/vl.c
index ec93988a03..bd6197c887 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -510,6 +510,20 @@ static QemuOptsList qemu_action_opts = {
},
};
+static QemuOptsList qemu_boot_certificates_opts = {
+ .name = "boot-certificates",
+ .implied_opt_name = "boot-certificates",
+ .merge_lists = true,
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_boot_certificates_opts.head),
+ .desc = {
+ {
+ .name = "boot-certificates",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */}
+ },
+};
+
const char *qemu_get_vm_name(void)
{
return qemu_name;
@@ -2879,6 +2893,7 @@ void qemu_init(int argc, char **argv)
qemu_add_opts(&qemu_semihosting_config_opts);
qemu_add_opts(&qemu_fw_cfg_opts);
qemu_add_opts(&qemu_action_opts);
+ qemu_add_opts(&qemu_boot_certificates_opts);
qemu_add_run_with_opts();
module_call_init(MODULE_INIT_OPTS);
@@ -3024,6 +3039,13 @@ void qemu_init(int argc, char **argv)
case QEMU_OPTION_boot:
machine_parse_property_opt(qemu_find_opts("boot-opts"), "boot", optarg);
break;
+ case QEMU_OPTION_boot_certificates:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("boot-certificates"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
case QEMU_OPTION_fda:
case QEMU_OPTION_fdb:
drive_add(IF_FLOPPY, popt->index - QEMU_OPTION_fda,
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line
2025-04-08 15:55 ` [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line Zhuoying Cai
@ 2025-04-11 10:44 ` Thomas Huth
2025-04-11 12:57 ` Daniel P. Berrangé
0 siblings, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 10:44 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> The `-boot-certificates /path/dir:/path/file` option is implemented
> to provide path to either a directory or a single certificate.
>
> Multiple paths can be delineated using a colon.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> qemu-options.hx | 11 +++++++++++
> system/vl.c | 22 ++++++++++++++++++++++
> 2 files changed, 33 insertions(+)
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index dc694a99a3..b460c63490 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1251,6 +1251,17 @@ SRST
> Set system UUID.
> ERST
>
> +DEF("boot-certificates", HAS_ARG, QEMU_OPTION_boot_certificates,
> + "-boot-certificates /path/directory:/path/file\n"
> + " Provide a path to a directory or a boot certificate.\n"
> + " A colon may be used to delineate multiple paths.\n",
> + QEMU_ARCH_S390X)
> +SRST
> +``-boot-certificates /path/directory:/path/file``
> + Provide a path to a directory or a boot certificate.
> + A colon may be used to delineate multiple paths.
> +ERST
Unless there is a really, really good reason for introducing new top-level
options to QEMU, this should rather be added to one of the existing options
instead.
I assume this is very specific to s390x, isn't it? So the best way is likely
to add this as a parameter of the machine type option, so that the user
would specify:
qemu-system-s390x -machine s390-ccw-virtio,boot-certificates=/path/to/certs
See the other object_class_property_add() statements in
ccw_machine_class_init() for some examples how to do this.
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line
2025-04-11 10:44 ` Thomas Huth
@ 2025-04-11 12:57 ` Daniel P. Berrangé
2025-04-11 13:33 ` Daniel P. Berrangé
2025-04-11 17:45 ` Zhuoying Cai
0 siblings, 2 replies; 54+ messages in thread
From: Daniel P. Berrangé @ 2025-04-11 12:57 UTC (permalink / raw)
To: Thomas Huth
Cc: Zhuoying Cai, richard.henderson, david, pbonzini, walling,
jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On Fri, Apr 11, 2025 at 12:44:17PM +0200, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
> > The `-boot-certificates /path/dir:/path/file` option is implemented
> > to provide path to either a directory or a single certificate.
> >
> > Multiple paths can be delineated using a colon.
> >
> > Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> > ---
> > qemu-options.hx | 11 +++++++++++
> > system/vl.c | 22 ++++++++++++++++++++++
> > 2 files changed, 33 insertions(+)
> >
> > diff --git a/qemu-options.hx b/qemu-options.hx
> > index dc694a99a3..b460c63490 100644
> > --- a/qemu-options.hx
> > +++ b/qemu-options.hx
> > @@ -1251,6 +1251,17 @@ SRST
> > Set system UUID.
> > ERST
> > +DEF("boot-certificates", HAS_ARG, QEMU_OPTION_boot_certificates,
> > + "-boot-certificates /path/directory:/path/file\n"
> > + " Provide a path to a directory or a boot certificate.\n"
> > + " A colon may be used to delineate multiple paths.\n",
> > + QEMU_ARCH_S390X)
> > +SRST
> > +``-boot-certificates /path/directory:/path/file``
> > + Provide a path to a directory or a boot certificate.
> > + A colon may be used to delineate multiple paths.
> > +ERST
>
> Unless there is a really, really good reason for introducing new top-level
> options to QEMU, this should rather be added to one of the existing options
> instead.
>
> I assume this is very specific to s390x, isn't it? So the best way is likely
> to add this as a parameter of the machine type option, so that the user
> would specify:
>
> qemu-system-s390x -machine s390-ccw-virtio,boot-certificates=/path/to/certs
>
> See the other object_class_property_add() statements in
> ccw_machine_class_init() for some examples how to do this.
With other arches that use EDK2 (x86, arm64, riscv64, loongarch64) we
pass this info via fw_cfg
-fw_cfg name=etc/edk2/https/cacerts,file=<certdb>
Assuming this series is trying to implement a pre-existing s390x machine
standard for passing certs, then it seems inevitable that it will need
a different config approach than we use for EDK2.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line
2025-04-11 12:57 ` Daniel P. Berrangé
@ 2025-04-11 13:33 ` Daniel P. Berrangé
2025-04-11 17:45 ` Zhuoying Cai
1 sibling, 0 replies; 54+ messages in thread
From: Daniel P. Berrangé @ 2025-04-11 13:33 UTC (permalink / raw)
To: Thomas Huth, Zhuoying Cai, richard.henderson, david, pbonzini,
walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On Fri, Apr 11, 2025 at 01:57:26PM +0100, Daniel P. Berrangé wrote:
> On Fri, Apr 11, 2025 at 12:44:17PM +0200, Thomas Huth wrote:
> > On 08/04/2025 17.55, Zhuoying Cai wrote:
> > > The `-boot-certificates /path/dir:/path/file` option is implemented
> > > to provide path to either a directory or a single certificate.
> > >
> > > Multiple paths can be delineated using a colon.
> > >
> > > Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> > > ---
> > > qemu-options.hx | 11 +++++++++++
> > > system/vl.c | 22 ++++++++++++++++++++++
> > > 2 files changed, 33 insertions(+)
> > >
> > > diff --git a/qemu-options.hx b/qemu-options.hx
> > > index dc694a99a3..b460c63490 100644
> > > --- a/qemu-options.hx
> > > +++ b/qemu-options.hx
> > > @@ -1251,6 +1251,17 @@ SRST
> > > Set system UUID.
> > > ERST
> > > +DEF("boot-certificates", HAS_ARG, QEMU_OPTION_boot_certificates,
> > > + "-boot-certificates /path/directory:/path/file\n"
> > > + " Provide a path to a directory or a boot certificate.\n"
> > > + " A colon may be used to delineate multiple paths.\n",
> > > + QEMU_ARCH_S390X)
> > > +SRST
> > > +``-boot-certificates /path/directory:/path/file``
> > > + Provide a path to a directory or a boot certificate.
> > > + A colon may be used to delineate multiple paths.
> > > +ERST
> >
> > Unless there is a really, really good reason for introducing new top-level
> > options to QEMU, this should rather be added to one of the existing options
> > instead.
> >
> > I assume this is very specific to s390x, isn't it? So the best way is likely
> > to add this as a parameter of the machine type option, so that the user
> > would specify:
> >
> > qemu-system-s390x -machine s390-ccw-virtio,boot-certificates=/path/to/certs
> >
> > See the other object_class_property_add() statements in
> > ccw_machine_class_init() for some examples how to do this.
>
> With other arches that use EDK2 (x86, arm64, riscv64, loongarch64) we
> pass this info via fw_cfg
s/this info/this kind of info/
because technically the stuff below is certs for PXE boot downloads,
not certs for secureboot. The latter are hardcoded in the EDK varstore
at boot time, so any setup of certs for secureboot is out of band
from QEMU startup
>
> -fw_cfg name=etc/edk2/https/cacerts,file=<certdb>
>
> Assuming this series is trying to implement a pre-existing s390x machine
> standard for passing certs, then it seems inevitable that it will need
> a different config approach than we use for EDK2.
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line
2025-04-11 12:57 ` Daniel P. Berrangé
2025-04-11 13:33 ` Daniel P. Berrangé
@ 2025-04-11 17:45 ` Zhuoying Cai
1 sibling, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-11 17:45 UTC (permalink / raw)
To: Daniel P. Berrangé, Thomas Huth
Cc: richard.henderson, david, pbonzini, walling, jjherne, jrossi,
fiuczy, pasic, borntraeger, farman, iii, qemu-s390x, qemu-devel
On 4/11/25 8:57 AM, Daniel P. Berrangé wrote:
> On Fri, Apr 11, 2025 at 12:44:17PM +0200, Thomas Huth wrote:
>> On 08/04/2025 17.55, Zhuoying Cai wrote:
>>> The `-boot-certificates /path/dir:/path/file` option is implemented
>>> to provide path to either a directory or a single certificate.
>>>
>>> Multiple paths can be delineated using a colon.
>>>
>>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>>> ---
>>> qemu-options.hx | 11 +++++++++++
>>> system/vl.c | 22 ++++++++++++++++++++++
>>> 2 files changed, 33 insertions(+)
>>>
>>> diff --git a/qemu-options.hx b/qemu-options.hx
>>> index dc694a99a3..b460c63490 100644
>>> --- a/qemu-options.hx
>>> +++ b/qemu-options.hx
>>> @@ -1251,6 +1251,17 @@ SRST
>>> Set system UUID.
>>> ERST
>>> +DEF("boot-certificates", HAS_ARG, QEMU_OPTION_boot_certificates,
>>> + "-boot-certificates /path/directory:/path/file\n"
>>> + " Provide a path to a directory or a boot certificate.\n"
>>> + " A colon may be used to delineate multiple paths.\n",
>>> + QEMU_ARCH_S390X)
>>> +SRST
>>> +``-boot-certificates /path/directory:/path/file``
>>> + Provide a path to a directory or a boot certificate.
>>> + A colon may be used to delineate multiple paths.
>>> +ERST
>>
>> Unless there is a really, really good reason for introducing new top-level
>> options to QEMU, this should rather be added to one of the existing options
>> instead.
>>
>> I assume this is very specific to s390x, isn't it? So the best way is likely
>> to add this as a parameter of the machine type option, so that the user
>> would specify:
>>
>> qemu-system-s390x -machine s390-ccw-virtio,boot-certificates=/path/to/certs
>>
>> See the other object_class_property_add() statements in
>> ccw_machine_class_init() for some examples how to do this.
>
> With other arches that use EDK2 (x86, arm64, riscv64, loongarch64) we
> pass this info via fw_cfg
>
> -fw_cfg name=etc/edk2/https/cacerts,file=<certdb>
>
> Assuming this series is trying to implement a pre-existing s390x machine
> standard for passing certs, then it seems inevitable that it will need
> a different config approach than we use for EDK2.
>
> With regards,
> Daniel
Thank you for your feedback.
The -boot-certificates option aims to provide a path to either a
directory or a single certificate on the host. The certificate(s) will
be loaded into the key store and used during signature verification.
s390x will likely need to handle certificates differently from other
architectures.
Regards,
Joy
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 02/24] hw/s390x/ipl: Create certificate store
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 12:44 ` Thomas Huth
2025-04-11 13:02 ` Daniel P. Berrangé
2025-04-08 15:55 ` [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
` (21 subsequent siblings)
23 siblings, 2 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Create a certificate store for boot certificates used for secure IPL.
Load certificates from the -boot-certificate option into the cert store.
Currently, only x509 certificates in DER format and uses SHA-256 hashing
algorithm are supported, as these are the types required for secure boot
on s390.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/cert-store.c | 249 ++++++++++++++++++++++++++++++++++++
hw/s390x/cert-store.h | 50 ++++++++
hw/s390x/ipl.c | 9 ++
hw/s390x/ipl.h | 3 +
hw/s390x/meson.build | 1 +
include/hw/s390x/ipl/qipl.h | 3 +
6 files changed, 315 insertions(+)
create mode 100644 hw/s390x/cert-store.c
create mode 100644 hw/s390x/cert-store.h
diff --git a/hw/s390x/cert-store.c b/hw/s390x/cert-store.c
new file mode 100644
index 0000000000..1aa8aea040
--- /dev/null
+++ b/hw/s390x/cert-store.c
@@ -0,0 +1,249 @@
+/*
+ * S390 certificate store implementation
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cert-store.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "hw/s390x/ebcdic.h"
+#include "qemu/cutils.h"
+#include "cert-store.h"
+
+#ifdef CONFIG_GNUTLS
+#include <gnutls/x509.h>
+#include <gnutls/gnutls.h>
+#endif /* #define CONFIG_GNUTLS */
+
+static const char *s390_get_boot_certificates(void)
+{
+ QemuOpts *opts;
+ const char *path;
+
+ opts = qemu_find_opts_singleton("boot-certificates");
+ path = qemu_opt_get(opts, "boot-certificates");
+
+ return path;
+}
+
+static size_t cert2buf(char *path, size_t max_size, char **cert_buf)
+{
+ size_t size;
+ g_autofree char *buf;
+ buf = g_malloc(max_size);
+
+ if (!g_file_get_contents(path, &buf, &size, NULL) ||
+ size == 0 || size > max_size) {
+ return 0;
+ }
+
+ *cert_buf = g_steal_pointer(&buf);
+
+ return size;
+}
+
+#ifdef CONFIG_GNUTLS
+int g_init_cert(uint8_t *raw_cert, size_t cert_size, gnutls_x509_crt_t *g_cert)
+{
+ int rc;
+
+ if (gnutls_x509_crt_init(g_cert) < 0) {
+ return -1;
+ }
+
+ gnutls_datum_t datum_cert = {raw_cert, cert_size};
+ rc = gnutls_x509_crt_import(*g_cert, &datum_cert, GNUTLS_X509_FMT_DER);
+ if (rc) {
+ gnutls_x509_crt_deinit(*g_cert);
+ return rc;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_GNUTLS */
+
+static int init_cert_x509_der(size_t size, char *raw, S390IPLCertificate **qcert)
+{
+#ifdef CONFIG_GNUTLS
+ gnutls_x509_crt_t g_cert = NULL;
+ g_autofree S390IPLCertificate *q_cert;
+ size_t key_id_size;
+ size_t hash_size;
+ int rc;
+
+ rc = g_init_cert((uint8_t *)raw, size, &g_cert);
+ if (rc) {
+ if (rc == GNUTLS_E_ASN1_TAG_ERROR) {
+ error_report("The certificate is not in DER format");
+ }
+ return -1;
+ }
+
+ rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256, NULL, &key_id_size);
+ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ error_report("Failed to get certificate key ID size");
+ goto out;
+ }
+
+ rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256, NULL, &hash_size);
+ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ error_report("Failed to get certificate hash size");
+ goto out;
+ }
+
+ q_cert = g_malloc(sizeof(*q_cert));
+ q_cert->size = size;
+ q_cert->key_id_size = key_id_size;
+ q_cert->hash_size = hash_size;
+ q_cert->raw = raw;
+ q_cert->format = GNUTLS_X509_FMT_DER;
+ *qcert = g_steal_pointer(&q_cert);
+
+ gnutls_x509_crt_deinit(g_cert);
+
+ return 0;
+out:
+ gnutls_x509_crt_deinit(g_cert);
+ return -1;
+#else
+ error_report("Cryptographic library is not enabled")
+ return -1;
+#endif /* #define CONFIG_GNUTLS */
+}
+
+static int check_path_type(const char *path)
+{
+ struct stat path_stat;
+
+ stat(path, &path_stat);
+
+ if (S_ISDIR(path_stat.st_mode)) {
+ return S_IFDIR;
+ } else if (S_ISREG(path_stat.st_mode)) {
+ return S_IFREG;
+ } else {
+ return -1;
+ }
+}
+
+static int init_cert(char *paths, S390IPLCertificate **qcert)
+{
+ char *buf;
+ char vc_name[VC_NAME_LEN_BYTES];
+ const gchar *filename;
+ size_t size;
+
+ filename = g_path_get_basename(paths);
+
+ size = cert2buf(paths, CERT_MAX_SIZE, &buf);
+ if (size == 0) {
+ error_report("Failed to load certificate: %s", paths);
+ return -1;
+ }
+
+ if (init_cert_x509_der(size, buf, qcert) < 0) {
+ error_report("Failed to initialize certificate: %s", paths);
+ return -1;
+ }
+
+ /*
+ * Left justified certificate name with padding on the right with blanks.
+ * Convert certificate name to EBCDIC.
+ */
+ strpadcpy(vc_name, VC_NAME_LEN_BYTES, filename, ' ');
+ ebcdic_put((*qcert)->vc_name, vc_name, VC_NAME_LEN_BYTES);
+
+ return 0;
+}
+
+static void update_cert_store(S390IPLCertificateStore *cert_store,
+ S390IPLCertificate *qcert)
+{
+ size_t data_size;
+
+ data_size = qcert->size + qcert->key_id_size + qcert->hash_size;
+
+ if (cert_store->max_cert_size < data_size) {
+ cert_store->max_cert_size = data_size;
+ }
+
+ cert_store->certs[cert_store->count] = *qcert;
+ cert_store->total_bytes += data_size;
+ cert_store->count++;
+}
+
+static GPtrArray *get_cert_paths(void)
+{
+ const char *path;
+ gchar **paths;
+ int path_type;
+ GDir *dir = NULL;
+ const gchar *filename;
+ GPtrArray *cert_path_builder;
+
+ cert_path_builder = g_ptr_array_new();
+
+ path = s390_get_boot_certificates();
+ if (path == NULL) {
+ return cert_path_builder;
+ }
+
+ paths = g_strsplit(path, ":", -1);
+ while (*paths) {
+ /* skip empty certificate path */
+ if (!strcmp(*paths, "")) {
+ paths += 1;
+ continue;
+ }
+
+ path_type = check_path_type(*paths);
+ if (path_type == S_IFREG) {
+ g_ptr_array_add(cert_path_builder, (gpointer) *paths);
+ } else if (path_type == S_IFDIR) {
+ dir = g_dir_open(*paths, 0, NULL);
+
+ while ((filename = g_dir_read_name(dir))) {
+ gchar *cert_path = NULL;
+ cert_path = g_build_filename(*paths, filename, NULL);
+ g_ptr_array_add(cert_path_builder, (gpointer) cert_path);
+ }
+
+ g_dir_close(dir);
+ }
+
+ paths += 1;
+ }
+
+ return cert_path_builder;
+}
+
+void s390_ipl_create_cert_store(S390IPLCertificateStore *cert_store)
+{
+ GPtrArray *cert_path_builder;
+
+ cert_path_builder = get_cert_paths();
+ if (cert_path_builder->len == 0) {
+ g_ptr_array_free(cert_path_builder, true);
+ return;
+ }
+
+ cert_store->max_cert_size = 0;
+ cert_store->total_bytes = 0;
+
+ for (int i = 0; i < cert_path_builder->len; i++) {
+ S390IPLCertificate *qcert = NULL;
+ if (init_cert((char *) cert_path_builder->pdata[i], &qcert) < 0) {
+ continue;
+ }
+
+ update_cert_store(cert_store, qcert);
+ }
+
+ g_ptr_array_free(cert_path_builder, true);
+}
diff --git a/hw/s390x/cert-store.h b/hw/s390x/cert-store.h
new file mode 100644
index 0000000000..80141c07da
--- /dev/null
+++ b/hw/s390x/cert-store.h
@@ -0,0 +1,50 @@
+/*
+ * S390 certificate store
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef HW_S390_CERT_STORE_H
+#define HW_S390_CERT_STORE_H
+
+#include "hw/s390x/ipl/qipl.h"
+
+#ifdef CONFIG_GNUTLS
+#include <gnutls/x509.h>
+#include <gnutls/gnutls.h>
+#endif /* #define CONFIG_GNUTLS */
+
+#define VC_NAME_LEN_BYTES 64
+
+struct S390IPLCertificate {
+ uint8_t vc_name[VC_NAME_LEN_BYTES];
+ size_t size;
+ size_t key_id_size;
+ size_t hash_size;
+ char *raw;
+#ifdef CONFIG_GNUTLS
+ gnutls_x509_crt_fmt_t format;
+#endif /* #define CONFIG_GNUTLS */
+};
+typedef struct S390IPLCertificate S390IPLCertificate;
+
+struct S390IPLCertificateStore {
+ uint16_t count;
+ size_t max_cert_size;
+ size_t total_bytes;
+ S390IPLCertificate certs[MAX_CERTIFICATES];
+} QEMU_PACKED;
+typedef struct S390IPLCertificateStore S390IPLCertificateStore;
+
+#ifdef CONFIG_GNUTLS
+int g_init_cert(uint8_t *raw_cert, size_t cert_size, gnutls_x509_crt_t *g_cert);
+#endif /* #define CONFIG_GNUTLS */
+
+void s390_ipl_create_cert_store(S390IPLCertificateStore *cert_store);
+
+#endif
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index ce6f6078d7..b0810c9191 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -36,6 +36,7 @@
#include "qemu/option.h"
#include "qemu/ctype.h"
#include "standard-headers/linux/virtio_ids.h"
+#include "cert-store.h"
#define KERN_IMAGE_START 0x010000UL
#define LINUX_MAGIC_ADDR 0x010008UL
@@ -423,6 +424,13 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp)
}
}
+S390IPLCertificateStore *s390_ipl_get_certificate_store(void)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ return &ipl->cert_store;
+}
+
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
{
CcwDevice *ccw_dev = NULL;
@@ -716,6 +724,7 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
if (!ipl->kernel || ipl->iplb_valid) {
cpu->env.psw.addr = ipl->bios_start_addr;
+ s390_ipl_create_cert_store(&ipl->cert_store);
if (!ipl->iplb_valid) {
ipl->iplb_valid = s390_init_all_iplbs(ipl);
} else {
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 8e3882d506..8c2a442255 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -13,6 +13,7 @@
#ifndef HW_S390_IPL_H
#define HW_S390_IPL_H
+#include "cert-store.h"
#include "cpu.h"
#include "exec/address-spaces.h"
#include "hw/qdev-core.h"
@@ -31,6 +32,7 @@ int s390_ipl_pv_unpack(void);
void s390_ipl_prepare_cpu(S390CPU *cpu);
IplParameterBlock *s390_ipl_get_iplb(void);
IplParameterBlock *s390_ipl_get_iplb_pv(void);
+S390IPLCertificateStore *s390_ipl_get_certificate_store(void);
enum s390_reset {
/* default is a reset not triggered by a CPU e.g. issued by QMP */
@@ -59,6 +61,7 @@ struct S390IPLState {
IplParameterBlock iplb;
IplParameterBlock iplb_pv;
QemuIplParameters qipl;
+ S390IPLCertificateStore cert_store;
uint64_t start_addr;
uint64_t compat_start_addr;
uint64_t bios_start_addr;
diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build
index 610b29bf76..8f0cfaa056 100644
--- a/hw/s390x/meson.build
+++ b/hw/s390x/meson.build
@@ -16,6 +16,7 @@ s390x_ss.add(files(
'sclpcpu.c',
'sclpquiesce.c',
'tod.c',
+ 'cert-store.c',
))
s390x_ss.add(when: 'CONFIG_KVM', if_true: files(
'tod-kvm.c',
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index 6824391111..b8e7d1da71 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -20,6 +20,9 @@
#define LOADPARM_LEN 8
#define NO_LOADPARM "\0\0\0\0\0\0\0\0"
+#define MAX_CERTIFICATES 64
+#define CERT_MAX_SIZE (1024 * 8)
+
/*
* The QEMU IPL Parameters will be stored at absolute address
* 204 (0xcc) which means it is 32-bit word aligned but not
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 02/24] hw/s390x/ipl: Create certificate store
2025-04-08 15:55 ` [PATCH v1 02/24] hw/s390x/ipl: Create certificate store Zhuoying Cai
@ 2025-04-11 12:44 ` Thomas Huth
2025-04-11 13:02 ` Daniel P. Berrangé
1 sibling, 0 replies; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 12:44 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> Create a certificate store for boot certificates used for secure IPL.
>
> Load certificates from the -boot-certificate option into the cert store.
>
> Currently, only x509 certificates in DER format and uses SHA-256 hashing
> algorithm are supported, as these are the types required for secure boot
> on s390.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
...
> +static size_t cert2buf(char *path, size_t max_size, char **cert_buf)
> +{
> + size_t size;
> + g_autofree char *buf;
> + buf = g_malloc(max_size);
> +
> + if (!g_file_get_contents(path, &buf, &size, NULL) ||
> + size == 0 || size > max_size) {
> + return 0;
> + }
> +
> + *cert_buf = g_steal_pointer(&buf);
> +
> + return size;
> +}
This function looks quite wrong to me. Why is there a g_malloc() in here if
g_file_get_contents() already allocates the memory?
And why do we need a max_size here? If there is a reason, please add a
proper comment in the source code.
> +#ifdef CONFIG_GNUTLS
> +int g_init_cert(uint8_t *raw_cert, size_t cert_size, gnutls_x509_crt_t *g_cert)
Please don't use a "g_" prefix here - otherwise that way the function could
be confused with the functions from the glib.
> +{
> + int rc;
> +
> + if (gnutls_x509_crt_init(g_cert) < 0) {
> + return -1;
> + }
> +
> + gnutls_datum_t datum_cert = {raw_cert, cert_size};
> + rc = gnutls_x509_crt_import(*g_cert, &datum_cert, GNUTLS_X509_FMT_DER);
> + if (rc) {
> + gnutls_x509_crt_deinit(*g_cert);
> + return rc;
> + }
> +
> + return 0;
> +}
> +#endif /* CONFIG_GNUTLS */
> +
> +static int init_cert_x509_der(size_t size, char *raw, S390IPLCertificate **qcert)
I'd maybe rather use "S390IPLCertificate *" as return type instead of "int"
and return a NULL in case of errors.
> +{
> +#ifdef CONFIG_GNUTLS
> + gnutls_x509_crt_t g_cert = NULL;
> + g_autofree S390IPLCertificate *q_cert;
> + size_t key_id_size;
> + size_t hash_size;
> + int rc;
> +
> + rc = g_init_cert((uint8_t *)raw, size, &g_cert);
> + if (rc) {
> + if (rc == GNUTLS_E_ASN1_TAG_ERROR) {
> + error_report("The certificate is not in DER format");
> + }
> + return -1;
> + }
> +
> + rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256, NULL, &key_id_size);
Is that documented somewhere that you can call gnutls_x509_crt_get_key_id()
like this? The docs that I found about this function do not say anything
about passing NULL here, they rather recommend to use a buffer of size 20 by
default?
> + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
> + error_report("Failed to get certificate key ID size");
> + goto out;
> + }
> +
> + rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256, NULL, &hash_size);
For this function, the NULL pointer handling is documented, so here it seems
to be OK.
> + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
> + error_report("Failed to get certificate hash size");
> + goto out;
> + }
> +
> + q_cert = g_malloc(sizeof(*q_cert));
Please use g_new() for allocating memory for structures instead.
> + q_cert->size = size;
> + q_cert->key_id_size = key_id_size;
> + q_cert->hash_size = hash_size;
> + q_cert->raw = raw;
> + q_cert->format = GNUTLS_X509_FMT_DER;
> + *qcert = g_steal_pointer(&q_cert);
If there is no "return" between the allocation and the final "return 0", you
can also drop the g_autofree and g_steal_pointer from this function.
> + gnutls_x509_crt_deinit(g_cert);
> +
> + return 0;
> +out:
> + gnutls_x509_crt_deinit(g_cert);
> + return -1;
> +#else
> + error_report("Cryptographic library is not enabled")
> + return -1;
> +#endif /* #define CONFIG_GNUTLS */
> +}
> +
> +static int check_path_type(const char *path)
> +{
> + struct stat path_stat;
> +
> + stat(path, &path_stat);
> +
> + if (S_ISDIR(path_stat.st_mode)) {
> + return S_IFDIR;
> + } else if (S_ISREG(path_stat.st_mode)) {
> + return S_IFREG;
> + } else {
> + return -1;
> + }
> +}
> +
> +static int init_cert(char *paths, S390IPLCertificate **qcert)
as with previous function, use "S390IPLCertificate *" as return type instead
of "int" ?
> +{
> + char *buf;
> + char vc_name[VC_NAME_LEN_BYTES];
> + const gchar *filename;
> + size_t size;
> +
> + filename = g_path_get_basename(paths);
g_path_get_basename() returns an allocated string. You've finally got to
free it again to avoid leaking memory. I'd suggest declaring filename with
g_autofree.
> + size = cert2buf(paths, CERT_MAX_SIZE, &buf);
> + if (size == 0) {
> + error_report("Failed to load certificate: %s", paths);
> + return -1;
> + }
> +
> + if (init_cert_x509_der(size, buf, qcert) < 0) {
> + error_report("Failed to initialize certificate: %s", paths);
> + return -1;
> + }
> +
> + /*
> + * Left justified certificate name with padding on the right with blanks.
> + * Convert certificate name to EBCDIC.
> + */
> + strpadcpy(vc_name, VC_NAME_LEN_BYTES, filename, ' ');
> + ebcdic_put((*qcert)->vc_name, vc_name, VC_NAME_LEN_BYTES);
> +
> + return 0;
> +}
> +
> +static void update_cert_store(S390IPLCertificateStore *cert_store,
> + S390IPLCertificate *qcert)
> +{
> + size_t data_size;
> +
> + data_size = qcert->size + qcert->key_id_size + qcert->hash_size;
> +
> + if (cert_store->max_cert_size < data_size) {
> + cert_store->max_cert_size = data_size;
> + }
> +
> + cert_store->certs[cert_store->count] = *qcert;
> + cert_store->total_bytes += data_size;
> + cert_store->count++;
> +}
> +
> +static GPtrArray *get_cert_paths(void)
> +{
> + const char *path;
> + gchar **paths;
> + int path_type;
> + GDir *dir = NULL;
> + const gchar *filename;
> + GPtrArray *cert_path_builder;
> +
> + cert_path_builder = g_ptr_array_new();
> +
> + path = s390_get_boot_certificates();
> + if (path == NULL) {
> + return cert_path_builder;
> + }
> +
> + paths = g_strsplit(path, ":", -1);
Free the memory that has been allocated by the g_strsplit at the end of the
function?
> + while (*paths) {
> + /* skip empty certificate path */
> + if (!strcmp(*paths, "")) {
> + paths += 1;
> + continue;
> + }
> +
> + path_type = check_path_type(*paths);
> + if (path_type == S_IFREG) {
> + g_ptr_array_add(cert_path_builder, (gpointer) *paths);
... that should likely g_strdup(*paths) when we want to free paths at the end.
> + } else if (path_type == S_IFDIR) {
> + dir = g_dir_open(*paths, 0, NULL);
> +
> + while ((filename = g_dir_read_name(dir))) {
> + gchar *cert_path = NULL;
> + cert_path = g_build_filename(*paths, filename, NULL);
> + g_ptr_array_add(cert_path_builder, (gpointer) cert_path);
> + }
> +
> + g_dir_close(dir);
> + }
> +
> + paths += 1;
> + }
> +
> + return cert_path_builder;
> +}
> +
> +void s390_ipl_create_cert_store(S390IPLCertificateStore *cert_store)
> +{
> + GPtrArray *cert_path_builder;
> +
> + cert_path_builder = get_cert_paths();
> + if (cert_path_builder->len == 0) {
> + g_ptr_array_free(cert_path_builder, true);
> + return;
> + }
> +
> + cert_store->max_cert_size = 0;
> + cert_store->total_bytes = 0;
> +
> + for (int i = 0; i < cert_path_builder->len; i++) {
> + S390IPLCertificate *qcert = NULL;
> + if (init_cert((char *) cert_path_builder->pdata[i], &qcert) < 0) {
> + continue;
Maybe invert the logic to call update_cert_store() in case of success, than
you don't need the "continue" anymore?
> + }
> +
> + update_cert_store(cert_store, qcert);
> + }
> +
> + g_ptr_array_free(cert_path_builder, true);
> +}
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 02/24] hw/s390x/ipl: Create certificate store
2025-04-08 15:55 ` [PATCH v1 02/24] hw/s390x/ipl: Create certificate store Zhuoying Cai
2025-04-11 12:44 ` Thomas Huth
@ 2025-04-11 13:02 ` Daniel P. Berrangé
1 sibling, 0 replies; 54+ messages in thread
From: Daniel P. Berrangé @ 2025-04-11 13:02 UTC (permalink / raw)
To: Zhuoying Cai
Cc: thuth, richard.henderson, david, pbonzini, walling, jjherne,
jrossi, fiuczy, pasic, borntraeger, farman, iii, qemu-s390x,
qemu-devel
On Tue, Apr 08, 2025 at 11:55:04AM -0400, Zhuoying Cai wrote:
> Create a certificate store for boot certificates used for secure IPL.
>
> Load certificates from the -boot-certificate option into the cert store.
>
> Currently, only x509 certificates in DER format and uses SHA-256 hashing
> algorithm are supported, as these are the types required for secure boot
> on s390.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> hw/s390x/cert-store.c | 249 ++++++++++++++++++++++++++++++++++++
> hw/s390x/cert-store.h | 50 ++++++++
> hw/s390x/ipl.c | 9 ++
> hw/s390x/ipl.h | 3 +
> hw/s390x/meson.build | 1 +
> include/hw/s390x/ipl/qipl.h | 3 +
> 6 files changed, 315 insertions(+)
> create mode 100644 hw/s390x/cert-store.c
> create mode 100644 hw/s390x/cert-store.h
>
> diff --git a/hw/s390x/cert-store.c b/hw/s390x/cert-store.c
> new file mode 100644
> index 0000000000..1aa8aea040
> --- /dev/null
> +++ b/hw/s390x/cert-store.c
> @@ -0,0 +1,249 @@
> +/*
> + * S390 certificate store implementation
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "cert-store.h"
> +#include "qemu/error-report.h"
> +#include "qemu/option.h"
> +#include "qemu/config-file.h"
> +#include "hw/s390x/ebcdic.h"
> +#include "qemu/cutils.h"
> +#include "cert-store.h"
> +
> +#ifdef CONFIG_GNUTLS
> +#include <gnutls/x509.h>
> +#include <gnutls/gnutls.h>
> +#endif /* #define CONFIG_GNUTLS */
It is bad practice to directly use GNUTLS in any QEMU code except
for under the 'crypto/' directory (and its test suites). We must
define internal APIs for accessing the info we need and then call
those instead of gnutls.
> +
> +static const char *s390_get_boot_certificates(void)
> +{
> + QemuOpts *opts;
> + const char *path;
> +
> + opts = qemu_find_opts_singleton("boot-certificates");
> + path = qemu_opt_get(opts, "boot-certificates");
> +
> + return path;
> +}
> +
> +static size_t cert2buf(char *path, size_t max_size, char **cert_buf)
> +{
> + size_t size;
> + g_autofree char *buf;
> + buf = g_malloc(max_size);
> +
> + if (!g_file_get_contents(path, &buf, &size, NULL) ||
> + size == 0 || size > max_size) {
> + return 0;
> + }
> +
> + *cert_buf = g_steal_pointer(&buf);
> +
> + return size;
> +}
> +
> +#ifdef CONFIG_GNUTLS
> +int g_init_cert(uint8_t *raw_cert, size_t cert_size, gnutls_x509_crt_t *g_cert)
> +{
> + int rc;
> +
> + if (gnutls_x509_crt_init(g_cert) < 0) {
> + return -1;
> + }
> +
> + gnutls_datum_t datum_cert = {raw_cert, cert_size};
> + rc = gnutls_x509_crt_import(*g_cert, &datum_cert, GNUTLS_X509_FMT_DER);
> + if (rc) {
> + gnutls_x509_crt_deinit(*g_cert);
> + return rc;
> + }
> +
> + return 0;
> +}
> +#endif /* CONFIG_GNUTLS */
> +
> +static int init_cert_x509_der(size_t size, char *raw, S390IPLCertificate **qcert)
> +{
> +#ifdef CONFIG_GNUTLS
> + gnutls_x509_crt_t g_cert = NULL;
> + g_autofree S390IPLCertificate *q_cert;
> + size_t key_id_size;
> + size_t hash_size;
> + int rc;
> +
> + rc = g_init_cert((uint8_t *)raw, size, &g_cert);
> + if (rc) {
> + if (rc == GNUTLS_E_ASN1_TAG_ERROR) {
> + error_report("The certificate is not in DER format");
> + }
> + return -1;
> + }
> +
> + rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256, NULL, &key_id_size);
> + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
> + error_report("Failed to get certificate key ID size");
> + goto out;
> + }
> +
> + rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256, NULL, &hash_size);
> + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) {
> + error_report("Failed to get certificate hash size");
> + goto out;
> + }
We already have qcrypto_get_x509_cert_fingerprint() to avoid direct use
of gnutls. That API could be extended to optionally also report the
key id.
> +
> + q_cert = g_malloc(sizeof(*q_cert));
> + q_cert->size = size;
> + q_cert->key_id_size = key_id_size;
> + q_cert->hash_size = hash_size;
> + q_cert->raw = raw;
> + q_cert->format = GNUTLS_X509_FMT_DER;
> + *qcert = g_steal_pointer(&q_cert);
> +
> + gnutls_x509_crt_deinit(g_cert);
> +
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS)
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 02/24] hw/s390x/ipl: Create certificate store Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 13:28 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
` (20 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
DIAG 320 is supported when the certificate-store (CS) facility
is installed.
Availability of CS facility is determined by byte 134 bit 5 of the
SCLP Read Info block.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
target/s390x/cpu_features.c | 1 +
target/s390x/cpu_features_def.h.inc | 1 +
target/s390x/cpu_models.c | 2 ++
target/s390x/gen-features.c | 1 +
target/s390x/kvm/kvm.c | 2 ++
5 files changed, 7 insertions(+)
diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index 4b5be6798e..99089ab3f5 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -147,6 +147,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
break;
case S390_FEAT_TYPE_SCLP_FAC134:
clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data);
+ clear_be_bit(s390_feat_def(S390_FEAT_DIAG_320)->bit, data);
break;
default:
return;
diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
index e23e603a79..65d38f546d 100644
--- a/target/s390x/cpu_features_def.h.inc
+++ b/target/s390x/cpu_features_def.h.inc
@@ -138,6 +138,7 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
/* Features exposed via SCLP SCCB Facilities byte 134 (bit numbers relative to byte-134) */
DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
+DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
/* Features exposed via SCLP CPU info. */
DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 93a05e43d7..7d65c40bd1 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -248,6 +248,7 @@ bool s390_has_feat(S390Feat feat)
if (s390_is_pv()) {
switch (feat) {
case S390_FEAT_DIAG_318:
+ case S390_FEAT_DIAG_320:
case S390_FEAT_HPMA2:
case S390_FEAT_SIE_F2:
case S390_FEAT_SIE_SKEY:
@@ -505,6 +506,7 @@ static void check_consistency(const S390CPUModel *model)
{ S390_FEAT_PTFF_STOUE, S390_FEAT_MULTIPLE_EPOCH },
{ S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP },
{ S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
+ { S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_NNPA, S390_FEAT_VECTOR },
{ S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
{ S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 41840677ce..52c649adcd 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -696,6 +696,7 @@ static uint16_t full_GEN14_GA1[] = {
S390_FEAT_HPMA2,
S390_FEAT_SIE_KSS,
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
+ S390_FEAT_DIAG_320,
};
#define full_GEN14_GA2 EmptyFeat
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 4d56e653dd..d07ca879a3 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -2487,6 +2487,8 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
set_bit(S390_FEAT_DIAG_318, model->features);
}
+ set_bit(S390_FEAT_DIAG_320, model->features);
+
/* Test for Ultravisor features that influence secure guest behavior */
query_uv_feat_guest(model->features);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS)
2025-04-08 15:55 ` [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
@ 2025-04-11 13:28 ` Thomas Huth
2025-04-14 17:53 ` Zhuoying Cai
0 siblings, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 13:28 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> DIAG 320 is supported when the certificate-store (CS) facility
> is installed.
>
> Availability of CS facility is determined by byte 134 bit 5 of the
> SCLP Read Info block.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
...
> diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
> index 4b5be6798e..99089ab3f5 100644
> --- a/target/s390x/cpu_features.c
> +++ b/target/s390x/cpu_features.c
> @@ -147,6 +147,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
> break;
> case S390_FEAT_TYPE_SCLP_FAC134:
> clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data);
> + clear_be_bit(s390_feat_def(S390_FEAT_DIAG_320)->bit, data);
> break;
> default:
> return;
> diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
> index e23e603a79..65d38f546d 100644
> --- a/target/s390x/cpu_features_def.h.inc
> +++ b/target/s390x/cpu_features_def.h.inc
> @@ -138,6 +138,7 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
>
> /* Features exposed via SCLP SCCB Facilities byte 134 (bit numbers relative to byte-134) */
> DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
> +DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
>
> /* Features exposed via SCLP CPU info. */
> DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> index 93a05e43d7..7d65c40bd1 100644
> --- a/target/s390x/cpu_models.c
> +++ b/target/s390x/cpu_models.c
> @@ -248,6 +248,7 @@ bool s390_has_feat(S390Feat feat)
> if (s390_is_pv()) {
> switch (feat) {
> case S390_FEAT_DIAG_318:
> + case S390_FEAT_DIAG_320:
So secure IPL is not available with secure execution? That's surprising.
Could you add a comment to the patch description why this is the case?
> case S390_FEAT_HPMA2:
> case S390_FEAT_SIE_F2:
> case S390_FEAT_SIE_SKEY:
> @@ -505,6 +506,7 @@ static void check_consistency(const S390CPUModel *model)
> { S390_FEAT_PTFF_STOUE, S390_FEAT_MULTIPLE_EPOCH },
> { S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP },
> { S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
> + { S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
Please also add a comment to the patch description why this feature needs
S390_FEAT_EXTENDED_LENGTH_SCCB.
> { S390_FEAT_NNPA, S390_FEAT_VECTOR },
> { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
> { S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
> diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
> index 41840677ce..52c649adcd 100644
> --- a/target/s390x/gen-features.c
> +++ b/target/s390x/gen-features.c
> @@ -696,6 +696,7 @@ static uint16_t full_GEN14_GA1[] = {
> S390_FEAT_HPMA2,
> S390_FEAT_SIE_KSS,
> S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
> + S390_FEAT_DIAG_320,
Is it available with the z14 already?
https://www.ibm.com/docs/en/linux-on-systems?topic=linux-secure-boot seems
to indicate a z15 instead??
> };
>
> #define full_GEN14_GA2 EmptyFeat
> diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
> index 4d56e653dd..d07ca879a3 100644
> --- a/target/s390x/kvm/kvm.c
> +++ b/target/s390x/kvm/kvm.c
> @@ -2487,6 +2487,8 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
> set_bit(S390_FEAT_DIAG_318, model->features);
> }
>
> + set_bit(S390_FEAT_DIAG_320, model->features);
> +
> /* Test for Ultravisor features that influence secure guest behavior */
> query_uv_feat_guest(model->features);
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS)
2025-04-11 13:28 ` Thomas Huth
@ 2025-04-14 17:53 ` Zhuoying Cai
0 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-14 17:53 UTC (permalink / raw)
To: Thomas Huth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/11/25 9:28 AM, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
>> DIAG 320 is supported when the certificate-store (CS) facility
>> is installed.
>>
>> Availability of CS facility is determined by byte 134 bit 5 of the
>> SCLP Read Info block.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>> ---
> ...
>> diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
>> index 4b5be6798e..99089ab3f5 100644
>> --- a/target/s390x/cpu_features.c
>> +++ b/target/s390x/cpu_features.c
>> @@ -147,6 +147,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
>> break;
>> case S390_FEAT_TYPE_SCLP_FAC134:
>> clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data);
>> + clear_be_bit(s390_feat_def(S390_FEAT_DIAG_320)->bit, data);
>> break;
>> default:
>> return;
>> diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
>> index e23e603a79..65d38f546d 100644
>> --- a/target/s390x/cpu_features_def.h.inc
>> +++ b/target/s390x/cpu_features_def.h.inc
>> @@ -138,6 +138,7 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
>>
>> /* Features exposed via SCLP SCCB Facilities byte 134 (bit numbers relative to byte-134) */
>> DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
>> +DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
>>
>> /* Features exposed via SCLP CPU info. */
>> DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
>> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
>> index 93a05e43d7..7d65c40bd1 100644
>> --- a/target/s390x/cpu_models.c
>> +++ b/target/s390x/cpu_models.c
>> @@ -248,6 +248,7 @@ bool s390_has_feat(S390Feat feat)
>> if (s390_is_pv()) {
>> switch (feat) {
>> case S390_FEAT_DIAG_318:
>> + case S390_FEAT_DIAG_320:
>
> So secure IPL is not available with secure execution? That's surprising.
> Could you add a comment to the patch description why this is the case?
>
Secure IPL is not available for Secure Execution (SE) guests, as their
images are already integrity protected, and an additional protection of
the kernel by secure IPL is not necessary.
I'll provide more context in the patch description for the next iteration.
>> case S390_FEAT_HPMA2:
>> case S390_FEAT_SIE_F2:
>> case S390_FEAT_SIE_SKEY:
>> @@ -505,6 +506,7 @@ static void check_consistency(const S390CPUModel *model)
>> { S390_FEAT_PTFF_STOUE, S390_FEAT_MULTIPLE_EPOCH },
>> { S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP },
>> { S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
>> + { S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
>
> Please also add a comment to the patch description why this feature needs
> S390_FEAT_EXTENDED_LENGTH_SCCB.
>
>> { S390_FEAT_NNPA, S390_FEAT_VECTOR },
>> { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
>> { S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
>> diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
>> index 41840677ce..52c649adcd 100644
>> --- a/target/s390x/gen-features.c
>> +++ b/target/s390x/gen-features.c
>> @@ -696,6 +696,7 @@ static uint16_t full_GEN14_GA1[] = {
>> S390_FEAT_HPMA2,
>> S390_FEAT_SIE_KSS,
>> S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
>> + S390_FEAT_DIAG_320,
>
> Is it available with the z14 already?
> https://www.ibm.com/docs/en/linux-on-systems?topic=linux-secure-boot seems
> to indicate a z15 instead??
>
>> };
>>
>> #define full_GEN14_GA2 EmptyFeat
>> diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
>> index 4d56e653dd..d07ca879a3 100644
>> --- a/target/s390x/kvm/kvm.c
>> +++ b/target/s390x/kvm/kvm.c
>> @@ -2487,6 +2487,8 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
>> set_bit(S390_FEAT_DIAG_318, model->features);
>> }
>>
>> + set_bit(S390_FEAT_DIAG_320, model->features);
>> +
>> /* Test for Ultravisor features that influence secure guest behavior */
>> query_uv_feat_guest(model->features);
>
> Thomas
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (2 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 13:43 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 05/24] s390x/diag: Refactor address validation check from diag308_parm_check Zhuoying Cai
` (19 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
From: Collin Walling <walling@linux.ibm.com>
DIAGNOSE 320 is introduced to support certificate store facility,
which includes operations such as query certificate storage
information and provide certificates in the certificate store.
Currently, only subcode 0 is supported with this patch, which is
used to query a bitmap of which subcodes are supported.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.h | 1 +
include/hw/s390x/ipl/diag320.h | 19 ++++++++++++++++++
target/s390x/diag.c | 36 ++++++++++++++++++++++++++++++++++
target/s390x/kvm/kvm.c | 14 +++++++++++++
target/s390x/s390x-internal.h | 2 ++
5 files changed, 72 insertions(+)
create mode 100644 include/hw/s390x/ipl/diag320.h
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 8c2a442255..1bd73b4dc1 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -17,6 +17,7 @@
#include "cpu.h"
#include "exec/address-spaces.h"
#include "hw/qdev-core.h"
+#include "hw/s390x/ipl/diag320.h"
#include "hw/s390x/ipl/qipl.h"
#include "qom/object.h"
diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
new file mode 100644
index 0000000000..d6f70c65df
--- /dev/null
+++ b/include/hw/s390x/ipl/diag320.h
@@ -0,0 +1,19 @@
+/*
+ * S/390 DIAGNOSE 320 definitions and structures
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef S390X_DIAG320_H
+#define S390X_DIAG320_H
+
+#define DIAG_320_SUBC_QUERY_ISM 0
+
+#define DIAG_320_RC_OK 0x0001
+
+#endif
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index da44b0133e..cb840e4b97 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -192,3 +192,39 @@ out:
break;
}
}
+
+void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
+{
+ S390CPU *cpu = env_archcpu(env);
+ uint64_t subcode = env->regs[r3];
+ uint64_t addr = env->regs[r1];
+ int rc;
+
+ if (env->psw.mask & PSW_MASK_PSTATE) {
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
+ return;
+ }
+
+ if (r1 & 1) {
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
+ return;
+ }
+
+ switch (subcode) {
+ case DIAG_320_SUBC_QUERY_ISM:
+ uint64_t ism = 0;
+
+ if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
+ be64_to_cpu(sizeof(ism)))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+
+ rc = DIAG_320_RC_OK;
+ break;
+ default:
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
+ return;
+ }
+ env->regs[r1 + 1] = rc;
+}
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index d07ca879a3..b013751478 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -98,6 +98,7 @@
#define DIAG_TIMEREVENT 0x288
#define DIAG_IPL 0x308
#define DIAG_SET_CONTROL_PROGRAM_CODES 0x318
+#define DIAG_CERT_STORE 0x320
#define DIAG_KVM_HYPERCALL 0x500
#define DIAG_KVM_BREAKPOINT 0x501
@@ -1561,6 +1562,16 @@ static void handle_diag_318(S390CPU *cpu, struct kvm_run *run)
}
}
+static void kvm_handle_diag_320(S390CPU *cpu, struct kvm_run *run)
+{
+ uint64_t r1, r3;
+
+ r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+ r3 = run->s390_sieic.ipa & 0x000f;
+
+ handle_diag_320(&cpu->env, r1, r3, RA_IGNORED);
+}
+
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
@@ -1591,6 +1602,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
case DIAG_KVM_BREAKPOINT:
r = handle_sw_breakpoint(cpu, run);
break;
+ case DIAG_CERT_STORE:
+ kvm_handle_diag_320(cpu, run);
+ break;
default:
trace_kvm_insn_diag(func_code);
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h
index a4ba6227ab..86a652f833 100644
--- a/target/s390x/s390x-internal.h
+++ b/target/s390x/s390x-internal.h
@@ -400,6 +400,8 @@ int mmu_translate_real(CPUS390XState *env, target_ulong raddr, int rw,
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3);
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3,
uintptr_t ra);
+void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3,
+ uintptr_t ra);
/* translate.c */
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility
2025-04-08 15:55 ` [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
@ 2025-04-11 13:43 ` Thomas Huth
2025-04-11 18:37 ` Collin Walling
0 siblings, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 13:43 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> From: Collin Walling <walling@linux.ibm.com>
...
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
So the patch is from Collin, but S-o-b only by you? Looks weird, this should
either have an additional S-o-b by Collin, too, or not have that "From:"
line at all?
...
> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
> new file mode 100644
> index 0000000000..d6f70c65df
> --- /dev/null
> +++ b/include/hw/s390x/ipl/diag320.h
> @@ -0,0 +1,19 @@
> +/*
> + * S/390 DIAGNOSE 320 definitions and structures
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Zhuoying Cai <zycai@linux.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or (at
> + * your option) any later version. See the COPYING file in the top-level
> + * directory.
By the way, new files need a SPDX-License-Identifier nowadays to make
scripts/checkpatch.pl happy.
> + */
> +
> +#ifndef S390X_DIAG320_H
> +#define S390X_DIAG320_H
> +
> +#define DIAG_320_SUBC_QUERY_ISM 0
> +
> +#define DIAG_320_RC_OK 0x0001
> +
> +#endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index da44b0133e..cb840e4b97 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -192,3 +192,39 @@ out:
> break;
> }
> }
> +
> +void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> +{
> + S390CPU *cpu = env_archcpu(env);
> + uint64_t subcode = env->regs[r3];
> + uint64_t addr = env->regs[r1];
> + int rc;
Do we also need a s390_has_feat(S390_FEAT_DIAG_320) check here?
> + if (env->psw.mask & PSW_MASK_PSTATE) {
> + s390_program_interrupt(env, PGM_PRIVILEGED, ra);
> + return;
> + }
> +
> + if (r1 & 1) {
> + s390_program_interrupt(env, PGM_SPECIFICATION, ra);
> + return;
> + }
> +
> + switch (subcode) {
> + case DIAG_320_SUBC_QUERY_ISM:
> + uint64_t ism = 0;
> +
> + if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
I think you could drop the (uint8_t) here?
> + be64_to_cpu(sizeof(ism)))) {
be64_to_cpu() looks very wrong here!
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility
2025-04-11 13:43 ` Thomas Huth
@ 2025-04-11 18:37 ` Collin Walling
0 siblings, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-11 18:37 UTC (permalink / raw)
To: Thomas Huth, Zhuoying Cai, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/11/25 9:43 AM, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
>> From: Collin Walling <walling@linux.ibm.com>
> ...
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>
> So the patch is from Collin, but S-o-b only by you? Looks weird, this should
> either have an additional S-o-b by Collin, too, or not have that "From:"
> line at all?
>
Joy took over the bulk of this item as I had to shift priorities. The
initial version of this patch was mine, but the work now reflects hers.
The "from" line should not be there.
@Joy, when you go through your rebasing/patch fixup, make sure this gets
a `git commit ... --reset-author` so it reflects you as the proper owner.
[...]
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 05/24] s390x/diag: Refactor address validation check from diag308_parm_check
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (3 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
` (18 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
From: Collin Walling <walling@linux.ibm.com>
Create a function to validate the address parameter of DIAGNOSE.
Refactor the function for reuse in the next patch, which allows address
validation in read or write operation of DIAGNOSE.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.h | 6 ++++++
target/s390x/diag.c | 4 +---
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 1bd73b4dc1..822535ad76 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -114,6 +114,12 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
#define S390_IPLB_MIN_FCP_LEN 384
#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
+static inline bool diag_parm_addr_valid(uint64_t addr, size_t size, bool write)
+{
+ return address_space_access_valid(&address_space_memory, addr,
+ size, write, MEMTXATTRS_UNSPECIFIED);
+}
+
static inline bool iplb_valid_len(IplParameterBlock *iplb)
{
return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock);
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index cb840e4b97..c64b935c87 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -64,9 +64,7 @@ static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -1;
}
- if (!address_space_access_valid(&address_space_memory, addr,
- sizeof(IplParameterBlock), write,
- MEMTXATTRS_UNSPECIFIED)) {
+ if (!diag_parm_addr_valid(addr, sizeof(IplParameterBlock), write)) {
s390_program_interrupt(env, PGM_ADDRESSING, ra);
return -1;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (4 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 05/24] s390x/diag: Refactor address validation check from diag308_parm_check Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 13:57 ` Thomas Huth
2025-04-11 17:40 ` Farhan Ali
2025-04-08 15:55 ` [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2 Zhuoying Cai
` (17 subsequent siblings)
23 siblings, 2 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
DIAG 320 subcode 1 provides information needed to determine
the amount of storage to store one or more certificates.
The subcode value is denoted by setting the left-most bit
of an 8-byte field.
The verification-certificate-storage-size block (VCSSB) contains
the output data when the operation completes successfully.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
include/hw/s390x/ipl/diag320.h | 25 ++++++++++++++++++++++
target/s390x/diag.c | 39 +++++++++++++++++++++++++++++++++-
2 files changed, 63 insertions(+), 1 deletion(-)
diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
index d6f70c65df..ded336df25 100644
--- a/include/hw/s390x/ipl/diag320.h
+++ b/include/hw/s390x/ipl/diag320.h
@@ -13,7 +13,32 @@
#define S390X_DIAG320_H
#define DIAG_320_SUBC_QUERY_ISM 0
+#define DIAG_320_SUBC_QUERY_VCSI 1
#define DIAG_320_RC_OK 0x0001
+#define DIAG_320_RC_NOMEM 0x0202
+
+#define VCSSB_MAX_LEN 128
+#define VCE_HEADER_LEN 128
+#define VCB_HEADER_LEN 64
+
+#define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
+
+struct VerificationCertificateStorageSizeBlock {
+ uint32_t length;
+ uint8_t reserved0[3];
+ uint8_t version;
+ uint32_t reserved1[6];
+ uint16_t totalvc;
+ uint16_t maxvc;
+ uint32_t reserved3[7];
+ uint32_t maxvcelen;
+ uint32_t reserved4[3];
+ uint32_t largestvcblen;
+ uint32_t totalvcblen;
+ uint32_t reserved5[10];
+} QEMU_PACKED;
+typedef struct VerificationCertificateStorageSizeBlock \
+VerificationCertificateStorageSizeBlock;
#endif
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index c64b935c87..cc639819ec 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -194,6 +194,7 @@ out:
void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
{
S390CPU *cpu = env_archcpu(env);
+ S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
uint64_t subcode = env->regs[r3];
uint64_t addr = env->regs[r1];
int rc;
@@ -210,7 +211,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
switch (subcode) {
case DIAG_320_SUBC_QUERY_ISM:
- uint64_t ism = 0;
+ uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
be64_to_cpu(sizeof(ism)))) {
@@ -218,6 +219,42 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
return;
}
+ rc = DIAG_320_RC_OK;
+ break;
+ case DIAG_320_SUBC_QUERY_VCSI:
+ VerificationCertificateStorageSizeBlock vcssb;
+
+ if (!diag_parm_addr_valid(addr, sizeof(VerificationCertificateStorageSizeBlock),
+ true)) {
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
+ return;
+ }
+
+ if (!qcs || !qcs->count) {
+ vcssb.length = 4;
+ } else {
+ vcssb.length = VCSSB_MAX_LEN;
+ vcssb.version = 0;
+ vcssb.totalvc = qcs->count;
+ vcssb.maxvc = MAX_CERTIFICATES;
+ vcssb.maxvcelen = VCE_HEADER_LEN + qcs->max_cert_size;
+ vcssb.largestvcblen = VCB_HEADER_LEN + vcssb.maxvcelen;
+ vcssb.totalvcblen = VCB_HEADER_LEN + qcs->count * VCE_HEADER_LEN +
+ qcs->total_bytes;
+ }
+
+ if (vcssb.length < 128) {
+ rc = DIAG_320_RC_NOMEM;
+ break;
+ }
+
+ if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &vcssb,
+ be64_to_cpu(
+ sizeof(VerificationCertificateStorageSizeBlock)
+ ))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
rc = DIAG_320_RC_OK;
break;
default:
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1
2025-04-08 15:55 ` [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
@ 2025-04-11 13:57 ` Thomas Huth
2025-04-17 19:57 ` Collin Walling
2025-04-11 17:40 ` Farhan Ali
1 sibling, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 13:57 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> DIAG 320 subcode 1 provides information needed to determine
> the amount of storage to store one or more certificates.
>
> The subcode value is denoted by setting the left-most bit
> of an 8-byte field.
>
> The verification-certificate-storage-size block (VCSSB) contains
> the output data when the operation completes successfully.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag320.h | 25 ++++++++++++++++++++++
> target/s390x/diag.c | 39 +++++++++++++++++++++++++++++++++-
> 2 files changed, 63 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
> index d6f70c65df..ded336df25 100644
> --- a/include/hw/s390x/ipl/diag320.h
> +++ b/include/hw/s390x/ipl/diag320.h
> @@ -13,7 +13,32 @@
> #define S390X_DIAG320_H
>
> #define DIAG_320_SUBC_QUERY_ISM 0
> +#define DIAG_320_SUBC_QUERY_VCSI 1
>
> #define DIAG_320_RC_OK 0x0001
> +#define DIAG_320_RC_NOMEM 0x0202
> +
> +#define VCSSB_MAX_LEN 128
> +#define VCE_HEADER_LEN 128
> +#define VCB_HEADER_LEN 64
> +
> +#define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
> +
> +struct VerificationCertificateStorageSizeBlock {
> + uint32_t length;
> + uint8_t reserved0[3];
> + uint8_t version;
> + uint32_t reserved1[6];
> + uint16_t totalvc;
> + uint16_t maxvc;
> + uint32_t reserved3[7];
> + uint32_t maxvcelen;
> + uint32_t reserved4[3];
> + uint32_t largestvcblen;
> + uint32_t totalvcblen;
> + uint32_t reserved5[10];
> +} QEMU_PACKED;
> +typedef struct VerificationCertificateStorageSizeBlock \
> +VerificationCertificateStorageSizeBlock;
That's quite a long name, maybe shorten to VerificationCertStorageBlock or
something similar?
>
> #endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index c64b935c87..cc639819ec 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -194,6 +194,7 @@ out:
> void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> {
> S390CPU *cpu = env_archcpu(env);
> + S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
> uint64_t subcode = env->regs[r3];
> uint64_t addr = env->regs[r1];
> int rc;
> @@ -210,7 +211,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>
> switch (subcode) {
> case DIAG_320_SUBC_QUERY_ISM:
> - uint64_t ism = 0;
> + uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
That likely should be a cpu_to_be64(DIAG_320_ISM_QUERY_VCSI) instead.
>
> if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
> be64_to_cpu(sizeof(ism)))) {
> @@ -218,6 +219,42 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> return;
> }
>
> + rc = DIAG_320_RC_OK;
> + break;
> + case DIAG_320_SUBC_QUERY_VCSI:
> + VerificationCertificateStorageSizeBlock vcssb;
> +
> + if (!diag_parm_addr_valid(addr, sizeof(VerificationCertificateStorageSizeBlock),
> + true)) {
> + s390_program_interrupt(env, PGM_ADDRESSING, ra);
> + return;
> + }
> +
> + if (!qcs || !qcs->count) {
> + vcssb.length = 4;
> + } else {
> + vcssb.length = VCSSB_MAX_LEN;
> + vcssb.version = 0;
> + vcssb.totalvc = qcs->count;
> + vcssb.maxvc = MAX_CERTIFICATES;
> + vcssb.maxvcelen = VCE_HEADER_LEN + qcs->max_cert_size;
> + vcssb.largestvcblen = VCB_HEADER_LEN + vcssb.maxvcelen;
> + vcssb.totalvcblen = VCB_HEADER_LEN + qcs->count * VCE_HEADER_LEN +
> + qcs->total_bytes;
You also need cpu_to_beXX() for these values here, too.
> + }
> +
> + if (vcssb.length < 128) {
> + rc = DIAG_320_RC_NOMEM;
> + break;
> + }
> +
> + if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &vcssb,
> + be64_to_cpu(
And that be64_to_cpu() is wrong here.
Thomas
> + sizeof(VerificationCertificateStorageSizeBlock)
> + ))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> rc = DIAG_320_RC_OK;
> break;
> default:
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1
2025-04-11 13:57 ` Thomas Huth
@ 2025-04-17 19:57 ` Collin Walling
0 siblings, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-17 19:57 UTC (permalink / raw)
To: Thomas Huth, Zhuoying Cai, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/11/25 9:57 AM, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
>> DIAG 320 subcode 1 provides information needed to determine
>> the amount of storage to store one or more certificates.
>>
>> The subcode value is denoted by setting the left-most bit
>> of an 8-byte field.
>>
>> The verification-certificate-storage-size block (VCSSB) contains
>> the output data when the operation completes successfully.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>> ---
>> include/hw/s390x/ipl/diag320.h | 25 ++++++++++++++++++++++
>> target/s390x/diag.c | 39 +++++++++++++++++++++++++++++++++-
>> 2 files changed, 63 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
>> index d6f70c65df..ded336df25 100644
>> --- a/include/hw/s390x/ipl/diag320.h
>> +++ b/include/hw/s390x/ipl/diag320.h
>> @@ -13,7 +13,32 @@
>> #define S390X_DIAG320_H
>>
>> #define DIAG_320_SUBC_QUERY_ISM 0
>> +#define DIAG_320_SUBC_QUERY_VCSI 1
>>
>> #define DIAG_320_RC_OK 0x0001
>> +#define DIAG_320_RC_NOMEM 0x0202
>> +
>> +#define VCSSB_MAX_LEN 128
>> +#define VCE_HEADER_LEN 128
>> +#define VCB_HEADER_LEN 64
>> +
>> +#define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
>> +
>> +struct VerificationCertificateStorageSizeBlock {
>> + uint32_t length;
>> + uint8_t reserved0[3];
>> + uint8_t version;
>> + uint32_t reserved1[6];
>> + uint16_t totalvc;
>> + uint16_t maxvc;
>> + uint32_t reserved3[7];
>> + uint32_t maxvcelen;
>> + uint32_t reserved4[3];
>> + uint32_t largestvcblen;
>> + uint32_t totalvcblen;
>> + uint32_t reserved5[10];
>> +} QEMU_PACKED;
>> +typedef struct VerificationCertificateStorageSizeBlock \
>> +VerificationCertificateStorageSizeBlock;
>
> That's quite a long name, maybe shorten to VerificationCertStorageBlock or
> something similar?
>
I think it would benefit many of the data structures using the
"VerificationCertificate" prefix to be short-handed to "VC".
[...]
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1
2025-04-08 15:55 ` [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
2025-04-11 13:57 ` Thomas Huth
@ 2025-04-11 17:40 ` Farhan Ali
1 sibling, 0 replies; 54+ messages in thread
From: Farhan Ali @ 2025-04-11 17:40 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/2025 8:55 AM, Zhuoying Cai wrote:
> DIAG 320 subcode 1 provides information needed to determine
> the amount of storage to store one or more certificates.
>
> The subcode value is denoted by setting the left-most bit
> of an 8-byte field.
>
> The verification-certificate-storage-size block (VCSSB) contains
> the output data when the operation completes successfully.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag320.h | 25 ++++++++++++++++++++++
> target/s390x/diag.c | 39 +++++++++++++++++++++++++++++++++-
> 2 files changed, 63 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
> index d6f70c65df..ded336df25 100644
> --- a/include/hw/s390x/ipl/diag320.h
> +++ b/include/hw/s390x/ipl/diag320.h
> @@ -13,7 +13,32 @@
> #define S390X_DIAG320_H
>
> #define DIAG_320_SUBC_QUERY_ISM 0
> +#define DIAG_320_SUBC_QUERY_VCSI 1
>
> #define DIAG_320_RC_OK 0x0001
> +#define DIAG_320_RC_NOMEM 0x0202
> +
> +#define VCSSB_MAX_LEN 128
> +#define VCE_HEADER_LEN 128
> +#define VCB_HEADER_LEN 64
> +
> +#define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
> +
> +struct VerificationCertificateStorageSizeBlock {
> + uint32_t length;
> + uint8_t reserved0[3];
> + uint8_t version;
> + uint32_t reserved1[6];
> + uint16_t totalvc;
> + uint16_t maxvc;
> + uint32_t reserved3[7];
> + uint32_t maxvcelen;
> + uint32_t reserved4[3];
> + uint32_t largestvcblen;
> + uint32_t totalvcblen;
> + uint32_t reserved5[10];
> +} QEMU_PACKED;
> +typedef struct VerificationCertificateStorageSizeBlock \
> +VerificationCertificateStorageSizeBlock;
>
> #endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index c64b935c87..cc639819ec 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -194,6 +194,7 @@ out:
> void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> {
> S390CPU *cpu = env_archcpu(env);
> + S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
> uint64_t subcode = env->regs[r3];
> uint64_t addr = env->regs[r1];
> int rc;
> @@ -210,7 +211,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>
> switch (subcode) {
> case DIAG_320_SUBC_QUERY_ISM:
> - uint64_t ism = 0;
> + uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
>
> if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
> be64_to_cpu(sizeof(ism)))) {
> @@ -218,6 +219,42 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> return;
> }
>
> + rc = DIAG_320_RC_OK;
> + break;
> + case DIAG_320_SUBC_QUERY_VCSI:
> + VerificationCertificateStorageSizeBlock vcssb;
> +
> + if (!diag_parm_addr_valid(addr, sizeof(VerificationCertificateStorageSizeBlock),
> + true)) {
> + s390_program_interrupt(env, PGM_ADDRESSING, ra);
> + return;
> + }
> +
> + if (!qcs || !qcs->count) {
> + vcssb.length = 4;
> + } else {
> + vcssb.length = VCSSB_MAX_LEN;
> + vcssb.version = 0;
> + vcssb.totalvc = qcs->count;
> + vcssb.maxvc = MAX_CERTIFICATES;
> + vcssb.maxvcelen = VCE_HEADER_LEN + qcs->max_cert_size;
> + vcssb.largestvcblen = VCB_HEADER_LEN + vcssb.maxvcelen;
> + vcssb.totalvcblen = VCB_HEADER_LEN + qcs->count * VCE_HEADER_LEN +
> + qcs->total_bytes;
> + }
> +
Do we need this if check below? or can we move it above where we check
for !qcs || !qcs->count?
> + if (vcssb.length < 128) {
> + rc = DIAG_320_RC_NOMEM;
> + break;
> + }
> +
> + if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &vcssb,
> + be64_to_cpu(
> + sizeof(VerificationCertificateStorageSizeBlock)
> + ))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> rc = DIAG_320_RC_OK;
> break;
> default:
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (5 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-16 21:32 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
` (16 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
DIAG 320 subcode 2 provides certificates that are in the
certificate store.
The subcode value is denoted by setting the second-left-most bit
of an 8-byte field.
The verification-certificate-block (VCB) contains the output data
when the operation completes successfully. VCB includes a common
header followed by zero or more verification-certificate entries (VCEs).
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
include/hw/s390x/ipl/diag320.h | 59 +++++++++
target/s390x/diag.c | 227 ++++++++++++++++++++++++++++++++-
2 files changed, 285 insertions(+), 1 deletion(-)
diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
index ded336df25..32b6914b3b 100644
--- a/include/hw/s390x/ipl/diag320.h
+++ b/include/hw/s390x/ipl/diag320.h
@@ -14,15 +14,24 @@
#define DIAG_320_SUBC_QUERY_ISM 0
#define DIAG_320_SUBC_QUERY_VCSI 1
+#define DIAG_320_SUBC_STORE_VC 2
#define DIAG_320_RC_OK 0x0001
#define DIAG_320_RC_NOMEM 0x0202
+#define DIAG_320_RC_INVAL_VCB_LEN 0x0204
+#define DIAG_320_RC_BAD_RANGE 0x0302
#define VCSSB_MAX_LEN 128
#define VCE_HEADER_LEN 128
#define VCB_HEADER_LEN 64
#define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
+#define DIAG_320_ISM_STORE_VC 0x2000000000000000
+
+#define DIAG_320_VCE_FLAGS_VALID 0x80
+#define DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING 0
+#define DIAG_320_VCE_FORMAT_X509_DER 1
+#define DIAG_320_VCE_HASHTYPE_SHA2_256 1
struct VerificationCertificateStorageSizeBlock {
uint32_t length;
@@ -41,4 +50,54 @@ struct VerificationCertificateStorageSizeBlock {
typedef struct VerificationCertificateStorageSizeBlock \
VerificationCertificateStorageSizeBlock;
+struct vcb_header {
+ uint32_t vcbinlen;
+ uint32_t reserved0;
+ uint16_t fvci;
+ uint16_t lvci;
+ uint32_t reserved1;
+ uint32_t cstoken;
+ uint32_t reserved2[3];
+ uint32_t vcboutlen;
+ uint8_t reserved3[3];
+ uint8_t version;
+ uint16_t svcc;
+ uint16_t rvcc;
+ uint32_t reserved4[5];
+} QEMU_PACKED;
+typedef struct vcb_header vcb_header;
+
+struct VerficationCertificateBlock {
+ vcb_header vcb_hdr;
+ uint8_t vcb_buf[];
+} QEMU_PACKED;
+typedef struct VerficationCertificateBlock VerficationCertificateBlock;
+
+struct vce_header {
+ uint32_t len;
+ uint8_t flags;
+ uint8_t keytype;
+ uint16_t certidx;
+ uint32_t name[16];
+ uint8_t format;
+ uint8_t reserved0;
+ uint16_t keyidlen;
+ uint8_t reserved1;
+ uint8_t hashtype;
+ uint16_t hashlen;
+ uint32_t reserved2;
+ uint32_t certlen;
+ uint32_t reserved3[2];
+ uint16_t hashoffset;
+ uint16_t certoffset;
+ uint32_t reserved4[7];
+} QEMU_PACKED;
+typedef struct vce_header vce_header;
+
+struct VerificationCertificateEntry {
+ vce_header vce_hdr;
+ uint8_t cert_data_buf[];
+} QEMU_PACKED;
+typedef struct VerificationCertificateEntry VerificationCertificateEntry;
+
#endif
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index cc639819ec..82e4dc9e1e 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -17,6 +17,7 @@
#include "s390x-internal.h"
#include "hw/watchdog/wdt_diag288.h"
#include "system/cpus.h"
+#include "hw/s390x/cert-store.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "system/kvm.h"
@@ -191,6 +192,94 @@ out:
}
}
+#ifdef CONFIG_GNUTLS
+static bool diag_320_is_cert_valid(gnutls_x509_crt_t cert)
+{
+ time_t now;
+
+ if (gnutls_x509_crt_get_version(cert) < 0) {
+ return false;
+ }
+
+ now = time(0);
+ if (!((gnutls_x509_crt_get_activation_time(cert) < now) &&
+ (gnutls_x509_crt_get_expiration_time(cert) > now))) {
+ return false;
+ }
+
+ return true;
+}
+#endif /* CONFIG_GNUTLS */
+
+static int diag_320_get_cert_info(VerificationCertificateEntry *vce,
+ S390IPLCertificate qcert, bool *is_valid,
+ unsigned char **key_id_data, void **hash_data)
+{
+#ifdef CONFIG_GNUTLS
+ unsigned int algo;
+ unsigned int bits;
+ int hash_type;
+ int rc;
+
+ gnutls_x509_crt_t g_cert = NULL;
+ if (g_init_cert((uint8_t *)qcert.raw, qcert.size, &g_cert)) {
+ return -1;
+ }
+
+ /* VCE flag (validity) */
+ *is_valid = diag_320_is_cert_valid(g_cert);
+
+ /* key-type */
+ algo = gnutls_x509_crt_get_pk_algorithm(g_cert, &bits);
+ if (algo == GNUTLS_PK_RSA) {
+ vce->vce_hdr.keytype = DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
+ }
+
+ /* VC format */
+ if (qcert.format == GNUTLS_X509_FMT_DER) {
+ vce->vce_hdr.format = DIAG_320_VCE_FORMAT_X509_DER;
+ }
+
+ /* key id and key id len */
+ *key_id_data = g_malloc0(qcert.key_id_size);
+ rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256,
+ *key_id_data, &qcert.key_id_size);
+ if (rc < 0) {
+ error_report("Fail to retrieve certificate key ID");
+ goto out;
+ }
+ vce->vce_hdr.keyidlen = (uint16_t)qcert.key_id_size;
+
+ /* hash type */
+ hash_type = gnutls_x509_crt_get_signature_algorithm(g_cert);
+ if (hash_type == GNUTLS_SIGN_RSA_SHA256) {
+ vce->vce_hdr.hashtype = DIAG_320_VCE_HASHTYPE_SHA2_256;
+ }
+
+ /* hash and hash len */
+ *hash_data = g_malloc0(qcert.hash_size);
+ rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256,
+ *hash_data, &qcert.hash_size);
+ if (rc < 0) {
+ error_report("Fail to retrieve certificate hash");
+ goto out;
+ }
+ vce->vce_hdr.hashlen = (uint16_t)qcert.hash_size;
+
+ gnutls_x509_crt_deinit(g_cert);
+
+ return 0;
+out:
+ gnutls_x509_crt_deinit(g_cert);
+ g_free(*key_id_data);
+ g_free(*hash_data);
+
+ return -1;
+#else
+ return -1;
+#endif /* CONFIG_GNUTLS */
+}
+
void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
{
S390CPU *cpu = env_archcpu(env);
@@ -211,7 +300,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
switch (subcode) {
case DIAG_320_SUBC_QUERY_ISM:
- uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
+ uint64_t ism = DIAG_320_ISM_QUERY_VCSI | DIAG_320_ISM_STORE_VC;
if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
be64_to_cpu(sizeof(ism)))) {
@@ -257,6 +346,142 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
}
rc = DIAG_320_RC_OK;
break;
+ case DIAG_320_SUBC_STORE_VC:
+ VerficationCertificateBlock *vcb;
+ size_t vce_offset = VCB_HEADER_LEN;
+ size_t remaining_space;
+ size_t vce_hdr_offset;
+ int i;
+
+ unsigned char *key_id_data = NULL;
+ void *hash_data = NULL;
+ bool is_valid = false;
+
+ vcb = g_new0(VerficationCertificateBlock, 1);
+ if (s390_cpu_virt_mem_read(cpu, addr, (uint8_t)r1, vcb, sizeof(*vcb))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+
+ if (vcb->vcb_hdr.vcbinlen % 4096 != 0) {
+ rc = DIAG_320_RC_INVAL_VCB_LEN;
+ g_free(vcb);
+ break;
+ }
+
+ if (1 > vcb->vcb_hdr.fvci || vcb->vcb_hdr.fvci > vcb->vcb_hdr.lvci) {
+ rc = DIAG_320_RC_BAD_RANGE;
+ g_free(vcb);
+ break;
+ }
+
+ vcb->vcb_hdr.vcboutlen = VCB_HEADER_LEN;
+ vcb->vcb_hdr.version = 0;
+ vcb->vcb_hdr.svcc = 0;
+ vcb->vcb_hdr.rvcc = 0;
+
+ remaining_space = vcb->vcb_hdr.vcbinlen - VCB_HEADER_LEN;
+
+ for (i = vcb->vcb_hdr.fvci - 1; i < vcb->vcb_hdr.lvci; i++) {
+ VerificationCertificateEntry vce;
+ S390IPLCertificate qcert;
+
+ /*
+ * If cert index goes beyond the highest cert
+ * store index (count - 1), then exit early
+ */
+ if (i >= qcs->count) {
+ break;
+ }
+
+ qcert = qcs->certs[i];
+
+ /*
+ * If there is no more space to store the cert,
+ * set the remaining verification cert count and
+ * break early.
+ */
+ if (remaining_space < qcert.size) {
+ vcb->vcb_hdr.rvcc = vcb->vcb_hdr.lvci - i;
+ break;
+ }
+
+ /* Construct VCE */
+ vce.vce_hdr.len = VCE_HEADER_LEN;
+ vce.vce_hdr.certidx = i + 1;
+ vce.vce_hdr.certlen = qcert.size;
+
+ strncpy((char *)vce.vce_hdr.name, (char *)qcert.vc_name, VC_NAME_LEN_BYTES);
+
+ rc = diag_320_get_cert_info(&vce, qcert, &is_valid, &key_id_data, &hash_data);
+ if (rc) {
+ continue;
+ }
+
+ vce.vce_hdr.len += vce.vce_hdr.keyidlen;
+ vce.vce_hdr.len += vce.vce_hdr.hashlen;
+ vce.vce_hdr.len += vce.vce_hdr.certlen;
+
+ vce.vce_hdr.hashoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen;
+ vce.vce_hdr.certoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen +
+ vce.vce_hdr.hashlen;
+
+ vce_hdr_offset = vce_offset;
+ vce_offset += VCE_HEADER_LEN;
+
+ /* Write Key ID */
+ if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, key_id_data,
+ be16_to_cpu(vce.vce_hdr.keyidlen))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+ vce_offset += vce.vce_hdr.keyidlen;
+
+ /* Write Hash key */
+ if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, hash_data,
+ be16_to_cpu(vce.vce_hdr.hashlen))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+ vce_offset += vce.vce_hdr.hashlen;
+
+ /* Write VCE cert data */
+ if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, qcert.raw,
+ be32_to_cpu(vce.vce_hdr.certlen))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+ vce_offset += qcert.size;
+
+ /* The certificate is valid and VCE contains the certificate */
+ if (is_valid) {
+ vce.vce_hdr.flags |= DIAG_320_VCE_FLAGS_VALID;
+ }
+
+ /* Write VCE Header */
+ if (s390_cpu_virt_mem_write(cpu, addr + vce_hdr_offset, (uint8_t)r1, &vce,
+ be32_to_cpu(VCE_HEADER_LEN))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+
+ vcb->vcb_hdr.vcboutlen += vce.vce_hdr.len;
+ remaining_space -= vce.vce_hdr.len;
+ vcb->vcb_hdr.svcc++;
+
+ g_free(key_id_data);
+ g_free(hash_data);
+ }
+
+ /* Finally, write the header */
+ if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, vcb,
+ be32_to_cpu(VCB_HEADER_LEN))) {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ return;
+ }
+ rc = DIAG_320_RC_OK;
+ g_free(vcb);
+ break;
default:
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2
2025-04-08 15:55 ` [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2 Zhuoying Cai
@ 2025-04-16 21:32 ` Collin Walling
0 siblings, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-16 21:32 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> DIAG 320 subcode 2 provides certificates that are in the
> certificate store.
>
> The subcode value is denoted by setting the second-left-most bit
> of an 8-byte field.
>
> The verification-certificate-block (VCB) contains the output data
> when the operation completes successfully. VCB includes a common
> header followed by zero or more verification-certificate entries (VCEs).
>
Please add a comment stating that only x509 certificates in DER format
and SHA-256 hash type are recognized.
Further, I would further explain what this 320 SC 2 implementation is
doing either in this commit message or where appropriate as comments:
- DIAG 320 uses a one-origin index for cert entries
- Describe the data structures in some more detail (mainly the more
obscure fields)
- Describe why the VCB header is written to userspace last
- Describe which fields are taken from the certificate and stored in
the data structures
- State that these certificates are retrieved from your cert store impl
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag320.h | 59 +++++++++
> target/s390x/diag.c | 227 ++++++++++++++++++++++++++++++++-
> 2 files changed, 285 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
> index ded336df25..32b6914b3b 100644
> --- a/include/hw/s390x/ipl/diag320.h
> +++ b/include/hw/s390x/ipl/diag320.h
> @@ -14,15 +14,24 @@
>
> #define DIAG_320_SUBC_QUERY_ISM 0
> #define DIAG_320_SUBC_QUERY_VCSI 1
> +#define DIAG_320_SUBC_STORE_VC 2
>
> #define DIAG_320_RC_OK 0x0001
> #define DIAG_320_RC_NOMEM 0x0202
> +#define DIAG_320_RC_INVAL_VCB_LEN 0x0204
> +#define DIAG_320_RC_BAD_RANGE 0x0302
>
> #define VCSSB_MAX_LEN 128
> #define VCE_HEADER_LEN 128
> #define VCB_HEADER_LEN 64
>
> #define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
> +#define DIAG_320_ISM_STORE_VC 0x2000000000000000
> +
> +#define DIAG_320_VCE_FLAGS_VALID 0x80
> +#define DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING 0
> +#define DIAG_320_VCE_FORMAT_X509_DER 1
> +#define DIAG_320_VCE_HASHTYPE_SHA2_256 1
>
> struct VerificationCertificateStorageSizeBlock {
> uint32_t length;
> @@ -41,4 +50,54 @@ struct VerificationCertificateStorageSizeBlock {
> typedef struct VerificationCertificateStorageSizeBlock \
> VerificationCertificateStorageSizeBlock;
>
> +struct vcb_header {
> + uint32_t vcbinlen;
> + uint32_t reserved0;
> + uint16_t fvci;> + uint16_t lvci;
> + uint32_t reserved1;
> + uint32_t cstoken;
> + uint32_t reserved2[3];
> + uint32_t vcboutlen;
> + uint8_t reserved3[3];
> + uint8_t version;
> + uint16_t svcc;
> + uint16_t rvcc;
> + uint32_t reserved4[5];
> +} QEMU_PACKED;
> +typedef struct vcb_header vcb_header;
s/vcbinlen/in_len
s/vcboutlen/out_len
s/fvci/first_vc_index
s/lvci/last_vc_index
s/cstoken/cs_token, which I guess we don't make use of at all... does it
make sense to just remove it and combine the reserved fields?
s/svcc/stored_ct
s/rvcc/remain_ct
This will make things much easier to understand.
> +
> +struct VerficationCertificateBlock {
> + vcb_header vcb_hdr;
> + uint8_t vcb_buf[];
s/vcb_buf/vce_buf
What is the justification of having a separate header struct? Can't we
just put everything in the VerificationCertificateBlock? It would make
some of the accessing in the functions below a LOT cleaner.
> +} QEMU_PACKED;
> +typedef struct VerficationCertificateBlock VerficationCertificateBlock;
> +
> +struct vce_header {
> + uint32_t len;
> + uint8_t flags;
> + uint8_t keytype;
> + uint16_t certidx;
> + uint32_t name[16];
> + uint8_t format;
> + uint8_t reserved0;
> + uint16_t keyidlen;
> + uint8_t reserved1;
> + uint8_t hashtype;
> + uint16_t hashlen;
> + uint32_t reserved2;
> + uint32_t certlen;
> + uint32_t reserved3[2];
> + uint16_t hashoffset;
> + uint16_t certoffset;
> + uint32_t reserved4[7];
Use underscores to separate words in these fields to make them easier on
the eyes.
> +} QEMU_PACKED;
> +typedef struct vce_header vce_header;
> +
> +struct VerificationCertificateEntry {
> + vce_header vce_hdr;
> + uint8_t cert_data_buf[];
s/cert_data_buf/cert_buf
and same comments as above w.r.t. to putting all header fields in here
> +} QEMU_PACKED;
> +typedef struct VerificationCertificateEntry VerificationCertificateEntry;
> +
> #endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index cc639819ec..82e4dc9e1e 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -17,6 +17,7 @@
> #include "s390x-internal.h"
> #include "hw/watchdog/wdt_diag288.h"
> #include "system/cpus.h"
> +#include "hw/s390x/cert-store.h"
> #include "hw/s390x/ipl.h"
> #include "hw/s390x/s390-virtio-ccw.h"
> #include "system/kvm.h"
> @@ -191,6 +192,94 @@ out:
> }
> }
>
> +#ifdef CONFIG_GNUTLS
> +static bool diag_320_is_cert_valid(gnutls_x509_crt_t cert)
> +{
> + time_t now;
> +
> + if (gnutls_x509_crt_get_version(cert) < 0) {
> + return false;
> + }
> +
> + now = time(0);
> + if (!((gnutls_x509_crt_get_activation_time(cert) < now) &&
> + (gnutls_x509_crt_get_expiration_time(cert) > now))) {
> + return false;
> + }
> +
> + return true;
> +}
qcrypto_tls_creds_check_cert_times may suffice for now.
> +#endif /* CONFIG_GNUTLS */
> +
> +static int diag_320_get_cert_info(VerificationCertificateEntry *vce,
> + S390IPLCertificate qcert, bool *is_valid,
> + unsigned char **key_id_data, void **hash_data)
> +{
> +#ifdef CONFIG_GNUTLS
> + unsigned int algo;
> + unsigned int bits;
> + int hash_type;
> + int rc;
> +
> + gnutls_x509_crt_t g_cert = NULL;
> + if (g_init_cert((uint8_t *)qcert.raw, qcert.size, &g_cert)) {
> + return -1;
> + }
> +
> + /* VCE flag (validity) */
> + *is_valid = diag_320_is_cert_valid(g_cert);
> +
> + /* key-type */
> + algo = gnutls_x509_crt_get_pk_algorithm(g_cert, &bits);
> + if (algo == GNUTLS_PK_RSA) {
> + vce->vce_hdr.keytype = DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
> + }
> +
> + /* VC format */
> + if (qcert.format == GNUTLS_X509_FMT_DER) {
> + vce->vce_hdr.format = DIAG_320_VCE_FORMAT_X509_DER;
> + }
> +
> + /* key id and key id len */
> + *key_id_data = g_malloc0(qcert.key_id_size);
> + rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256,
> + *key_id_data, &qcert.key_id_size);
> + if (rc < 0) {
> + error_report("Fail to retrieve certificate key ID");
> + goto out;
> + }
> + vce->vce_hdr.keyidlen = (uint16_t)qcert.key_id_size;
> +
> + /* hash type */
> + hash_type = gnutls_x509_crt_get_signature_algorithm(g_cert);
> + if (hash_type == GNUTLS_SIGN_RSA_SHA256) {
> + vce->vce_hdr.hashtype = DIAG_320_VCE_HASHTYPE_SHA2_256;
> + }
> +
> + /* hash and hash len */
> + *hash_data = g_malloc0(qcert.hash_size);
> + rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256,
> + *hash_data, &qcert.hash_size);
> + if (rc < 0) {
> + error_report("Fail to retrieve certificate hash");
> + goto out;
> + }
> + vce->vce_hdr.hashlen = (uint16_t)qcert.hash_size;
> +
> + gnutls_x509_crt_deinit(g_cert);
> +
> + return 0;
> +out:
> + gnutls_x509_crt_deinit(g_cert);
> + g_free(*key_id_data);
> + g_free(*hash_data);
> +
> + return -1;
> +#else
> + return -1;
> +#endif /* CONFIG_GNUTLS */
As per Daniel's suggestion, let's move all gnutls functions to somewhere
in the crypto/* directory. There seems to be a file aptly titled
"x509-utils.c" that may be a good starting point to contribute to.
A lot of this function should get offloaded to new/existing qcrypto
functions, so I'll reserve my comments for now.
> +}
> +
> void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> {
> S390CPU *cpu = env_archcpu(env);
> @@ -211,7 +300,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>
> switch (subcode) {
> case DIAG_320_SUBC_QUERY_ISM:
> - uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
> + uint64_t ism = DIAG_320_ISM_QUERY_VCSI | DIAG_320_ISM_STORE_VC;
>
> if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
> be64_to_cpu(sizeof(ism)))) {
> @@ -257,6 +346,142 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> }
> rc = DIAG_320_RC_OK;
> break;
> + case DIAG_320_SUBC_STORE_VC:
> + VerficationCertificateBlock *vcb;
> + size_t vce_offset = VCB_HEADER_LEN;
> + size_t remaining_space;
> + size_t vce_hdr_offset;
> + int i;
> +
> + unsigned char *key_id_data = NULL;
> + void *hash_data = NULL;
> + bool is_valid = false;
> +
> + vcb = g_new0(VerficationCertificateBlock, 1);
> + if (s390_cpu_virt_mem_read(cpu, addr, (uint8_t)r1, vcb, sizeof(*vcb))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> +
> + if (vcb->vcb_hdr.vcbinlen % 4096 != 0) {
I think you want to use TARGET_PAGE_MASK here instead of the explicit 4096.
> + rc = DIAG_320_RC_INVAL_VCB_LEN;
> + g_free(vcb);
> + break;
> + }
> +
> + if (1 > vcb->vcb_hdr.fvci || vcb->vcb_hdr.fvci > vcb->vcb_hdr.lvci) {
This is no longer entirely correct for vc index 0. If first and last
indexes are both 0, no certificates are returned and rc should be OK.
We may not use this feature in this patch series, but we need to be
compliant.
Additionally, it's valid to have first == 0 and last > first. In this
case, index 0 is skipped since the DIAG 320 cert store uses a one-origin
index.
> + rc = DIAG_320_RC_BAD_RANGE;
> + g_free(vcb);
> + break;
> + }
> +
> + vcb->vcb_hdr.vcboutlen = VCB_HEADER_LEN;
> + vcb->vcb_hdr.version = 0;
> + vcb->vcb_hdr.svcc = 0;
> + vcb->vcb_hdr.rvcc = 0;
> +
g_new0 initalizes all fields to 0, so no need to do that here.
> + remaining_space = vcb->vcb_hdr.vcbinlen - VCB_HEADER_LEN;
> +
> + for (i = vcb->vcb_hdr.fvci - 1; i < vcb->vcb_hdr.lvci; i++) {
> + VerificationCertificateEntry vce;> +
S390IPLCertificate qcert;
> +
> + /*
> + * If cert index goes beyond the highest cert
> + * store index (count - 1), then exit early
> + */
> + if (i >= qcs->count) {
> + break;
> + }
May make sense to just toss this in with the loop condition and then
initialize qcert = qcs->certs[i]; above.
> +
> + qcert = qcs->certs[i];
> +
> + /*
> + * If there is no more space to store the cert,
> + * set the remaining verification cert count and
> + * break early.
> + */
> + if (remaining_space < qcert.size) {
> + vcb->vcb_hdr.rvcc = vcb->vcb_hdr.lvci - i;
> + break;
> + }
> +
> + /* Construct VCE */
> + vce.vce_hdr.len = VCE_HEADER_LEN;
> + vce.vce_hdr.certidx = i + 1;
> + vce.vce_hdr.certlen = qcert.size;
> +
> + strncpy((char *)vce.vce_hdr.name, (char *)qcert.vc_name, VC_NAME_LEN_BYTES);
> +
> + rc = diag_320_get_cert_info(&vce, qcert, &is_valid, &key_id_data, &hash_data);
> + if (rc) {
> + continue;
> + }
> +
> + vce.vce_hdr.len += vce.vce_hdr.keyidlen;
> + vce.vce_hdr.len += vce.vce_hdr.hashlen;
> + vce.vce_hdr.len += vce.vce_hdr.certlen;
> +
> + vce.vce_hdr.hashoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen;
> + vce.vce_hdr.certoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen +
> + vce.vce_hdr.hashlen;
> +
> + vce_hdr_offset = vce_offset;
> + vce_offset += VCE_HEADER_LEN;
> +
> + /* Write Key ID */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, key_id_data,
> + be16_to_cpu(vce.vce_hdr.keyidlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += vce.vce_hdr.keyidlen;
> +
> + /* Write Hash key */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, hash_data,
> + be16_to_cpu(vce.vce_hdr.hashlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += vce.vce_hdr.hashlen;
Leading space.
> +
> + /* Write VCE cert data */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, qcert.raw,
> + be32_to_cpu(vce.vce_hdr.certlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += qcert.size;
> +
> + /* The certificate is valid and VCE contains the certificate */
> + if (is_valid) {
> + vce.vce_hdr.flags |= DIAG_320_VCE_FLAGS_VALID;
> + }
> +
> + /* Write VCE Header */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_hdr_offset, (uint8_t)r1, &vce,
> + be32_to_cpu(VCE_HEADER_LEN))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> +
> + vcb->vcb_hdr.vcboutlen += vce.vce_hdr.len;
> + remaining_space -= vce.vce_hdr.len;
> + vcb->vcb_hdr.svcc++;
> +
> + g_free(key_id_data);
> + g_free(hash_data);
> + }
> +
> + /* Finally, write the header */
/* Write VCB Header */
> + if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, vcb,
> + be32_to_cpu(VCB_HEADER_LEN))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }> + rc = DIAG_320_RC_OK;
> + g_free(vcb);
> + break;
> default:
> s390_program_interrupt(env, PGM_SPECIFICATION, ra);
> return;
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (6 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2 Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 14:22 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification Zhuoying Cai
` (15 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
From: Collin Walling <walling@linux.ibm.com>
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>
---
hw/s390x/ipl.h | 1 +
include/hw/s390x/ipl/diag508.h | 17 +++++++++++++++++
target/s390x/diag.c | 26 ++++++++++++++++++++++++++
target/s390x/kvm/kvm.c | 14 ++++++++++++++
target/s390x/s390x-internal.h | 2 ++
5 files changed, 60 insertions(+)
create mode 100644 include/hw/s390x/ipl/diag508.h
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 822535ad76..e9ef8ddccd 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -18,6 +18,7 @@
#include "exec/address-spaces.h"
#include "hw/qdev-core.h"
#include "hw/s390x/ipl/diag320.h"
+#include "hw/s390x/ipl/diag508.h"
#include "hw/s390x/ipl/qipl.h"
#include "qom/object.h"
diff --git a/include/hw/s390x/ipl/diag508.h b/include/hw/s390x/ipl/diag508.h
new file mode 100644
index 0000000000..83c4439cb2
--- /dev/null
+++ b/include/hw/s390x/ipl/diag508.h
@@ -0,0 +1,17 @@
+/*
+ * S/390 DIAGNOSE 508 definitions and structures
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Collin Walling <walling@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+
+#ifndef S390X_DIAG508_H
+#define S390X_DIAG508_H
+
+#define DIAG_508_SUBC_QUERY_SUBC 0x0000
+
+#endif
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index 82e4dc9e1e..ad7f4b5025 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -488,3 +488,29 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
}
env->regs[r1 + 1] = rc;
}
+
+void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
+{
+ uint64_t subcode = env->regs[r3];
+ int rc;
+
+ if (env->psw.mask & PSW_MASK_PSTATE) {
+ s390_program_interrupt(env, PGM_PRIVILEGED, ra);
+ return;
+ }
+
+ if ((subcode & ~0x0ffffULL) || (r1 & 1)) {
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
+ return;
+ }
+
+ switch (subcode) {
+ case DIAG_508_SUBC_QUERY_SUBC:
+ rc = 0;
+ break;
+ default:
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
+ return;
+ }
+ env->regs[r1 + 1] = rc;
+}
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index b013751478..a5c5150c04 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -101,6 +101,7 @@
#define DIAG_CERT_STORE 0x320
#define DIAG_KVM_HYPERCALL 0x500
#define DIAG_KVM_BREAKPOINT 0x501
+#define DIAG_SECURE_IPL 0x508
#define ICPT_INSTRUCTION 0x04
#define ICPT_PROGRAM 0x08
@@ -1572,6 +1573,16 @@ static void kvm_handle_diag_320(S390CPU *cpu, struct kvm_run *run)
handle_diag_320(&cpu->env, r1, r3, RA_IGNORED);
}
+static void kvm_handle_diag_508(S390CPU *cpu, struct kvm_run *run)
+{
+ uint64_t r1, r3;
+
+ r1 = (run->s390_sieic.ipa & 0x00f0) >> 4;
+ r3 = run->s390_sieic.ipa & 0x000f;
+
+ handle_diag_508(&cpu->env, r1, r3, RA_IGNORED);
+}
+
#define DIAG_KVM_CODE_MASK 0x000000000000ffff
static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
@@ -1605,6 +1616,9 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb)
case DIAG_CERT_STORE:
kvm_handle_diag_320(cpu, run);
break;
+ case DIAG_SECURE_IPL:
+ kvm_handle_diag_508(cpu, run);
+ break;
default:
trace_kvm_insn_diag(func_code);
kvm_s390_program_interrupt(cpu, PGM_SPECIFICATION);
diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h
index 86a652f833..df0973266a 100644
--- a/target/s390x/s390x-internal.h
+++ b/target/s390x/s390x-internal.h
@@ -402,6 +402,8 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3,
uintptr_t ra);
void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3,
uintptr_t ra);
+void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3,
+ uintptr_t ra);
/* translate.c */
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations
2025-04-08 15:55 ` [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
@ 2025-04-11 14:22 ` Thomas Huth
0 siblings, 0 replies; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 14:22 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> From: Collin Walling <walling@linux.ibm.com>
>
> 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>
> ---
> hw/s390x/ipl.h | 1 +
> include/hw/s390x/ipl/diag508.h | 17 +++++++++++++++++
> target/s390x/diag.c | 26 ++++++++++++++++++++++++++
> target/s390x/kvm/kvm.c | 14 ++++++++++++++
> target/s390x/s390x-internal.h | 2 ++
> 5 files changed, 60 insertions(+)
> create mode 100644 include/hw/s390x/ipl/diag508.h
>
> diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
> index 822535ad76..e9ef8ddccd 100644
> --- a/hw/s390x/ipl.h
> +++ b/hw/s390x/ipl.h
> @@ -18,6 +18,7 @@
> #include "exec/address-spaces.h"
> #include "hw/qdev-core.h"
> #include "hw/s390x/ipl/diag320.h"
> +#include "hw/s390x/ipl/diag508.h"
> #include "hw/s390x/ipl/qipl.h"
> #include "qom/object.h"
>
> diff --git a/include/hw/s390x/ipl/diag508.h b/include/hw/s390x/ipl/diag508.h
> new file mode 100644
> index 0000000000..83c4439cb2
> --- /dev/null
> +++ b/include/hw/s390x/ipl/diag508.h
> @@ -0,0 +1,17 @@
> +/*
> + * S/390 DIAGNOSE 508 definitions and structures
> + *
> + * Copyright 2025 IBM Corp.
> + * Author(s): Collin Walling <walling@linux.ibm.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or (at
> + * your option) any later version. See the COPYING file in the top-level
> + * directory.
> + */
> +
> +#ifndef S390X_DIAG508_H
> +#define S390X_DIAG508_H
> +
> +#define DIAG_508_SUBC_QUERY_SUBC 0x0000
> +
> +#endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index 82e4dc9e1e..ad7f4b5025 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -488,3 +488,29 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> }
> env->regs[r1 + 1] = rc;
> }
> +
> +void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> +{
> + uint64_t subcode = env->regs[r3];
> + int rc;
Do we need to check some feature bit here? e.g. check
s390_has_feat(S390_FEAT_DIAG_320) here, too?
> + if (env->psw.mask & PSW_MASK_PSTATE) {
> + s390_program_interrupt(env, PGM_PRIVILEGED, ra);
> + return;
> + }
> +
> + if ((subcode & ~0x0ffffULL) || (r1 & 1)) {
> + s390_program_interrupt(env, PGM_SPECIFICATION, ra);
> + return;
> + }
> +
> + switch (subcode) {
> + case DIAG_508_SUBC_QUERY_SUBC:
> + rc = 0;
> + break;
> + default:
> + s390_program_interrupt(env, PGM_SPECIFICATION, ra);
> + return;
> + }
> + env->regs[r1 + 1] = rc;
> +}
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (7 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 14:38 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 10/24] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB) Zhuoying Cai
` (14 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
From: Collin Walling <walling@linux.ibm.com>
DIAG 508 subcode 2 performs signature-verfication on signed components.
A signed component may be a Linux kernel image, or any other signed
binary. **Verification of initrd is not supported.**
The instruction call expects two item-pairs: an address of a device
component, an address of the analogous signature file (in PKCS#7 format),
and their respective lengths. All of this data should be encapsulated
within a Diag508SignatureVerificationBlock, with the CertificateStoreInfo
fields ignored. The DIAG handler will read from the provided addresses
to retrieve the necessary data, parse the signature file, then
perform the signature-verification. Because there is no way to
correlate a specific certificate to a component, each certificate
in the store is tried until either verification succeeds, or all
certs have been exhausted.
The subcode value is denoted by setting the second-to-left-most bit of
a 2-byte field.
A return code of 1 indicates success, and the index and length of the
corresponding certificate will be set in the CertificateStoreInfo
portion of the SigVerifBlock. The following values indicate failure:
0x0402: component data is invalid
0x0502: certificate is not in x509 format
0x0602: signature is not in PKCS#7 format
0x0702: signature-verification failed
Signed-off-by: Collin Walling <walling@linux.ibm.com>
---
include/hw/s390x/ipl/diag508.h | 25 +++++++
target/s390x/diag.c | 131 ++++++++++++++++++++++++++++++++-
2 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/include/hw/s390x/ipl/diag508.h b/include/hw/s390x/ipl/diag508.h
index 83c4439cb2..f8f4b6398e 100644
--- a/include/hw/s390x/ipl/diag508.h
+++ b/include/hw/s390x/ipl/diag508.h
@@ -13,5 +13,30 @@
#define S390X_DIAG508_H
#define DIAG_508_SUBC_QUERY_SUBC 0x0000
+#define DIAG_508_SUBC_SIG_VERIF 0x4000
+
+#define DIAG_508_RC_OK 0x0001
+#define DIAG_508_RC_NO_CERTS 0x0102
+#define DIAG_508_RC_CERT_NOT_FOUND 0x0202
+#define DIAG_508_RC_NO_MEM_FOR_CERT 0x0302
+#define DIAG_508_RC_INVAL_COMP_DATA 0x0402
+#define DIAG_508_RC_INVAL_X509_CERT 0x0502
+#define DIAG_508_RC_INVAL_PKCS7_SIG 0x0602
+#define DIAG_508_RC_FAIL_VERIF 0x0702
+
+struct Diag508CertificateStoreInfo {
+ uint8_t idx;
+ uint64_t len;
+} QEMU_PACKED;
+typedef struct Diag508CertificateStoreInfo Diag508CertificateStoreInfo;
+
+struct Diag508SignatureVerificationBlock {
+ Diag508CertificateStoreInfo csi;
+ uint64_t comp_len;
+ uint64_t comp_addr;
+ uint64_t sig_len;
+ uint64_t sig_addr;
+} QEMU_PACKED;
+typedef struct Diag508SignatureVerificationBlock Diag508SignatureVerificationBlock;
#endif
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index ad7f4b5025..cecb8bf130 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -25,6 +25,11 @@
#include "target/s390x/kvm/pv.h"
#include "qemu/error-report.h"
+#ifdef CONFIG_GNUTLS
+#include <gnutls/x509.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/pkcs7.h>
+#endif /* CONFIG_GNUTLS */
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
@@ -489,9 +494,67 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
env->regs[r1 + 1] = rc;
}
+#ifdef CONFIG_GNUTLS
+#define datum_init(datum, data, size) \
+ datum = (gnutls_datum_t){data, size}
+
+static int diag_508_init_comp(gnutls_datum_t *comp,
+ Diag508SignatureVerificationBlock *svb)
+{
+ uint8_t *svb_comp = NULL;
+
+ if (!svb->comp_len || !svb->comp_addr) {
+ error_report("No component data.");
+ return -1;
+ }
+
+ /*
+ * corrupted size vs. prev_size in fastbins, occurs during 2nd iteration,
+ * allocating 1mil bytes.
+ */
+ svb_comp = g_malloc0(svb->comp_len);
+ cpu_physical_memory_read(svb->comp_addr, svb_comp, svb->comp_len);
+
+ /*
+ * Component data is not written back to the caller,
+ * so no need to do a deep copy. Comp is freed when
+ * svb is freed.
+ */
+ datum_init(*comp, svb_comp, svb->comp_len);
+ return 0;
+}
+
+static int diag_508_init_signature(gnutls_pkcs7_t *sig,
+ Diag508SignatureVerificationBlock *svb)
+{
+ gnutls_datum_t datum_sig;
+ uint8_t *svb_sig = NULL;
+
+ if (!svb->sig_len || !svb->sig_addr) {
+ error_report("No signature data");
+ return -1;
+ }
+
+ svb_sig = g_malloc0(svb->sig_len);
+ cpu_physical_memory_read(svb->sig_addr, svb_sig, svb->sig_len);
+
+ if (gnutls_pkcs7_init(sig) < 0) {
+ error_report("Failed to initalize pkcs7 data.");
+ return -1;
+ }
+
+ datum_init(datum_sig, svb_sig, svb->sig_len);
+ return gnutls_pkcs7_import(*sig, &datum_sig, GNUTLS_X509_FMT_DER);
+
+}
+#endif /* CONFIG_GNUTLS */
+
void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
{
+ S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
+ size_t csi_size = sizeof(Diag508CertificateStoreInfo);
uint64_t subcode = env->regs[r3];
+ uint64_t addr = env->regs[r1];
int rc;
if (env->psw.mask & PSW_MASK_PSTATE) {
@@ -506,7 +569,73 @@ void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
switch (subcode) {
case DIAG_508_SUBC_QUERY_SUBC:
- rc = 0;
+ rc = DIAG_508_SUBC_SIG_VERIF;
+ break;
+ case DIAG_508_SUBC_SIG_VERIF:
+ size_t svb_size = sizeof(Diag508SignatureVerificationBlock);
+ Diag508SignatureVerificationBlock *svb;
+
+ if (!qcs || !qcs->count) {
+ error_report("No certificates in cert store.");
+ rc = DIAG_508_RC_NO_CERTS;
+ break;
+ }
+
+ if (!diag_parm_addr_valid(addr, svb_size, false) ||
+ !diag_parm_addr_valid(addr, csi_size, true)) {
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
+ return;
+ }
+
+ svb = g_new0(Diag508SignatureVerificationBlock, 1);
+ cpu_physical_memory_read(addr, svb, svb_size);
+
+#ifdef CONFIG_GNUTLS
+ gnutls_pkcs7_t sig = NULL;
+ gnutls_datum_t comp;
+ int i;
+
+ if (diag_508_init_comp(&comp, svb) < 0) {
+ rc = DIAG_508_RC_INVAL_COMP_DATA;
+ g_free(svb);
+ break;
+ }
+
+ if (diag_508_init_signature(&sig, svb) < 0) {
+ rc = DIAG_508_RC_INVAL_PKCS7_SIG;
+ gnutls_pkcs7_deinit(sig);
+ g_free(svb);
+ break;
+ }
+
+ rc = DIAG_508_RC_FAIL_VERIF;
+ /*
+ * It is uncertain which certificate contains
+ * the analogous key to verify the signed data
+ */
+ for (i = 0; i < qcs->count; i++) {
+ gnutls_x509_crt_t g_cert = NULL;
+ if (g_init_cert((uint8_t *)qcs->certs[i].raw, qcs->certs[i].size, &g_cert)) {
+ continue;
+ }
+
+ if (gnutls_pkcs7_verify_direct(sig, g_cert, 0, &comp, 0) == 0) {
+ svb->csi.idx = i;
+ svb->csi.len = qcs->certs[i].size;
+ cpu_physical_memory_write(addr, &svb->csi,
+ be32_to_cpu(csi_size));
+ rc = DIAG_508_RC_OK;
+ break;
+ }
+
+ gnutls_x509_crt_deinit(g_cert);
+ }
+
+ gnutls_pkcs7_deinit(sig);
+#else
+ rc = DIAG_508_RC_FAIL_VERIF;
+#endif /* CONFIG_GNUTLS */
+ g_free(svb);
break;
default:
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification
2025-04-08 15:55 ` [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification Zhuoying Cai
@ 2025-04-11 14:38 ` Thomas Huth
2025-04-11 17:30 ` Collin Walling
0 siblings, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 14:38 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> From: Collin Walling <walling@linux.ibm.com>
>
> DIAG 508 subcode 2 performs signature-verfication on signed components.
> A signed component may be a Linux kernel image, or any other signed
> binary. **Verification of initrd is not supported.**
>
> The instruction call expects two item-pairs: an address of a device
> component, an address of the analogous signature file (in PKCS#7 format),
> and their respective lengths. All of this data should be encapsulated
> within a Diag508SignatureVerificationBlock, with the CertificateStoreInfo
> fields ignored. The DIAG handler will read from the provided addresses
> to retrieve the necessary data, parse the signature file, then
> perform the signature-verification. Because there is no way to
> correlate a specific certificate to a component, each certificate
> in the store is tried until either verification succeeds, or all
> certs have been exhausted.
>
> The subcode value is denoted by setting the second-to-left-most bit of
> a 2-byte field.
>
> A return code of 1 indicates success, and the index and length of the
> corresponding certificate will be set in the CertificateStoreInfo
> portion of the SigVerifBlock. The following values indicate failure:
>
> 0x0402: component data is invalid
> 0x0502: certificate is not in x509 format
> 0x0602: signature is not in PKCS#7 format
> 0x0702: signature-verification failed
>
> Signed-off-by: Collin Walling <walling@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag508.h | 25 +++++++
> target/s390x/diag.c | 131 ++++++++++++++++++++++++++++++++-
> 2 files changed, 155 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/s390x/ipl/diag508.h b/include/hw/s390x/ipl/diag508.h
> index 83c4439cb2..f8f4b6398e 100644
> --- a/include/hw/s390x/ipl/diag508.h
> +++ b/include/hw/s390x/ipl/diag508.h
> @@ -13,5 +13,30 @@
> #define S390X_DIAG508_H
>
> #define DIAG_508_SUBC_QUERY_SUBC 0x0000
> +#define DIAG_508_SUBC_SIG_VERIF 0x4000
> +
> +#define DIAG_508_RC_OK 0x0001
> +#define DIAG_508_RC_NO_CERTS 0x0102
> +#define DIAG_508_RC_CERT_NOT_FOUND 0x0202
> +#define DIAG_508_RC_NO_MEM_FOR_CERT 0x0302
> +#define DIAG_508_RC_INVAL_COMP_DATA 0x0402
> +#define DIAG_508_RC_INVAL_X509_CERT 0x0502
> +#define DIAG_508_RC_INVAL_PKCS7_SIG 0x0602
> +#define DIAG_508_RC_FAIL_VERIF 0x0702
> +
> +struct Diag508CertificateStoreInfo {
> + uint8_t idx;
> + uint64_t len;
> +} QEMU_PACKED;
> +typedef struct Diag508CertificateStoreInfo Diag508CertificateStoreInfo;
> +
> +struct Diag508SignatureVerificationBlock {
> + Diag508CertificateStoreInfo csi;
> + uint64_t comp_len;
> + uint64_t comp_addr;
> + uint64_t sig_len;
> + uint64_t sig_addr;
> +} QEMU_PACKED;
> +typedef struct Diag508SignatureVerificationBlock Diag508SignatureVerificationBlock;
>
> #endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index ad7f4b5025..cecb8bf130 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -25,6 +25,11 @@
> #include "target/s390x/kvm/pv.h"
> #include "qemu/error-report.h"
>
> +#ifdef CONFIG_GNUTLS
> +#include <gnutls/x509.h>
> +#include <gnutls/gnutls.h>
> +#include <gnutls/pkcs7.h>
> +#endif /* CONFIG_GNUTLS */
>
> int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
> {
> @@ -489,9 +494,67 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> env->regs[r1 + 1] = rc;
> }
>
> +#ifdef CONFIG_GNUTLS
> +#define datum_init(datum, data, size) \
> + datum = (gnutls_datum_t){data, size}
> +
> +static int diag_508_init_comp(gnutls_datum_t *comp,
> + Diag508SignatureVerificationBlock *svb)
> +{
> + uint8_t *svb_comp = NULL;
> +
> + if (!svb->comp_len || !svb->comp_addr) {
> + error_report("No component data.");
> + return -1;
> + }
> +
> + /*
> + * corrupted size vs. prev_size in fastbins, occurs during 2nd iteration,
> + * allocating 1mil bytes.
I don't understand that comment - could you elaborate?
> + */
> + svb_comp = g_malloc0(svb->comp_len);
> + cpu_physical_memory_read(svb->comp_addr, svb_comp, svb->comp_len);
> +
> + /*
> + * Component data is not written back to the caller,
> + * so no need to do a deep copy. Comp is freed when
> + * svb is freed.
> + */
> + datum_init(*comp, svb_comp, svb->comp_len);
> + return 0;
> +}
> +
> +static int diag_508_init_signature(gnutls_pkcs7_t *sig,
> + Diag508SignatureVerificationBlock *svb)
> +{
> + gnutls_datum_t datum_sig;
> + uint8_t *svb_sig = NULL;
> +
> + if (!svb->sig_len || !svb->sig_addr) {
> + error_report("No signature data");
> + return -1;
> + }
> +
> + svb_sig = g_malloc0(svb->sig_len);
> + cpu_physical_memory_read(svb->sig_addr, svb_sig, svb->sig_len);
> +
> + if (gnutls_pkcs7_init(sig) < 0) {
> + error_report("Failed to initalize pkcs7 data.");
> + return -1;
> + }
> +
> + datum_init(datum_sig, svb_sig, svb->sig_len);
> + return gnutls_pkcs7_import(*sig, &datum_sig, GNUTLS_X509_FMT_DER);
> +
> +}
> +#endif /* CONFIG_GNUTLS */
> +
> void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> {
> + S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
> + size_t csi_size = sizeof(Diag508CertificateStoreInfo);
> uint64_t subcode = env->regs[r3];
> + uint64_t addr = env->regs[r1];
> int rc;
>
> if (env->psw.mask & PSW_MASK_PSTATE) {
> @@ -506,7 +569,73 @@ void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>
> switch (subcode) {
> case DIAG_508_SUBC_QUERY_SUBC:
> - rc = 0;
> + rc = DIAG_508_SUBC_SIG_VERIF;
> + break;
> + case DIAG_508_SUBC_SIG_VERIF:
> + size_t svb_size = sizeof(Diag508SignatureVerificationBlock);
> + Diag508SignatureVerificationBlock *svb;
> +
> + if (!qcs || !qcs->count) {
> + error_report("No certificates in cert store.");
Not sure whether we should print an error by default here ... it's likely
better to use a trace_...() function here, I think.
> + rc = DIAG_508_RC_NO_CERTS;
> + break;
> + }
> +
> + if (!diag_parm_addr_valid(addr, svb_size, false) ||
> + !diag_parm_addr_valid(addr, csi_size, true)) {
> + s390_program_interrupt(env, PGM_ADDRESSING, ra);
> + return;
> + }
> +
> + svb = g_new0(Diag508SignatureVerificationBlock, 1);
> + cpu_physical_memory_read(addr, svb, svb_size);
Do you need to byteswap the read values with be_to_cpuXX() here?
> +#ifdef CONFIG_GNUTLS
> + gnutls_pkcs7_t sig = NULL;
> + gnutls_datum_t comp;
> + int i;
> +
> + if (diag_508_init_comp(&comp, svb) < 0) {
> + rc = DIAG_508_RC_INVAL_COMP_DATA;
> + g_free(svb);
> + break;
> + }
> +
> + if (diag_508_init_signature(&sig, svb) < 0) {
> + rc = DIAG_508_RC_INVAL_PKCS7_SIG;
> + gnutls_pkcs7_deinit(sig);
> + g_free(svb);
> + break;
> + }
> +
> + rc = DIAG_508_RC_FAIL_VERIF;
> + /*
> + * It is uncertain which certificate contains
> + * the analogous key to verify the signed data
> + */
> + for (i = 0; i < qcs->count; i++) {
> + gnutls_x509_crt_t g_cert = NULL;
> + if (g_init_cert((uint8_t *)qcs->certs[i].raw, qcs->certs[i].size, &g_cert)) {
> + continue;
> + }
> +
> + if (gnutls_pkcs7_verify_direct(sig, g_cert, 0, &comp, 0) == 0) {
> + svb->csi.idx = i;
> + svb->csi.len = qcs->certs[i].size;
> + cpu_physical_memory_write(addr, &svb->csi,
> + be32_to_cpu(csi_size));
> + rc = DIAG_508_RC_OK;
> + break;
> + }
> +
> + gnutls_x509_crt_deinit(g_cert);
> + }
> +
> + gnutls_pkcs7_deinit(sig);
> +#else
> + rc = DIAG_508_RC_FAIL_VERIF;
> +#endif /* CONFIG_GNUTLS */
> + g_free(svb);
> break;
> default:
> s390_program_interrupt(env, PGM_SPECIFICATION, ra);
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification
2025-04-11 14:38 ` Thomas Huth
@ 2025-04-11 17:30 ` Collin Walling
0 siblings, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-11 17:30 UTC (permalink / raw)
To: Thomas Huth, Zhuoying Cai, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/11/25 10:38 AM, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
>> From: Collin Walling <walling@linux.ibm.com>
>>
>> DIAG 508 subcode 2 performs signature-verfication on signed components.
>> A signed component may be a Linux kernel image, or any other signed
>> binary. **Verification of initrd is not supported.**
>>
>> The instruction call expects two item-pairs: an address of a device
>> component, an address of the analogous signature file (in PKCS#7 format),
>> and their respective lengths. All of this data should be encapsulated
>> within a Diag508SignatureVerificationBlock, with the CertificateStoreInfo
>> fields ignored. The DIAG handler will read from the provided addresses
>> to retrieve the necessary data, parse the signature file, then
>> perform the signature-verification. Because there is no way to
>> correlate a specific certificate to a component, each certificate
>> in the store is tried until either verification succeeds, or all
>> certs have been exhausted.
>>
>> The subcode value is denoted by setting the second-to-left-most bit of
>> a 2-byte field.
>>
>> A return code of 1 indicates success, and the index and length of the
>> corresponding certificate will be set in the CertificateStoreInfo
>> portion of the SigVerifBlock. The following values indicate failure:
>>
>> 0x0402: component data is invalid
>> 0x0502: certificate is not in x509 format
>> 0x0602: signature is not in PKCS#7 format
>> 0x0702: signature-verification failed
>>
>> Signed-off-by: Collin Walling <walling@linux.ibm.com>
>> ---
>> include/hw/s390x/ipl/diag508.h | 25 +++++++
>> target/s390x/diag.c | 131 ++++++++++++++++++++++++++++++++-
>> 2 files changed, 155 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/hw/s390x/ipl/diag508.h b/include/hw/s390x/ipl/diag508.h
>> index 83c4439cb2..f8f4b6398e 100644
>> --- a/include/hw/s390x/ipl/diag508.h
>> +++ b/include/hw/s390x/ipl/diag508.h
>> @@ -13,5 +13,30 @@
>> #define S390X_DIAG508_H
>>
>> #define DIAG_508_SUBC_QUERY_SUBC 0x0000
>> +#define DIAG_508_SUBC_SIG_VERIF 0x4000
>> +
>> +#define DIAG_508_RC_OK 0x0001
>> +#define DIAG_508_RC_NO_CERTS 0x0102
>> +#define DIAG_508_RC_CERT_NOT_FOUND 0x0202
>> +#define DIAG_508_RC_NO_MEM_FOR_CERT 0x0302
>> +#define DIAG_508_RC_INVAL_COMP_DATA 0x0402
>> +#define DIAG_508_RC_INVAL_X509_CERT 0x0502
>> +#define DIAG_508_RC_INVAL_PKCS7_SIG 0x0602
>> +#define DIAG_508_RC_FAIL_VERIF 0x0702
>> +
>> +struct Diag508CertificateStoreInfo {
>> + uint8_t idx;
>> + uint64_t len;
>> +} QEMU_PACKED;
>> +typedef struct Diag508CertificateStoreInfo Diag508CertificateStoreInfo;
>> +
>> +struct Diag508SignatureVerificationBlock {
>> + Diag508CertificateStoreInfo csi;
>> + uint64_t comp_len;
>> + uint64_t comp_addr;
>> + uint64_t sig_len;
>> + uint64_t sig_addr;
>> +} QEMU_PACKED;
>> +typedef struct Diag508SignatureVerificationBlock Diag508SignatureVerificationBlock;
>>
>> #endif
>> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
>> index ad7f4b5025..cecb8bf130 100644
>> --- a/target/s390x/diag.c
>> +++ b/target/s390x/diag.c
>> @@ -25,6 +25,11 @@
>> #include "target/s390x/kvm/pv.h"
>> #include "qemu/error-report.h"
>>
>> +#ifdef CONFIG_GNUTLS
>> +#include <gnutls/x509.h>
>> +#include <gnutls/gnutls.h>
>> +#include <gnutls/pkcs7.h>
>> +#endif /* CONFIG_GNUTLS */
>>
>> int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
>> {
>> @@ -489,9 +494,67 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>> env->regs[r1 + 1] = rc;
>> }
>>
>> +#ifdef CONFIG_GNUTLS
>> +#define datum_init(datum, data, size) \
>> + datum = (gnutls_datum_t){data, size}
>> +
>> +static int diag_508_init_comp(gnutls_datum_t *comp,
>> + Diag508SignatureVerificationBlock *svb)
>> +{
>> + uint8_t *svb_comp = NULL;
>> +
>> + if (!svb->comp_len || !svb->comp_addr) {
>> + error_report("No component data.");
>> + return -1;
>> + }
>> +
>> + /*
>> + * corrupted size vs. prev_size in fastbins, occurs during 2nd iteration,
>> + * allocating 1mil bytes.
>
> I don't understand that comment - could you elaborate?
>
Elaboration: developer (me) forgot to remove this comment from an early
implementation. It is irrelevant to the code presented here, sorry.
If you're curious:
<irrelevant>
I may be incorrect here, but from what I was able to gather, this issue
was encountered during a memory allocation, specifically without
properly freeing the previous component.
When allocating memory for the 2nd component (also the 2nd
invocation/iteration of this function), I would see the "corrupted size
vs prev_size in fastbins" error. I think this is because the previous
component wasn't properly freed and there was a conflict between the
amount of space needed for the new component and however much was
available in the fastbins... debugging showed it was requesting 1mil bytes.
This has been corrected.
</irrelevant>
>> + */
>> + svb_comp = g_malloc0(svb->comp_len);
>> + cpu_physical_memory_read(svb->comp_addr, svb_comp, svb->comp_len);
>> +
>> + /*
>> + * Component data is not written back to the caller,
>> + * so no need to do a deep copy. Comp is freed when
>> + * svb is freed.
>> + */
>> + datum_init(*comp, svb_comp, svb->comp_len);
>> + return 0;
>> +}
>> +
>> +static int diag_508_init_signature(gnutls_pkcs7_t *sig,
>> + Diag508SignatureVerificationBlock *svb)
>> +{
>> + gnutls_datum_t datum_sig;
>> + uint8_t *svb_sig = NULL;
>> +
>> + if (!svb->sig_len || !svb->sig_addr) {
>> + error_report("No signature data");
>> + return -1;
>> + }
>> +
>> + svb_sig = g_malloc0(svb->sig_len);
>> + cpu_physical_memory_read(svb->sig_addr, svb_sig, svb->sig_len);
>> +
>> + if (gnutls_pkcs7_init(sig) < 0) {
>> + error_report("Failed to initalize pkcs7 data.");
>> + return -1;
>> + }
>> +
>> + datum_init(datum_sig, svb_sig, svb->sig_len);
>> + return gnutls_pkcs7_import(*sig, &datum_sig, GNUTLS_X509_FMT_DER);
>> +
>> +}
>> +#endif /* CONFIG_GNUTLS */
>> +
>> void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>> {
>> + S390IPLCertificateStore *qcs = s390_ipl_get_certificate_store();
>> + size_t csi_size = sizeof(Diag508CertificateStoreInfo);
>> uint64_t subcode = env->regs[r3];
>> + uint64_t addr = env->regs[r1];
>> int rc;
>>
>> if (env->psw.mask & PSW_MASK_PSTATE) {
>> @@ -506,7 +569,73 @@ void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>>
>> switch (subcode) {
>> case DIAG_508_SUBC_QUERY_SUBC:
>> - rc = 0;
>> + rc = DIAG_508_SUBC_SIG_VERIF;
>> + break;
>> + case DIAG_508_SUBC_SIG_VERIF:
>> + size_t svb_size = sizeof(Diag508SignatureVerificationBlock);
>> + Diag508SignatureVerificationBlock *svb;
>> +
>> + if (!qcs || !qcs->count) {
>> + error_report("No certificates in cert store.");
>
> Not sure whether we should print an error by default here ... it's likely
> better to use a trace_...() function here, I think.
>
The return value is likely sufficient enough, and the caller can decide
if it's necessary to print a message or handle it some other way.
>> + rc = DIAG_508_RC_NO_CERTS;
>> + break;
>> + }
>> +
>> + if (!diag_parm_addr_valid(addr, svb_size, false) ||
>> + !diag_parm_addr_valid(addr, csi_size, true)) {
>> + s390_program_interrupt(env, PGM_ADDRESSING, ra);
>> + return;
>> + }
>> +
>> + svb = g_new0(Diag508SignatureVerificationBlock, 1);
>> + cpu_physical_memory_read(addr, svb, svb_size);
>
> Do you need to byteswap the read values with be_to_cpuXX() here?
>
For cross-arch compliance, yeah... I'll fix that for next time.
>> +#ifdef CONFIG_GNUTLS
>> + gnutls_pkcs7_t sig = NULL;
>> + gnutls_datum_t comp;
>> + int i;
>> +
>> + if (diag_508_init_comp(&comp, svb) < 0) {
>> + rc = DIAG_508_RC_INVAL_COMP_DATA;
>> + g_free(svb);
>> + break;
>> + }
>> +
>> + if (diag_508_init_signature(&sig, svb) < 0) {
>> + rc = DIAG_508_RC_INVAL_PKCS7_SIG;
>> + gnutls_pkcs7_deinit(sig);
>> + g_free(svb);
>> + break;
>> + }
>> +
>> + rc = DIAG_508_RC_FAIL_VERIF;
>> + /*
>> + * It is uncertain which certificate contains
>> + * the analogous key to verify the signed data
>> + */
>> + for (i = 0; i < qcs->count; i++) {
>> + gnutls_x509_crt_t g_cert = NULL;
>> + if (g_init_cert((uint8_t *)qcs->certs[i].raw, qcs->certs[i].size, &g_cert)) {
>> + continue;
>> + }
>> +
>> + if (gnutls_pkcs7_verify_direct(sig, g_cert, 0, &comp, 0) == 0) {
>> + svb->csi.idx = i;
>> + svb->csi.len = qcs->certs[i].size;
>> + cpu_physical_memory_write(addr, &svb->csi,
>> + be32_to_cpu(csi_size));
>> + rc = DIAG_508_RC_OK;
>> + break;
>> + }
>> +
>> + gnutls_x509_crt_deinit(g_cert);
>> + }
>> +
>> + gnutls_pkcs7_deinit(sig);
>> +#else
>> + rc = DIAG_508_RC_FAIL_VERIF;
>> +#endif /* CONFIG_GNUTLS */
>> + g_free(svb);
>> break;
>> default:
>> s390_program_interrupt(env, PGM_SPECIFICATION, ra);
>
> Thomas
>
>
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 10/24] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB)
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (8 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 11/24] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers Zhuoying Cai
` (13 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The IPL information report block (IIRB) contains information used
to locate IPL records and to report the results of signature verification
of one or more secure components of the load device.
IIRB is stored immediately following the IPL Parameter Block. Results on
component verification in any case (failure or success) are stored.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/iplb.h | 62 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 08f259ff31..bdbc733e16 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -23,6 +23,68 @@ extern QemuIplParameters qipl;
extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
extern bool have_iplb;
+struct IplInfoReportBlockHeader {
+ uint32_t len;
+ uint8_t iirb_flags;
+ uint8_t reserved1[2];
+ uint8_t version;
+ uint8_t reserved2[8];
+} __attribute__ ((packed));
+typedef struct IplInfoReportBlockHeader IplInfoReportBlockHeader;
+
+struct IplInfoBlockHeader {
+ uint32_t len;
+ uint8_t ibt;
+ uint8_t reserved1[3];
+ uint8_t reserved2[8];
+} __attribute__ ((packed));
+typedef struct IplInfoBlockHeader IplInfoBlockHeader;
+
+enum IplIbt {
+ IPL_IBT_CERTIFICATES = 1,
+ IPL_IBT_COMPONENTS = 2,
+};
+
+struct IplSignatureCertificateEntry {
+ uint64_t addr;
+ uint64_t len;
+} __attribute__ ((packed));
+typedef struct IplSignatureCertificateEntry IplSignatureCertificateEntry;
+
+struct IplSignatureCertificateList {
+ IplInfoBlockHeader ipl_info_header;
+ IplSignatureCertificateEntry cert_entries[MAX_CERTIFICATES];
+} __attribute__ ((packed));
+typedef struct IplSignatureCertificateList IplSignatureCertificateList;
+
+#define S390_IPL_COMPONENT_FLAG_SC 0x80
+#define S390_IPL_COMPONENT_FLAG_CSV 0x40
+
+struct IplDeviceComponentEntry {
+ uint64_t addr;
+ uint64_t len;
+ uint8_t flags;
+ uint8_t reserved1[5];
+ uint16_t cert_index;
+ uint8_t reserved2[8];
+} __attribute__ ((packed));
+typedef struct IplDeviceComponentEntry IplDeviceComponentEntry;
+
+struct IplDeviceComponentList {
+ IplInfoBlockHeader ipl_info_header;
+ IplDeviceComponentEntry device_entries[MAX_CERTIFICATES];
+} __attribute__ ((packed));
+typedef struct IplDeviceComponentList IplDeviceComponentList;
+
+#define COMP_LIST_MAX sizeof(IplDeviceComponentList)
+#define CERT_LIST_MAX sizeof(IplSignatureCertificateList)
+
+struct IplInfoReportBlock {
+ IplInfoReportBlockHeader hdr;
+ uint8_t info_blks[COMP_LIST_MAX + CERT_LIST_MAX];
+} __attribute__ ((packed));
+typedef struct IplInfoReportBlock IplInfoReportBlock;
+
#define S390_IPL_TYPE_FCP 0x00
#define S390_IPL_TYPE_CCW 0x02
#define S390_IPL_TYPE_QEMU_SCSI 0xff
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 11/24] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (9 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 10/24] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB) Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
` (12 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
This patch is necessary because of the architectural design of
IPL Parameter Block (IPLB) and IPL Information Report Block (IIRB).
IIRB will be introduced in the next patch.
Define a memory space for both IPL Parameter Block (IPLB) and
IPL Information Report Block (IIRB) since IIRB is stored immediately
following IPLB.
Convert IPLB to pointer and it points to the start of the defined memory space.
IIRB points to the end of IPLB.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/iplb.h | 12 ++++++++++--
pc-bios/s390-ccw/jump2ipl.c | 6 +++---
pc-bios/s390-ccw/main.c | 34 +++++++++++++++++++---------------
pc-bios/s390-ccw/netmain.c | 8 ++++----
4 files changed, 36 insertions(+), 24 deletions(-)
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index bdbc733e16..11302e004d 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -20,7 +20,7 @@
#include <string.h>
extern QemuIplParameters qipl;
-extern IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
+extern IplParameterBlock *iplb;
extern bool have_iplb;
struct IplInfoReportBlockHeader {
@@ -85,6 +85,14 @@ struct IplInfoReportBlock {
} __attribute__ ((packed));
typedef struct IplInfoReportBlock IplInfoReportBlock;
+struct IplBlocks {
+ IplParameterBlock iplb;
+ IplInfoReportBlock iirb;
+} __attribute__ ((packed));
+typedef struct IplBlocks IplBlocks;
+
+extern IplBlocks ipl_data __attribute__((__aligned__(PAGE_SIZE)));
+
#define S390_IPL_TYPE_FCP 0x00
#define S390_IPL_TYPE_CCW 0x02
#define S390_IPL_TYPE_QEMU_SCSI 0xff
@@ -127,7 +135,7 @@ static inline bool load_next_iplb(void)
qipl.index++;
next_iplb = (IplParameterBlock *) qipl.next_iplb;
- memcpy(&iplb, next_iplb, sizeof(IplParameterBlock));
+ memcpy(iplb, next_iplb, sizeof(IplParameterBlock));
qipl.chain_len--;
qipl.next_iplb = qipl.next_iplb + sizeof(IplParameterBlock);
diff --git a/pc-bios/s390-ccw/jump2ipl.c b/pc-bios/s390-ccw/jump2ipl.c
index 86321d0f46..fa2ca5cbe1 100644
--- a/pc-bios/s390-ccw/jump2ipl.c
+++ b/pc-bios/s390-ccw/jump2ipl.c
@@ -43,11 +43,11 @@ int jump_to_IPL_code(uint64_t address)
* The IPLB for QEMU SCSI type devices must be rebuilt during re-ipl. The
* iplb.devno is set to the boot position of the target SCSI device.
*/
- if (iplb.pbt == S390_IPL_TYPE_QEMU_SCSI) {
- iplb.devno = qipl.index;
+ if (iplb->pbt == S390_IPL_TYPE_QEMU_SCSI) {
+ iplb->devno = qipl.index;
}
- if (have_iplb && !set_iplb(&iplb)) {
+ if (have_iplb && !set_iplb(iplb)) {
panic("Failed to set IPLB");
}
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 76bf743900..c9328f1c51 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -22,7 +22,9 @@
static SubChannelId blk_schid = { .one = 1 };
static char loadparm_str[LOADPARM_LEN + 1];
QemuIplParameters qipl;
-IplParameterBlock iplb __attribute__((__aligned__(PAGE_SIZE)));
+/* Ensure that IPLB and IIRB are page aligned and sequential in memory */
+IplBlocks ipl_data;
+IplParameterBlock *iplb;
bool have_iplb;
static uint16_t cutype;
LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
@@ -51,7 +53,7 @@ void write_subsystem_identification(void)
void write_iplb_location(void)
{
if (cutype == CU_TYPE_VIRTIO && virtio_get_device_type() != VIRTIO_ID_NET) {
- lowcore->ptr_iplb = ptr2u32(&iplb);
+ lowcore->ptr_iplb = ptr2u32(iplb);
}
}
@@ -162,7 +164,7 @@ static void menu_setup(void)
return;
}
- switch (iplb.pbt) {
+ switch (iplb->pbt) {
case S390_IPL_TYPE_CCW:
case S390_IPL_TYPE_QEMU_SCSI:
menu_set_parms(qipl.qipl_flags & BOOT_MENU_FLAG_MASK,
@@ -191,8 +193,8 @@ static void boot_setup(void)
{
char lpmsg[] = "LOADPARM=[________]\n";
- if (have_iplb && memcmp(iplb.loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
- ebcdic_to_ascii((char *) iplb.loadparm, loadparm_str, LOADPARM_LEN);
+ if (have_iplb && memcmp(iplb->loadparm, NO_LOADPARM, LOADPARM_LEN) != 0) {
+ ebcdic_to_ascii((char *) iplb->loadparm, loadparm_str, LOADPARM_LEN);
} else {
sclp_get_loadparm_ascii(loadparm_str);
}
@@ -216,21 +218,21 @@ static bool find_boot_device(void)
VDev *vdev = virtio_get_device();
bool found = false;
- switch (iplb.pbt) {
+ switch (iplb->pbt) {
case S390_IPL_TYPE_CCW:
vdev->scsi_device_selected = false;
- debug_print_int("device no. ", iplb.ccw.devno);
- blk_schid.ssid = iplb.ccw.ssid & 0x3;
+ debug_print_int("device no. ", iplb->ccw.devno);
+ blk_schid.ssid = iplb->ccw.ssid & 0x3;
debug_print_int("ssid ", blk_schid.ssid);
- found = find_subch(iplb.ccw.devno);
+ found = find_subch(iplb->ccw.devno);
break;
case S390_IPL_TYPE_QEMU_SCSI:
vdev->scsi_device_selected = true;
- vdev->selected_scsi_device.channel = iplb.scsi.channel;
- vdev->selected_scsi_device.target = iplb.scsi.target;
- vdev->selected_scsi_device.lun = iplb.scsi.lun;
- blk_schid.ssid = iplb.scsi.ssid & 0x3;
- found = find_subch(iplb.scsi.devno);
+ vdev->selected_scsi_device.channel = iplb->scsi.channel;
+ vdev->selected_scsi_device.target = iplb->scsi.target;
+ vdev->selected_scsi_device.lun = iplb->scsi.lun;
+ blk_schid.ssid = iplb->scsi.ssid & 0x3;
+ found = find_subch(iplb->scsi.devno);
break;
default:
puts("Unsupported IPLB");
@@ -311,10 +313,12 @@ static void probe_boot_device(void)
void main(void)
{
+ iplb = &ipl_data.iplb;
+
copy_qipl();
sclp_setup();
css_setup();
- have_iplb = store_iplb(&iplb);
+ have_iplb = store_iplb(iplb);
if (!have_iplb) {
boot_setup();
probe_boot_device();
diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c
index 719a547ada..49afd9100d 100644
--- a/pc-bios/s390-ccw/netmain.c
+++ b/pc-bios/s390-ccw/netmain.c
@@ -488,11 +488,11 @@ static bool virtio_setup(void)
*/
enable_mss_facility();
- if (have_iplb || store_iplb(&iplb)) {
- IPL_assert(iplb.pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected");
- dev_no = iplb.ccw.devno;
+ if (have_iplb || store_iplb(iplb)) {
+ IPL_assert(iplb->pbt == S390_IPL_TYPE_CCW, "IPL_TYPE_CCW expected");
+ dev_no = iplb->ccw.devno;
debug_print_int("device no. ", dev_no);
- net_schid.ssid = iplb.ccw.ssid & 0x3;
+ net_schid.ssid = iplb->ccw.ssid & 0x3;
debug_print_int("ssid ", net_schid.ssid);
found = find_net_dev(&schib, dev_no);
} else {
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (10 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 11/24] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 19:13 ` Farhan Ali
2025-04-08 15:55 ` [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
` (11 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Add IPIB flags to IPL Parameter Block to determine if IPL needs to
perform securely and if IPL Information Report Block (IIRB) exists.
Secure boot in audit mode will perform if certificate(s) exist in the
key store. IIRB will exist and results of verification will be stored in
IIRB.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.c | 20 ++++++++++++++++++++
include/hw/s390x/ipl/qipl.h | 6 +++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index b0810c9191..59ec81181d 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -431,6 +431,13 @@ S390IPLCertificateStore *s390_ipl_get_certificate_store(void)
return &ipl->cert_store;
}
+static bool s390_has_certificate(void)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ return ipl->cert_store.count > 0;
+}
+
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
{
CcwDevice *ccw_dev = NULL;
@@ -488,6 +495,19 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
iplb->flags |= DIAG308_FLAGS_LP_VALID;
+ /*
+ * Secure boot in audit mode will perform
+ * if certificate(s) exist in the key store.
+ *
+ * IPL Information Report Block (IIRB) will exist
+ * for secure boot in audit mode.
+ *
+ * Results of secure boot will be stored in IIRB.
+ */
+ if (s390_has_certificate()) {
+ iplb->hdr_flags |= DIAG308_IPIB_FLAGS_IPLIR;
+ }
+
return true;
}
diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
index b8e7d1da71..2355fcecbb 100644
--- a/include/hw/s390x/ipl/qipl.h
+++ b/include/hw/s390x/ipl/qipl.h
@@ -23,6 +23,9 @@
#define MAX_CERTIFICATES 64
#define CERT_MAX_SIZE (1024 * 8)
+#define DIAG308_IPIB_FLAGS_SIPL 0x40
+#define DIAG308_IPIB_FLAGS_IPLIR 0x20
+
/*
* The QEMU IPL Parameters will be stored at absolute address
* 204 (0xcc) which means it is 32-bit word aligned but not
@@ -104,7 +107,8 @@ typedef struct IplBlockQemuScsi IplBlockQemuScsi;
union IplParameterBlock {
struct {
uint32_t len;
- uint8_t reserved0[3];
+ uint8_t hdr_flags;
+ uint8_t reserved0[2];
uint8_t version;
uint32_t blk0_len;
uint8_t pbt;
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block
2025-04-08 15:55 ` [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
@ 2025-04-11 19:13 ` Farhan Ali
0 siblings, 0 replies; 54+ messages in thread
From: Farhan Ali @ 2025-04-11 19:13 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
[-- Attachment #1: Type: text/plain, Size: 1017 bytes --]
...snip...
>
> diff --git a/include/hw/s390x/ipl/qipl.h b/include/hw/s390x/ipl/qipl.h
> index b8e7d1da71..2355fcecbb 100644
> --- a/include/hw/s390x/ipl/qipl.h
> +++ b/include/hw/s390x/ipl/qipl.h
> @@ -23,6 +23,9 @@
> #define MAX_CERTIFICATES 64
> #define CERT_MAX_SIZE (1024 * 8)
>
> +#define DIAG308_IPIB_FLAGS_SIPL 0x40
> +#define DIAG308_IPIB_FLAGS_IPLIR 0x20
> +
nit: We have a diag308 flag defined here
https://github.com/qemu/qemu/blob/master/hw/s390x/ipl.h#L22. It would be
easier if we had the flags defined in one place.
> /*
> * The QEMU IPL Parameters will be stored at absolute address
> * 204 (0xcc) which means it is 32-bit word aligned but not
> @@ -104,7 +107,8 @@ typedef struct IplBlockQemuScsi IplBlockQemuScsi;
> union IplParameterBlock {
> struct {
> uint32_t len;
> - uint8_t reserved0[3];
> + uint8_t hdr_flags;
> + uint8_t reserved0[2];
> uint8_t version;
> uint32_t blk0_len;
> uint8_t pbt;
[-- Attachment #2: Type: text/html, Size: 1759 bytes --]
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of IPL Parameter Block
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (11 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 14:46 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
` (10 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The IPL Information Report Block (IIRB) immediately follows the IPL
Parameter Block (IPLB).
The IPLB struct is allocated 4KB in memory, and iplb->len indicates
the amount of memory currently used by the IPLB.
To ensure proper alignment of the IIRB and prevent overlap, set
iplb->len to the maximum length of the IPLB, allowing alignment
constraints to be determined based on its size.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.c | 6 +++---
hw/s390x/ipl.h | 1 +
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 59ec81181d..b646fcc74e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -460,7 +460,7 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
if (scsi_lp && strlen(scsi_lp) > 0) {
lp = scsi_lp;
}
- iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
+ iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
iplb->blk0_len =
cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
@@ -471,14 +471,14 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
break;
case CCW_DEVTYPE_VFIO:
- iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+ iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
iplb->pbt = S390_IPL_TYPE_CCW;
iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
break;
case CCW_DEVTYPE_VIRTIO_NET:
case CCW_DEVTYPE_VIRTIO:
- iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
+ iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
iplb->blk0_len =
cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
iplb->pbt = S390_IPL_TYPE_CCW;
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index e9ef8ddccd..c05f238753 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -114,6 +114,7 @@ QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
#define S390_IPLB_MIN_CCW_LEN 200
#define S390_IPLB_MIN_FCP_LEN 384
#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
+#define S390_IPLB_MAX_LEN 4096
static inline bool diag_parm_addr_valid(uint64_t addr, size_t size, bool write)
{
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of IPL Parameter Block
2025-04-08 15:55 ` [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
@ 2025-04-11 14:46 ` Thomas Huth
2025-04-11 15:39 ` Jared Rossi
0 siblings, 1 reply; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 14:46 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> The IPL Information Report Block (IIRB) immediately follows the IPL
> Parameter Block (IPLB).
>
> The IPLB struct is allocated 4KB in memory, and iplb->len indicates
> the amount of memory currently used by the IPLB.
>
> To ensure proper alignment of the IIRB and prevent overlap, set
> iplb->len to the maximum length of the IPLB, allowing alignment
> constraints to be determined based on its size.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> hw/s390x/ipl.c | 6 +++---
> hw/s390x/ipl.h | 1 +
> 2 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
> index 59ec81181d..b646fcc74e 100644
> --- a/hw/s390x/ipl.c
> +++ b/hw/s390x/ipl.c
> @@ -460,7 +460,7 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
> if (scsi_lp && strlen(scsi_lp) > 0) {
> lp = scsi_lp;
> }
> - iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
> iplb->blk0_len =
> cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
> iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
> @@ -471,14 +471,14 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
> iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
> break;
> case CCW_DEVTYPE_VFIO:
> - iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
> iplb->pbt = S390_IPL_TYPE_CCW;
> iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
> iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
> break;
> case CCW_DEVTYPE_VIRTIO_NET:
> case CCW_DEVTYPE_VIRTIO:
> - iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
> iplb->blk0_len =
> cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
> iplb->pbt = S390_IPL_TYPE_CCW;
Wouldn't it make sense to only do this iff the secure IPL is also used?
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of IPL Parameter Block
2025-04-11 14:46 ` Thomas Huth
@ 2025-04-11 15:39 ` Jared Rossi
0 siblings, 0 replies; 54+ messages in thread
From: Jared Rossi @ 2025-04-11 15:39 UTC (permalink / raw)
To: Thomas Huth, Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/11/25 10:46 AM, Thomas Huth wrote:
> On 08/04/2025 17.55, Zhuoying Cai wrote:
>> The IPL Information Report Block (IIRB) immediately follows the IPL
>> Parameter Block (IPLB).
>>
>> The IPLB struct is allocated 4KB in memory, and iplb->len indicates
>> the amount of memory currently used by the IPLB.
>>
>> To ensure proper alignment of the IIRB and prevent overlap, set
>> iplb->len to the maximum length of the IPLB, allowing alignment
>> constraints to be determined based on its size.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>> ---
>> hw/s390x/ipl.c | 6 +++---
>> hw/s390x/ipl.h | 1 +
>> 2 files changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
>> index 59ec81181d..b646fcc74e 100644
>> --- a/hw/s390x/ipl.c
>> +++ b/hw/s390x/ipl.c
>> @@ -460,7 +460,7 @@ static bool s390_build_iplb(DeviceState *dev_st,
>> IplParameterBlock *iplb)
>> if (scsi_lp && strlen(scsi_lp) > 0) {
>> lp = scsi_lp;
>> }
>> - iplb->len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
>> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
>> iplb->blk0_len =
>> cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN -
>> S390_IPLB_HEADER_LEN);
>> iplb->pbt = S390_IPL_TYPE_QEMU_SCSI;
>> @@ -471,14 +471,14 @@ static bool s390_build_iplb(DeviceState
>> *dev_st, IplParameterBlock *iplb)
>> iplb->scsi.ssid = ccw_dev->sch->ssid & 3;
>> break;
>> case CCW_DEVTYPE_VFIO:
>> - iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
>> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
>> iplb->pbt = S390_IPL_TYPE_CCW;
>> iplb->ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
>> iplb->ccw.ssid = ccw_dev->sch->ssid & 3;
>> break;
>> case CCW_DEVTYPE_VIRTIO_NET:
>> case CCW_DEVTYPE_VIRTIO:
>> - iplb->len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
>> + iplb->len = cpu_to_be32(S390_IPLB_MAX_LEN);
>> iplb->blk0_len =
>> cpu_to_be32(S390_IPLB_MIN_CCW_LEN -
>> S390_IPLB_HEADER_LEN);
>> iplb->pbt = S390_IPL_TYPE_CCW;
>
> Wouldn't it make sense to only do this iff the secure IPL is also used?
The size of the IPLB struct itself is always the 4K max length, just
that most of it is (currently) unused reserved space at the end. With
secure IPL this matters for alignment, because the next block (IIRB)
must follow immediately after the IPLB in memory, but I think in general
the length of the IPLB as a whole should be 4K anyway, since that is
what's actually allocated for the IplParameterBlock struct.
>
> Thomas
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (12 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-17 4:58 ` Thomas Huth
2025-04-17 18:54 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 15/24] pc-bios/s390-ccw: Refactor zipl_run() Zhuoying Cai
` (9 subsequent siblings)
23 siblings, 2 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Introduce Secure-IPL (SIPL) facility.
Use the abbreviation CBL (Consolidated-Boot-Loader facility at bit 0 of
byte 136) to represent bytes 136 and 137 for IPL device facilities of the
SCLP Read Info block.
Availability of SIPL facility is determined by byte 136 bit 1 of the
SCLP Read Info block.
When SIPL facility is installed, the IPL Parameter Block length must
contains value that is multiple of 8 bytes.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/sclp.c | 2 ++
include/hw/s390x/sclp.h | 4 +++-
target/s390x/cpu_features.c | 3 +++
target/s390x/cpu_features.h | 1 +
target/s390x/cpu_features_def.h.inc | 3 +++
target/s390x/cpu_models.c | 2 ++
target/s390x/gen-features.c | 1 +
target/s390x/kvm/kvm.c | 3 +++
8 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index 5945c9b1d8..bab65955b7 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -145,6 +145,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
s390_get_feat_block(S390_FEAT_TYPE_SCLP_FAC134,
&read_info->fac134);
+ s390_get_feat_block(S390_FEAT_TYPE_SCLP_CBL,
+ read_info->cbl);
}
read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
index d32f6180e0..97af95a78d 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -136,7 +136,9 @@ typedef struct ReadInfo {
uint32_t hmfai;
uint8_t _reserved7[134 - 128]; /* 128-133 */
uint8_t fac134;
- uint8_t _reserved8[144 - 135]; /* 135-143 */
+ uint8_t _reserved8;
+ uint8_t cbl[2]; /* 136-137 */
+ uint8_t _reserved9[144 - 137]; /* 138-143 */
struct CPUEntry entries[];
/*
* When the Extended-Length SCCB (ELS) feature is enabled the
diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index 99089ab3f5..e9371569cc 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -149,6 +149,9 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data);
clear_be_bit(s390_feat_def(S390_FEAT_DIAG_320)->bit, data);
break;
+ case S390_FEAT_TYPE_SCLP_CBL:
+ clear_be_bit(s390_feat_def(S390_FEAT_SIPL)->bit, data);
+ break;
default:
return;
}
diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h
index 5635839d03..823fcf8b90 100644
--- a/target/s390x/cpu_features.h
+++ b/target/s390x/cpu_features.h
@@ -24,6 +24,7 @@ typedef enum {
S390_FEAT_TYPE_SCLP_CONF_CHAR,
S390_FEAT_TYPE_SCLP_CONF_CHAR_EXT,
S390_FEAT_TYPE_SCLP_FAC134,
+ S390_FEAT_TYPE_SCLP_CBL,
S390_FEAT_TYPE_SCLP_CPU,
S390_FEAT_TYPE_MISC,
S390_FEAT_TYPE_PLO,
diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
index 65d38f546d..f874b9da6f 100644
--- a/target/s390x/cpu_features_def.h.inc
+++ b/target/s390x/cpu_features_def.h.inc
@@ -140,6 +140,9 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
+/* Features exposed via SCLP SCCB Facilities byte 136 - 137 (bit numbers relative to byte-136) */
+DEF_FEAT(SIPL, "sipl", SCLP_CBL, 1, "Seucre-IPL facility")
+
/* Features exposed via SCLP CPU info. */
DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
DEF_FEAT(SIE_SKEY, "skey", SCLP_CPU, 5, "SIE: Storage-key facility")
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index 7d65c40bd1..a83c27dcb8 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -263,6 +263,7 @@ bool s390_has_feat(S390Feat feat)
case S390_FEAT_SIE_CMMA:
case S390_FEAT_SIE_PFMFI:
case S390_FEAT_SIE_IBS:
+ case S390_FEAT_SIPL:
case S390_FEAT_CONFIGURATION_TOPOLOGY:
return false;
break;
@@ -507,6 +508,7 @@ static void check_consistency(const S390CPUModel *model)
{ S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP },
{ S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
+ { S390_FEAT_SIPL, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_NNPA, S390_FEAT_VECTOR },
{ S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
{ S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 52c649adcd..d973efbf72 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -697,6 +697,7 @@ static uint16_t full_GEN14_GA1[] = {
S390_FEAT_SIE_KSS,
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
S390_FEAT_DIAG_320,
+ S390_FEAT_SIPL,
};
#define full_GEN14_GA2 EmptyFeat
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index a5c5150c04..f418102b7f 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -2517,6 +2517,9 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
set_bit(S390_FEAT_DIAG_320, model->features);
+ /* Secure-IPL facility is handled entirely within QEMU */
+ set_bit(S390_FEAT_SIPL, model->features);
+
/* Test for Ultravisor features that influence secure guest behavior */
query_uv_feat_guest(model->features);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility
2025-04-08 15:55 ` [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
@ 2025-04-17 4:58 ` Thomas Huth
2025-04-17 18:54 ` Collin Walling
1 sibling, 0 replies; 54+ messages in thread
From: Thomas Huth @ 2025-04-17 4:58 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> Introduce Secure-IPL (SIPL) facility.
>
> Use the abbreviation CBL (Consolidated-Boot-Loader facility at bit 0 of
> byte 136) to represent bytes 136 and 137 for IPL device facilities of the
> SCLP Read Info block.
>
> Availability of SIPL facility is determined by byte 136 bit 1 of the
> SCLP Read Info block.
>
> When SIPL facility is installed, the IPL Parameter Block length must
> contains value that is multiple of 8 bytes.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
...
> diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
> index 65d38f546d..f874b9da6f 100644
> --- a/target/s390x/cpu_features_def.h.inc
> +++ b/target/s390x/cpu_features_def.h.inc
> @@ -140,6 +140,9 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
> DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
> DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
>
> +/* Features exposed via SCLP SCCB Facilities byte 136 - 137 (bit numbers relative to byte-136) */
> +DEF_FEAT(SIPL, "sipl", SCLP_CBL, 1, "Seucre-IPL facility")
s/Seucre/Secure/
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility
2025-04-08 15:55 ` [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
2025-04-17 4:58 ` Thomas Huth
@ 2025-04-17 18:54 ` Collin Walling
1 sibling, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-17 18:54 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> Introduce Secure-IPL (SIPL) facility.
>
> Use the abbreviation CBL (Consolidated-Boot-Loader facility at bit 0 of
> byte 136) to represent bytes 136 and 137 for IPL device facilities of the
> SCLP Read Info block.
This should *not* be used to label these bytes! This facility is
represented by a specific bit on the entire byte, and the other bits are
used for multiple things unrelated to CBL.
Just use the generic byte-naming scheme (e.g. fac136) for now until
these bits are defined and a better name comes up. Further, you only
need byte 136 for the secure IPL bit.
Further, explain the significance for the dependency of ELS in the
commit message:
"Byte 136's facilities cannot be represented without the availability of
the extended-length-SCCB, so add it as a check for consistency."
>
> Availability of SIPL facility is determined by byte 136 bit 1 of the
> SCLP Read Info block.
>
> When SIPL facility is installed, the IPL Parameter Block length must
> contains value that is multiple of 8 bytes.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> hw/s390x/sclp.c | 2 ++
> include/hw/s390x/sclp.h | 4 +++-
> target/s390x/cpu_features.c | 3 +++
> target/s390x/cpu_features.h | 1 +
> target/s390x/cpu_features_def.h.inc | 3 +++
> target/s390x/cpu_models.c | 2 ++
> target/s390x/gen-features.c | 1 +
> target/s390x/kvm/kvm.c | 3 +++
> 8 files changed, 18 insertions(+), 1 deletion(-)
>
> diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
> index 5945c9b1d8..bab65955b7 100644
> --- a/hw/s390x/sclp.c
> +++ b/hw/s390x/sclp.c
> @@ -145,6 +145,8 @@ static void read_SCP_info(SCLPDevice *sclp, SCCB *sccb)
> if (s390_has_feat(S390_FEAT_EXTENDED_LENGTH_SCCB)) {
> s390_get_feat_block(S390_FEAT_TYPE_SCLP_FAC134,
> &read_info->fac134);
> + s390_get_feat_block(S390_FEAT_TYPE_SCLP_CBL,
> + read_info->cbl);
> }
>
> read_info->facilities = cpu_to_be64(SCLP_HAS_CPU_INFO |
> diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
> index d32f6180e0..97af95a78d 100644
> --- a/include/hw/s390x/sclp.h
> +++ b/include/hw/s390x/sclp.h
> @@ -136,7 +136,9 @@ typedef struct ReadInfo {
> uint32_t hmfai;
> uint8_t _reserved7[134 - 128]; /* 128-133 */
> uint8_t fac134;
> - uint8_t _reserved8[144 - 135]; /* 135-143 */
> + uint8_t _reserved8;
> + uint8_t cbl[2]; /* 136-137 */
> + uint8_t _reserved9[144 - 137]; /* 138-143 */
> struct CPUEntry entries[];
> /*
> * When the Extended-Length SCCB (ELS) feature is enabled the
> diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
> index 99089ab3f5..e9371569cc 100644
> --- a/target/s390x/cpu_features.c
> +++ b/target/s390x/cpu_features.c
> @@ -149,6 +149,9 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
> clear_be_bit(s390_feat_def(S390_FEAT_DIAG_318)->bit, data);
> clear_be_bit(s390_feat_def(S390_FEAT_DIAG_320)->bit, data);
> break;
> + case S390_FEAT_TYPE_SCLP_CBL:
> + clear_be_bit(s390_feat_def(S390_FEAT_SIPL)->bit, data);
> + break;
> default:
> return;
> }
> diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h
> index 5635839d03..823fcf8b90 100644
> --- a/target/s390x/cpu_features.h
> +++ b/target/s390x/cpu_features.h
> @@ -24,6 +24,7 @@ typedef enum {
> S390_FEAT_TYPE_SCLP_CONF_CHAR,
> S390_FEAT_TYPE_SCLP_CONF_CHAR_EXT,
> S390_FEAT_TYPE_SCLP_FAC134,
> + S390_FEAT_TYPE_SCLP_CBL,
> S390_FEAT_TYPE_SCLP_CPU,
> S390_FEAT_TYPE_MISC,
> S390_FEAT_TYPE_PLO,
> diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
> index 65d38f546d..f874b9da6f 100644
> --- a/target/s390x/cpu_features_def.h.inc
> +++ b/target/s390x/cpu_features_def.h.inc
> @@ -140,6 +140,9 @@ DEF_FEAT(SIE_IBS, "ibs", SCLP_CONF_CHAR_EXT, 10, "SIE: Interlock-and-broadcast-s
> DEF_FEAT(DIAG_318, "diag318", SCLP_FAC134, 0, "Control program name and version codes")
> DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functions")
>
> +/* Features exposed via SCLP SCCB Facilities byte 136 - 137 (bit numbers relative to byte-136) */
> +DEF_FEAT(SIPL, "sipl", SCLP_CBL, 1, "Seucre-IPL facility")
> +
> /* Features exposed via SCLP CPU info. */
> DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
> DEF_FEAT(SIE_SKEY, "skey", SCLP_CPU, 5, "SIE: Storage-key facility")
> diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
> index 7d65c40bd1..a83c27dcb8 100644
> --- a/target/s390x/cpu_models.c
> +++ b/target/s390x/cpu_models.c
> @@ -263,6 +263,7 @@ bool s390_has_feat(S390Feat feat)
> case S390_FEAT_SIE_CMMA:
> case S390_FEAT_SIE_PFMFI:
> case S390_FEAT_SIE_IBS:
> + case S390_FEAT_SIPL:
> case S390_FEAT_CONFIGURATION_TOPOLOGY:
> return false;
> break;
> @@ -507,6 +508,7 @@ static void check_consistency(const S390CPUModel *model)
> { S390_FEAT_AP_QUEUE_INTERRUPT_CONTROL, S390_FEAT_AP },
> { S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
> { S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
> + { S390_FEAT_SIPL, S390_FEAT_EXTENDED_LENGTH_SCCB },
> { S390_FEAT_NNPA, S390_FEAT_VECTOR },
> { S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
> { S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
> diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
> index 52c649adcd..d973efbf72 100644
> --- a/target/s390x/gen-features.c
> +++ b/target/s390x/gen-features.c
> @@ -697,6 +697,7 @@ static uint16_t full_GEN14_GA1[] = {
> S390_FEAT_SIE_KSS,
> S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
> S390_FEAT_DIAG_320,
> + S390_FEAT_SIPL,
> };
>
> #define full_GEN14_GA2 EmptyFeat
> diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
> index a5c5150c04..f418102b7f 100644
> --- a/target/s390x/kvm/kvm.c
> +++ b/target/s390x/kvm/kvm.c
> @@ -2517,6 +2517,9 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
>
> set_bit(S390_FEAT_DIAG_320, model->features);
>
> + /* Secure-IPL facility is handled entirely within QEMU */
> + set_bit(S390_FEAT_SIPL, model->features);
> +
> /* Test for Ultravisor features that influence secure guest behavior */
> query_uv_feat_guest(model->features);
>
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 15/24] pc-bios/s390-ccw: Refactor zipl_run()
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (13 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 16/24] pc-bios/s390-ccw: Refactor zipl_load_segment function Zhuoying Cai
` (8 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Refactor to enhance readability before enabling secure IPL in later
patches.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 58 ++++++++++++++++++++++----------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 0f8baa0198..485b55f1bf 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -674,6 +674,38 @@ static int zipl_load_segment(ComponentEntry *entry)
return 0;
}
+static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
+{
+ while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
+ entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
+
+ /* Secure boot is off, so we skip signature entries */
+ if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
+ entry++;
+ continue;
+ }
+
+ if (zipl_load_segment(entry)) {
+ return -1;
+ }
+
+ entry++;
+
+ if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+ puts("Wrong entry value");
+ return -EINVAL;
+ }
+ }
+
+ if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+ puts("No EXEC entry");
+ return -EINVAL;
+ }
+
+ write_reset_psw(entry->compdat.load_psw);
+ return 0;
+}
+
/* Run a zipl program */
static int zipl_run(ScsiBlockPtr *pte)
{
@@ -700,34 +732,12 @@ static int zipl_run(ScsiBlockPtr *pte)
/* Load image(s) into RAM */
entry = (ComponentEntry *)(&header[1]);
- while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
- entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
-
- /* We don't support secure boot yet, so we skip signature entries */
- if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
- entry++;
- continue;
- }
-
- if (zipl_load_segment(entry)) {
- return -1;
- }
- entry++;
-
- if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
- puts("Wrong entry value");
- return -EINVAL;
- }
- }
-
- if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
- puts("No EXEC entry");
- return -EINVAL;
+ if (zipl_run_normal(entry, tmp_sec)) {
+ return -1;
}
/* should not return */
- write_reset_psw(entry->compdat.load_psw);
jump_to_IPL_code(0);
return -1;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 16/24] pc-bios/s390-ccw: Refactor zipl_load_segment function
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (14 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 15/24] pc-bios/s390-ccw: Refactor zipl_run() Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode Zhuoying Cai
` (7 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Make the address variable a parameter of zipl_load_segment.
Modify this function for reuse in the next patch, which allows
loading segment or signature data to the destination memory address.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 485b55f1bf..4fb3e99f4b 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -613,19 +613,17 @@ static int ipl_eckd(void)
* IPL a SCSI disk
*/
-static int zipl_load_segment(ComponentEntry *entry)
+static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
{
const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
ScsiBlockPtr *bprs = (void *)sec;
const int bprs_size = sizeof(sec);
block_number_t blockno;
- uint64_t address;
int i;
char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
blockno = entry->data.blockno;
- address = entry->compdat.load_addr;
debug_print_int("loading segment at block", blockno);
debug_print_int("addr", address);
@@ -685,7 +683,7 @@ static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
continue;
}
- if (zipl_load_segment(entry)) {
+ if (zipl_load_segment(entry, entry->compdat.load_addr)) {
return -1;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (15 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 16/24] pc-bios/s390-ccw: Refactor zipl_load_segment function Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-13 23:57 ` Jared Rossi
2025-04-17 22:39 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
` (6 subsequent siblings)
23 siblings, 2 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Enable secure IPL in audit mode, which performs signature verification,
but any error does not terminate the boot process. Only warnings will be
logged to the console instead.
Add a comp_len variable to store the length of a segment in
zipl_load_segment. comp_len variable is necessary to store the
calculated segment length and is used during signature verification.
Return the length on success, or a negative return code on failure.
Secure IPL in audit mode requires at least one certificate provided in
the key store along with necessary facilities (Secure IPL Facility,
Certificate Store Facility and secure IPL extension support).
Note: Secure IPL in audit mode is implemented for the SCSI scheme of
virtio-blk/virtio-scsi devices.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 344 +++++++++++++++++++++++++++++++++++-
pc-bios/s390-ccw/bootmap.h | 9 +
pc-bios/s390-ccw/iplb.h | 68 +++++++
pc-bios/s390-ccw/main.c | 9 +
pc-bios/s390-ccw/s390-ccw.h | 10 ++
pc-bios/s390-ccw/sclp.c | 43 +++++
pc-bios/s390-ccw/sclp.h | 6 +
7 files changed, 486 insertions(+), 3 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 4fb3e99f4b..bdbd6ccd96 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -30,6 +30,13 @@
/* Scratch space */
static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
+/* sector for storing certificates */
+static uint8_t certs_sec[CERT_MAX_SIZE * MAX_CERTIFICATES];
+/* sector for storing signatures */
+static uint8_t sig_sec[MAX_SECTOR_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+uint8_t vcb_data[MAX_SECTOR_SIZE * 4] __attribute__((__aligned__(PAGE_SIZE)));
+uint8_t vcssb_data[VCSSB_MAX_LEN] __attribute__((__aligned__(PAGE_SIZE)));
const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
@@ -622,6 +629,7 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
int i;
char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
+ int comp_len = 0;
blockno = entry->data.blockno;
@@ -660,6 +668,9 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
*/
break;
}
+
+ comp_len += (uint64_t)bprs->size * ((uint64_t)bprs[i].blockct + 1);
+
address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
(void *)address);
if (!address) {
@@ -669,6 +680,305 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
}
} while (blockno);
+ return comp_len;
+}
+
+int get_vcssb(VerificationCertificateStorageSizeBlock *vcssb)
+{
+ int rc;
+
+ /* avoid retrieving vcssb multiple times */
+ if (vcssb->length == VCSSB_MAX_LEN) {
+ return 0;
+ }
+
+ rc = diag320(vcssb, DIAG_320_SUBC_QUERY_VCSI);
+ if (rc != DIAG_320_RC_OK) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static inline uint32_t request_certificate(uint64_t *cert, uint8_t index)
+{
+ VerificationCertificateStorageSizeBlock *vcssb;
+ VerficationCertificateBlock *vcb;
+ VerificationCertificateEntry *vce;
+ uint64_t rc = 0;
+ uint32_t cert_len = 0;
+
+ vcssb = (VerificationCertificateStorageSizeBlock *)vcssb_data;
+ vcb = (VerficationCertificateBlock *)vcb_data;
+
+ /* Get Verification Certificate Storage Size block with DIAG320 subcode 1 */
+ if (get_vcssb(vcssb)) {
+ return 0;
+ }
+
+ /*
+ * Request single entry
+ * Fill input fields of single-entry VCB
+ */
+ vcb->vcb_hdr.vcbinlen = ROUND_UP(vcssb->largestvcblen, PAGE_SIZE);
+ vcb->vcb_hdr.fvci = index + 1;
+ vcb->vcb_hdr.lvci = index + 1;
+
+ rc = diag320(vcb, DIAG_320_SUBC_STORE_VC);
+ if (rc == DIAG_320_RC_OK) {
+ vce = (VerificationCertificateEntry *)vcb->vcb_buf;
+ cert_len = vce->vce_hdr.certlen;
+ memcpy(cert, (uint8_t *)vce + vce->vce_hdr.certoffset, vce->vce_hdr.certlen);
+ /* clear out region for next cert(s) */
+ memcpy(vcb_data, 0, sizeof(vcb_data));
+ }
+
+ return cert_len;
+}
+
+static int cert_table_add(uint64_t **cert_table, uint64_t **cert,
+ uint64_t cert_len, uint8_t cert_idx)
+{
+ if (request_certificate(*cert, cert_idx)) {
+ /* save certificate address to cert_table */
+ cert_table[cert_idx] = *cert;
+ /* update cert address for the next certificate */
+ *cert += cert_len;
+ } else {
+ puts("Could not get certificate");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void cert_list_add(IplSignatureCertificateList *certs, int cert_index,
+ uint64_t *cert, uint64_t cert_len)
+{
+ if (cert_index > MAX_CERTIFICATES - 1) {
+ printf("Warning: Ignoring cert entry [%d] because it's over 64 entires\n",
+ cert_index + 1);
+ return;
+ }
+
+ certs->cert_entries[cert_index].addr = (uint64_t)cert;
+ certs->cert_entries[cert_index].len = cert_len;
+ certs->ipl_info_header.len += sizeof(certs->cert_entries[cert_index]);
+}
+
+static void comp_list_add(IplDeviceComponentList *comps, int comp_index,
+ int cert_index, uint64_t comp_addr,
+ uint64_t comp_len, uint8_t flags)
+{
+ if (comp_index > MAX_CERTIFICATES - 1) {
+ printf("Warning: Ignoring comp entry [%d] because it's over 64 entires\n",
+ comp_index + 1);
+ return;
+ }
+
+ comps->device_entries[comp_index].addr = comp_addr;
+ comps->device_entries[comp_index].len = comp_len;
+ comps->device_entries[comp_index].flags = flags;
+ comps->device_entries[comp_index].cert_index = cert_index;
+ comps->ipl_info_header.len += sizeof(comps->device_entries[comp_index]);
+}
+
+static int update_iirb(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
+{
+ IplInfoReportBlock *iirb;
+ IplDeviceComponentList *comp_list;
+ IplSignatureCertificateList *cert_list;
+
+ if (iplb->len % 8 != 0) {
+ puts("IPL parameter block length field value is not multiple of 8 bytes");
+ return -1;
+ }
+
+ /* IIRB immediately follows IPLB */
+ iirb = &ipl_data.iirb;
+ iirb->hdr.len = sizeof(IplInfoReportBlockHeader);
+
+ /* Copy IPL device component list after IIRB Header */
+ comp_list = (IplDeviceComponentList *) iirb->info_blks;
+ memcpy(comp_list, comps, comps->ipl_info_header.len);
+
+ /* Update IIRB length */
+ iirb->hdr.len += comps->ipl_info_header.len;
+
+ /* Copy IPL sig cert list after IPL device component list */
+ cert_list = (IplSignatureCertificateList *) (iirb->info_blks +
+ comp_list->ipl_info_header.len);
+ memcpy(cert_list, certs, certs->ipl_info_header.len);
+
+ /* Update IIRB length */
+ iirb->hdr.len += certs->ipl_info_header.len;
+
+ return 0;
+}
+
+static bool secure_ipl_supported(void)
+{
+ if (!sclp_is_sipl_on()) {
+ puts("Secure IPL Facility is not supported by the hypervisor!");
+ return false;
+ }
+
+ if (!is_secure_ipl_extension_supported()) {
+ puts("Secure IPL extensions are not supported by the hypervisor!");
+ return false;
+ }
+
+ if (!(sclp_is_diag320_on() && is_cert_store_facility_supported())) {
+ puts("Certificate Store Facility is not supported by the hypervisor!");
+ return false;
+ }
+
+ return true;
+}
+
+static void init_lists(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
+{
+ comps->ipl_info_header.ibt = IPL_IBT_COMPONENTS;
+ comps->ipl_info_header.len = sizeof(comps->ipl_info_header);
+
+ certs->ipl_info_header.ibt = IPL_IBT_CERTIFICATES;
+ certs->ipl_info_header.len = sizeof(certs->ipl_info_header);
+}
+
+static bool check_sig_entry(ComponentEntry *entry, uint32_t *sig_len)
+{
+ if ((entry + 1)->component_type != ZIPL_COMP_ENTRY_LOAD) {
+ puts("Next component does not contain signed binary code");
+ return false;
+ }
+
+ if (zipl_load_segment(entry, (uint64_t)sig_sec) < 0) {
+ return false;
+ };
+
+ if (entry->compdat.sig_info.format != DER_SIGNATURE_FORMAT) {
+ puts("Signature is not in DER format");
+ return false;
+ }
+
+ *sig_len = entry->compdat.sig_info.sig_len;
+ return true;
+}
+
+static int perform_sig_verf(uint64_t comp_addr, uint64_t comp_len, uint64_t sig_len,
+ uint64_t *cert_table[], uint64_t **cert,
+ IplDeviceComponentList *comps,
+ IplSignatureCertificateList *certs,
+ int comp_index, int cert_index,
+ void (*print_func)(bool, const char *))
+{
+ uint64_t cert_len = -1;
+ uint8_t cert_idx = -1;
+ bool verified;
+
+ verified = verify_signature(comp_len, comp_addr, sig_len, (uint64_t)sig_sec,
+ &cert_len, &cert_idx);
+
+ if (verified) {
+ if (cert_table[cert_idx] == 0) {
+ if (cert_table_add(cert_table, cert, cert_len, cert_idx)) {
+ return -1;
+ }
+ }
+
+ puts("Verified component");
+ cert_list_add(certs, cert_index, cert_table[cert_idx], cert_len);
+ comp_list_add(comps, comp_index, cert_index, comp_addr, comp_len,
+ S390_IPL_COMPONENT_FLAG_SC | S390_IPL_COMPONENT_FLAG_CSV);
+ } else {
+ comp_list_add(comps, comp_index, -1, comp_addr, comp_len,
+ S390_IPL_COMPONENT_FLAG_SC);
+ print_func(verified, "Could not verify component");
+ }
+
+ return 0;
+}
+
+static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
+{
+ bool found_signature = false;
+ struct IplDeviceComponentList comps;
+ struct IplSignatureCertificateList certs;
+ uint64_t *cert = (uint64_t *)certs_sec;
+ int cert_index = 0;
+ int comp_index = 0;
+ int comp_len;
+ bool valid_sig;
+ uint32_t sig_len;
+ /*
+ * Store address of certificate to prevent allocating
+ * the same certificate multiple times.
+ */
+ uint64_t *cert_table[MAX_CERTIFICATES];
+
+ void (*print_func)(bool, const char *) = NULL;
+ print_func = &IPL_check;
+
+ if (!secure_ipl_supported()) {
+ return -1;
+ }
+
+ init_lists(&comps, &certs);
+
+ valid_sig = false;
+ while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
+ entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
+
+ if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
+ valid_sig = check_sig_entry(entry, &sig_len);
+ if (!valid_sig) {
+ return -1;
+ }
+ } else {
+ comp_len = zipl_load_segment(entry, entry->compdat.load_addr);
+ if (comp_len < 0) {
+ return -1;
+ }
+
+ if (valid_sig) {
+ perform_sig_verf(entry->compdat.load_addr, comp_len, sig_len, cert_table,
+ &cert, &comps, &certs, comp_index, cert_index,
+ print_func);
+
+ cert_index++;
+ found_signature = true;
+ /*
+ * complete signature verification for current component,
+ * reset variable for the next signature entry.
+ */
+ valid_sig = false;
+ }
+
+ comp_index++;
+ }
+
+ entry++;
+
+ if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
+ puts("Wrong entry value");
+ return -EINVAL;
+ }
+ }
+
+ if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
+ puts("No EXEC entry");
+ return -EINVAL;
+ }
+
+ if (!found_signature) {
+ print_func(found_signature, "Secure boot is on, but components are not signed");
+ }
+
+ if (update_iirb(&comps, &certs)) {
+ print_func(false, "Failed to write IPL Information Report Block");
+ }
+ write_reset_psw(entry->compdat.load_psw);
+
return 0;
}
@@ -683,7 +993,7 @@ static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
continue;
}
- if (zipl_load_segment(entry, entry->compdat.load_addr)) {
+ if (zipl_load_segment(entry, entry->compdat.load_addr) < 0) {
return -1;
}
@@ -731,8 +1041,17 @@ static int zipl_run(ScsiBlockPtr *pte)
/* Load image(s) into RAM */
entry = (ComponentEntry *)(&header[1]);
- if (zipl_run_normal(entry, tmp_sec)) {
- return -1;
+ switch (boot_mode) {
+ case ZIPL_SECURE_AUDIT_MODE:
+ if (zipl_run_secure(entry, tmp_sec)) {
+ return -1;
+ }
+ break;
+ case ZIPL_NORMAL_MODE:
+ if (zipl_run_normal(entry, tmp_sec)) {
+ return -1;
+ }
+ break;
}
/* should not return */
@@ -1091,17 +1410,32 @@ static int zipl_load_vscsi(void)
* IPL starts here
*/
+int zipl_mode(void)
+{
+ uint32_t cert_len;
+
+ cert_len = request_certificate((uint64_t *)certs_sec, 0);
+
+ return (cert_len > 0) ? ZIPL_SECURE_AUDIT_MODE : ZIPL_NORMAL_MODE;
+}
+
void zipl_load(void)
{
VDev *vdev = virtio_get_device();
if (vdev->is_cdrom) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ panic("Secure boot from ISO image is not supported!");
+ }
ipl_iso_el_torito();
puts("Failed to IPL this ISO image!");
return;
}
if (virtio_get_device_type() == VIRTIO_ID_NET) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ panic("Virtio net boot device does not support secure boot!");
+ }
netmain();
puts("Failed to IPL from this network!");
return;
@@ -1112,6 +1446,10 @@ void zipl_load(void)
return;
}
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ panic("ECKD boot device does not support secure boot!");
+ }
+
switch (virtio_get_device_type()) {
case VIRTIO_ID_BLOCK:
zipl_load_vblk();
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 95943441d3..e48823a835 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -88,9 +88,18 @@ typedef struct BootMapTable {
BootMapPointer entry[];
} __attribute__ ((packed)) BootMapTable;
+#define DER_SIGNATURE_FORMAT 1
+
+typedef struct SignatureInformation {
+ uint8_t format;
+ uint8_t reserved[3];
+ uint32_t sig_len;
+} __attribute__((packed)) SignatureInformation;
+
typedef union ComponentEntryData {
uint64_t load_psw;
uint64_t load_addr;
+ SignatureInformation sig_info;
} ComponentEntryData;
typedef struct ComponentEntry {
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 11302e004d..8d9fdde30a 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -16,12 +16,15 @@
#define QEMU_PACKED __attribute__((packed))
#endif
+#include <diag320.h>
+#include <diag508.h>
#include <qipl.h>
#include <string.h>
extern QemuIplParameters qipl;
extern IplParameterBlock *iplb;
extern bool have_iplb;
+extern int boot_mode;
struct IplInfoReportBlockHeader {
uint32_t len;
@@ -143,4 +146,69 @@ static inline bool load_next_iplb(void)
return true;
}
+static inline uint64_t diag320(void *data, unsigned long subcode)
+{
+ register unsigned long addr asm("0") = (unsigned long)data;
+ register unsigned long rc asm("1") = 0;
+
+ asm volatile ("diag %0,%2,0x320\n"
+ : "+d" (addr), "+d" (rc)
+ : "d" (subcode)
+ : "memory", "cc");
+ return rc;
+}
+
+static inline uint64_t get_320_subcodes(uint64_t *ism)
+{
+ return diag320(ism, DIAG_320_SUBC_QUERY_ISM);
+}
+
+static inline bool is_cert_store_facility_supported(void)
+{
+ uint64_t d320_ism;
+ get_320_subcodes(&d320_ism);
+ return (d320_ism & DIAG_320_ISM_QUERY_VCSI) &&
+ (d320_ism & DIAG_320_ISM_STORE_VC);
+}
+
+static inline uint64_t _diag508(void *data, unsigned long subcode)
+{
+ register unsigned long addr asm("0") = (unsigned long)data;
+ register unsigned long rc asm("1") = 0;
+
+ asm volatile ("diag %0,%2,0x508\n"
+ : "+d" (addr), "+d" (rc)
+ : "d" (subcode)
+ : "memory", "cc");
+ return rc;
+}
+
+static inline uint64_t get_508_subcodes(void)
+{
+ return _diag508(NULL, DIAG_508_SUBC_QUERY_SUBC);
+}
+
+static inline bool is_secure_ipl_extension_supported(void)
+{
+ uint64_t d508_subcodes;
+
+ d508_subcodes = get_508_subcodes();
+ return d508_subcodes & DIAG_508_SUBC_SIG_VERIF;
+}
+
+static inline bool verify_signature(uint64_t comp_len, uint64_t comp_addr,
+ uint64_t sig_len, uint64_t sig_addr,
+ uint64_t *cert_len, uint8_t *cert_idx)
+{
+ Diag508SignatureVerificationBlock svb = {{}, comp_len, comp_addr,
+ sig_len, sig_addr };
+
+ if (_diag508(&svb, DIAG_508_SUBC_SIG_VERIF) == DIAG_508_RC_OK) {
+ *cert_len = svb.csi.len;
+ *cert_idx = svb.csi.idx;
+ return true;
+ }
+ return false;
+}
+
#endif /* IPLB_H */
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index c9328f1c51..92004a6f82 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -28,6 +28,7 @@ IplParameterBlock *iplb;
bool have_iplb;
static uint16_t cutype;
LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
+int boot_mode;
#define LOADPARM_PROMPT "PROMPT "
#define LOADPARM_EMPTY " "
@@ -272,9 +273,17 @@ static int virtio_setup(void)
static void ipl_boot_device(void)
{
+ if (boot_mode == 0) {
+ boot_mode = zipl_mode();
+ }
+
switch (cutype) {
case CU_TYPE_DASD_3990:
case CU_TYPE_DASD_2107:
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ panic("Passthrough (vfio) device does not support secure boot!");
+ }
+
dasd_ipl(blk_schid, cutype);
break;
case CU_TYPE_VIRTIO:
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 6cdce3e5e5..68ffbf7bc8 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -39,6 +39,9 @@ typedef unsigned long long u64;
#define MIN_NON_ZERO(a, b) ((a) == 0 ? (b) : \
((b) == 0 ? (a) : (MIN(a, b))))
#endif
+#ifndef ROUND_UP
+#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
+#endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -64,6 +67,8 @@ void sclp_print(const char *string);
void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
void sclp_setup(void);
void sclp_get_loadparm_ascii(char *loadparm);
+bool sclp_is_diag320_on(void);
+bool sclp_is_sipl_on(void);
int sclp_read(char *str, size_t count);
/* virtio.c */
@@ -76,6 +81,11 @@ int virtio_read(unsigned long sector, void *load_addr);
/* bootmap.c */
void zipl_load(void);
+#define ZIPL_NORMAL_MODE 1
+#define ZIPL_SECURE_AUDIT_MODE 2
+
+int zipl_mode(void);
+
/* jump2ipl.c */
void write_reset_psw(uint64_t psw);
int jump_to_IPL_code(uint64_t address);
diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
index 4a07de018d..fd25c83387 100644
--- a/pc-bios/s390-ccw/sclp.c
+++ b/pc-bios/s390-ccw/sclp.c
@@ -113,6 +113,49 @@ void sclp_get_loadparm_ascii(char *loadparm)
}
}
+static void sclp_get_fac134(uint8_t *fac134)
+{
+
+ ReadInfo *sccb = (void *)_sccb;
+
+ memset((char *)_sccb, 0, sizeof(ReadInfo));
+ sccb->h.length = SCCB_SIZE;
+ if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
+ *fac134 = sccb->fac134;
+ }
+}
+
+bool sclp_is_diag320_on(void)
+{
+ uint8_t fac134 = 0;
+
+ sclp_get_fac134(&fac134);
+ return fac134 & SCCB_FAC134_DIAG320_BIT;
+}
+
+/*
+ * Get cbl (byte 136 and byte 137 of the SCLP Read Info block) for IPL device facilities.
+ */
+static void sclp_get_cbl(uint16_t *cbl)
+{
+
+ ReadInfo *sccb = (void *)_sccb;
+
+ memset((char *)_sccb, 0, sizeof(ReadInfo));
+ sccb->h.length = SCCB_SIZE;
+ if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
+ *cbl = sccb->cbl;
+ }
+}
+
+bool sclp_is_sipl_on(void)
+{
+ uint16_t cbl = 0;
+
+ sclp_get_cbl(&cbl);
+ return cbl & SCCB_CBL_SIPL_BIT;
+}
+
int sclp_read(char *str, size_t count)
{
ReadEventData *sccb = (void *)_sccb;
diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h
index 64b53cad29..de4141cb86 100644
--- a/pc-bios/s390-ccw/sclp.h
+++ b/pc-bios/s390-ccw/sclp.h
@@ -50,6 +50,8 @@ typedef struct SCCBHeader {
} __attribute__((packed)) SCCBHeader;
#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
+#define SCCB_FAC134_DIAG320_BIT 0x4
+#define SCCB_CBL_SIPL_BIT 0x4000
typedef struct ReadInfo {
SCCBHeader h;
@@ -57,6 +59,10 @@ typedef struct ReadInfo {
uint8_t rnsize;
uint8_t reserved[13];
uint8_t loadparm[LOADPARM_LEN];
+ uint8_t reserved1[102];
+ uint8_t fac134;
+ uint8_t reserved2;
+ uint16_t cbl;
} __attribute__((packed)) ReadInfo;
typedef struct SCCB {
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode
2025-04-08 15:55 ` [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode Zhuoying Cai
@ 2025-04-13 23:57 ` Jared Rossi
2025-04-17 22:39 ` Collin Walling
1 sibling, 0 replies; 54+ messages in thread
From: Jared Rossi @ 2025-04-13 23:57 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> Enable secure IPL in audit mode, which performs signature verification,
> but any error does not terminate the boot process. Only warnings will be
> logged to the console instead.
>
> Add a comp_len variable to store the length of a segment in
> zipl_load_segment. comp_len variable is necessary to store the
> calculated segment length and is used during signature verification.
> Return the length on success, or a negative return code on failure.
>
> Secure IPL in audit mode requires at least one certificate provided in
> the key store along with necessary facilities (Secure IPL Facility,
> Certificate Store Facility and secure IPL extension support).
>
> Note: Secure IPL in audit mode is implemented for the SCSI scheme of
> virtio-blk/virtio-scsi devices.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> pc-bios/s390-ccw/bootmap.c | 344 +++++++++++++++++++++++++++++++++++-
> pc-bios/s390-ccw/bootmap.h | 9 +
> pc-bios/s390-ccw/iplb.h | 68 +++++++
> pc-bios/s390-ccw/main.c | 9 +
> pc-bios/s390-ccw/s390-ccw.h | 10 ++
> pc-bios/s390-ccw/sclp.c | 43 +++++
> pc-bios/s390-ccw/sclp.h | 6 +
> 7 files changed, 486 insertions(+), 3 deletions(-)
>
> diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
> index 4fb3e99f4b..bdbd6ccd96 100644
> --- a/pc-bios/s390-ccw/bootmap.c
> +++ b/pc-bios/s390-ccw/bootmap.c
> @@ -30,6 +30,13 @@
>
> /* Scratch space */
> static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
> +/* sector for storing certificates */
> +static uint8_t certs_sec[CERT_MAX_SIZE * MAX_CERTIFICATES];
> +/* sector for storing signatures */
> +static uint8_t sig_sec[MAX_SECTOR_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
> +
> +uint8_t vcb_data[MAX_SECTOR_SIZE * 4] __attribute__((__aligned__(PAGE_SIZE)));
> +uint8_t vcssb_data[VCSSB_MAX_LEN] __attribute__((__aligned__(PAGE_SIZE)));
>
> const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
> "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
> @@ -622,6 +629,7 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> int i;
> char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
> char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
> + int comp_len = 0;
>
> blockno = entry->data.blockno;
>
> @@ -660,6 +668,9 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> */
> break;
> }
> +
> + comp_len += (uint64_t)bprs->size * ((uint64_t)bprs[i].blockct + 1);
> +
> address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
> (void *)address);
> if (!address) {
> @@ -669,6 +680,305 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> }
> } while (blockno);
>
> + return comp_len;
> +}
> +
> +int get_vcssb(VerificationCertificateStorageSizeBlock *vcssb)
> +{
> + int rc;
> +
> + /* avoid retrieving vcssb multiple times */
> + if (vcssb->length == VCSSB_MAX_LEN) {
> + return 0;
> + }
> +
> + rc = diag320(vcssb, DIAG_320_SUBC_QUERY_VCSI);
> + if (rc != DIAG_320_RC_OK) {
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static inline uint32_t request_certificate(uint64_t *cert, uint8_t index)
> +{
> + VerificationCertificateStorageSizeBlock *vcssb;
> + VerficationCertificateBlock *vcb;
> + VerificationCertificateEntry *vce;
> + uint64_t rc = 0;
> + uint32_t cert_len = 0;
> +
> + vcssb = (VerificationCertificateStorageSizeBlock *)vcssb_data;
> + vcb = (VerficationCertificateBlock *)vcb_data;
> +
> + /* Get Verification Certificate Storage Size block with DIAG320 subcode 1 */
> + if (get_vcssb(vcssb)) {
> + return 0;
> + }
> +
> + /*
> + * Request single entry
> + * Fill input fields of single-entry VCB
> + */
> + vcb->vcb_hdr.vcbinlen = ROUND_UP(vcssb->largestvcblen, PAGE_SIZE);
> + vcb->vcb_hdr.fvci = index + 1;
> + vcb->vcb_hdr.lvci = index + 1;
> +
> + rc = diag320(vcb, DIAG_320_SUBC_STORE_VC);
> + if (rc == DIAG_320_RC_OK) {
> + vce = (VerificationCertificateEntry *)vcb->vcb_buf;
> + cert_len = vce->vce_hdr.certlen;
> + memcpy(cert, (uint8_t *)vce + vce->vce_hdr.certoffset, vce->vce_hdr.certlen);
> + /* clear out region for next cert(s) */
> + memcpy(vcb_data, 0, sizeof(vcb_data));
> + }
> +
> + return cert_len;
> +}
> +
> +static int cert_table_add(uint64_t **cert_table, uint64_t **cert,
> + uint64_t cert_len, uint8_t cert_idx)
> +{
> + if (request_certificate(*cert, cert_idx)) {
> + /* save certificate address to cert_table */
> + cert_table[cert_idx] = *cert;
> + /* update cert address for the next certificate */
> + *cert += cert_len;
> + } else {
> + puts("Could not get certificate");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void cert_list_add(IplSignatureCertificateList *certs, int cert_index,
> + uint64_t *cert, uint64_t cert_len)
> +{
> + if (cert_index > MAX_CERTIFICATES - 1) {
> + printf("Warning: Ignoring cert entry [%d] because it's over 64 entires\n",
> + cert_index + 1);
> + return;
> + }
> +
> + certs->cert_entries[cert_index].addr = (uint64_t)cert;
> + certs->cert_entries[cert_index].len = cert_len;
> + certs->ipl_info_header.len += sizeof(certs->cert_entries[cert_index]);
> +}
> +
> +static void comp_list_add(IplDeviceComponentList *comps, int comp_index,
> + int cert_index, uint64_t comp_addr,
> + uint64_t comp_len, uint8_t flags)
> +{
> + if (comp_index > MAX_CERTIFICATES - 1) {
> + printf("Warning: Ignoring comp entry [%d] because it's over 64 entires\n",
> + comp_index + 1);
> + return;
> + }
> +
> + comps->device_entries[comp_index].addr = comp_addr;
> + comps->device_entries[comp_index].len = comp_len;
> + comps->device_entries[comp_index].flags = flags;
> + comps->device_entries[comp_index].cert_index = cert_index;
> + comps->ipl_info_header.len += sizeof(comps->device_entries[comp_index]);
> +}
Use MAX_CERTIFICATES in the warnings as well, rather than hard coding
the 64.
> +
> +static int update_iirb(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
> +{
> + IplInfoReportBlock *iirb;
> + IplDeviceComponentList *comp_list;
> + IplSignatureCertificateList *cert_list;
> +
> + if (iplb->len % 8 != 0) {
> + puts("IPL parameter block length field value is not multiple of 8 bytes");
> + return -1;
> + }
> +
> + /* IIRB immediately follows IPLB */
> + iirb = &ipl_data.iirb;
> + iirb->hdr.len = sizeof(IplInfoReportBlockHeader);
> +
> + /* Copy IPL device component list after IIRB Header */
> + comp_list = (IplDeviceComponentList *) iirb->info_blks;
> + memcpy(comp_list, comps, comps->ipl_info_header.len);
> +
> + /* Update IIRB length */
> + iirb->hdr.len += comps->ipl_info_header.len;
> +
> + /* Copy IPL sig cert list after IPL device component list */
> + cert_list = (IplSignatureCertificateList *) (iirb->info_blks +
> + comp_list->ipl_info_header.len);
> + memcpy(cert_list, certs, certs->ipl_info_header.len);
> +
> + /* Update IIRB length */
> + iirb->hdr.len += certs->ipl_info_header.len;
> +
> + return 0;
> +}
> +
> +static bool secure_ipl_supported(void)
> +{
> + if (!sclp_is_sipl_on()) {
> + puts("Secure IPL Facility is not supported by the hypervisor!");
> + return false;
> + }
> +
> + if (!is_secure_ipl_extension_supported()) {
> + puts("Secure IPL extensions are not supported by the hypervisor!");
> + return false;
> + }
> +
> + if (!(sclp_is_diag320_on() && is_cert_store_facility_supported())) {
> + puts("Certificate Store Facility is not supported by the hypervisor!");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void init_lists(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
> +{
> + comps->ipl_info_header.ibt = IPL_IBT_COMPONENTS;
> + comps->ipl_info_header.len = sizeof(comps->ipl_info_header);
> +
> + certs->ipl_info_header.ibt = IPL_IBT_CERTIFICATES;
> + certs->ipl_info_header.len = sizeof(certs->ipl_info_header);
> +}
> +
> +static bool check_sig_entry(ComponentEntry *entry, uint32_t *sig_len)
> +{
> + if ((entry + 1)->component_type != ZIPL_COMP_ENTRY_LOAD) {
> + puts("Next component does not contain signed binary code");
> + return false;
> + }
I think it is best to avoid accessing the n+1 index directly.
> +
> + if (zipl_load_segment(entry, (uint64_t)sig_sec) < 0) {
> + return false;
> + };
> +
> + if (entry->compdat.sig_info.format != DER_SIGNATURE_FORMAT) {
> + puts("Signature is not in DER format");
> + return false;
> + }
> +
> + *sig_len = entry->compdat.sig_info.sig_len;
> + return true;
This could be simplified with an int return value, and returning the
length on success or a negative RC on error.
> +}
> +
> +static int perform_sig_verf(uint64_t comp_addr, uint64_t comp_len, uint64_t sig_len,
> + uint64_t *cert_table[], uint64_t **cert,
> + IplDeviceComponentList *comps,
> + IplSignatureCertificateList *certs,
> + int comp_index, int cert_index,
> + void (*print_func)(bool, const char *))
> +{
> + uint64_t cert_len = -1;
> + uint8_t cert_idx = -1;
> + bool verified;
> +
> + verified = verify_signature(comp_len, comp_addr, sig_len, (uint64_t)sig_sec,
> + &cert_len, &cert_idx);
> +
> + if (verified) {
> + if (cert_table[cert_idx] == 0) {
> + if (cert_table_add(cert_table, cert, cert_len, cert_idx)) {
> + return -1;
> + }
> + }
> +
> + puts("Verified component");
> + cert_list_add(certs, cert_index, cert_table[cert_idx], cert_len);
> + comp_list_add(comps, comp_index, cert_index, comp_addr, comp_len,
> + S390_IPL_COMPONENT_FLAG_SC | S390_IPL_COMPONENT_FLAG_CSV);
> + } else {
> + comp_list_add(comps, comp_index, -1, comp_addr, comp_len,
> + S390_IPL_COMPONENT_FLAG_SC);
> + print_func(verified, "Could not verify component");
> + }
> +
> + return 0;
> +}
> +
> +static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
> +{
> + bool found_signature = false;
> + struct IplDeviceComponentList comps;
> + struct IplSignatureCertificateList certs;
> + uint64_t *cert = (uint64_t *)certs_sec;
> + int cert_index = 0;
> + int comp_index = 0;
> + int comp_len;
> + bool valid_sig;
> + uint32_t sig_len;
> + /*
> + * Store address of certificate to prevent allocating
> + * the same certificate multiple times.
> + */
> + uint64_t *cert_table[MAX_CERTIFICATES];
> +
> + void (*print_func)(bool, const char *) = NULL;
> + print_func = &IPL_check;
> +
> + if (!secure_ipl_supported()) {
> + return -1;
> + }
> +
> + init_lists(&comps, &certs);
> +
> + valid_sig = false;
> + while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
> + entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
> +
> + if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
> + valid_sig = check_sig_entry(entry, &sig_len);
> + if (!valid_sig) {
> + return -1;
> + }
> + } else {
> + comp_len = zipl_load_segment(entry, entry->compdat.load_addr);
> + if (comp_len < 0) {
> + return -1;
> + }
> +
> + if (valid_sig) {
> + perform_sig_verf(entry->compdat.load_addr, comp_len, sig_len, cert_table,
> + &cert, &comps, &certs, comp_index, cert_index,
> + print_func);
Since this function is only called here and rather short anyway, might
it be better to do it in-line and avoid needing 9 arguments?
> +
> + cert_index++;
> + found_signature = true;
> + /*
> + * complete signature verification for current component,
> + * reset variable for the next signature entry.
> + */
> + valid_sig = false;
> + }
> +
> + comp_index++;
> + }
> +
> + entry++;
> +
> + if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
> + puts("Wrong entry value");
> + return -EINVAL;
> + }
Is it intended that this check happens after incrementing the entry?
> + }
> +
> + if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
> + puts("No EXEC entry");
> + return -EINVAL;
> + }
> +
> + if (!found_signature) {
> + print_func(found_signature, "Secure boot is on, but components are not signed");
> + }
> +
> + if (update_iirb(&comps, &certs)) {
> + print_func(false, "Failed to write IPL Information Report Block");
> + }
> + write_reset_psw(entry->compdat.load_psw);
> +
> return 0;
> }
>
> @@ -683,7 +993,7 @@ static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
> continue;
> }
>
> - if (zipl_load_segment(entry, entry->compdat.load_addr)) {
> + if (zipl_load_segment(entry, entry->compdat.load_addr) < 0) {
> return -1;
> }
>
> @@ -731,8 +1041,17 @@ static int zipl_run(ScsiBlockPtr *pte)
> /* Load image(s) into RAM */
> entry = (ComponentEntry *)(&header[1]);
>
> - if (zipl_run_normal(entry, tmp_sec)) {
> - return -1;
> + switch (boot_mode) {
> + case ZIPL_SECURE_AUDIT_MODE:
> + if (zipl_run_secure(entry, tmp_sec)) {
> + return -1;
> + }
> + break;
> + case ZIPL_NORMAL_MODE:
> + if (zipl_run_normal(entry, tmp_sec)) {
> + return -1;
> + }
> + break;
> }
>
> /* should not return */
> @@ -1091,17 +1410,32 @@ static int zipl_load_vscsi(void)
> * IPL starts here
> */
>
> +int zipl_mode(void)
> +{
> + uint32_t cert_len;
> +
> + cert_len = request_certificate((uint64_t *)certs_sec, 0);
> +
> + return (cert_len > 0) ? ZIPL_SECURE_AUDIT_MODE : ZIPL_NORMAL_MODE;
> +}
> +
> void zipl_load(void)
> {
> VDev *vdev = virtio_get_device();
>
> if (vdev->is_cdrom) {
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Secure boot from ISO image is not supported!");
> + }
> ipl_iso_el_torito();
> puts("Failed to IPL this ISO image!");
> return;
> }
>
> if (virtio_get_device_type() == VIRTIO_ID_NET) {
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Virtio net boot device does not support secure boot!");
> + }
> netmain();
> puts("Failed to IPL from this network!");
> return;
> @@ -1112,6 +1446,10 @@ void zipl_load(void)
> return;
> }
>
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("ECKD boot device does not support secure boot!");
> + }
> +
> switch (virtio_get_device_type()) {
> case VIRTIO_ID_BLOCK:
> zipl_load_vblk();
> diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
> index 95943441d3..e48823a835 100644
> --- a/pc-bios/s390-ccw/bootmap.h
> +++ b/pc-bios/s390-ccw/bootmap.h
> @@ -88,9 +88,18 @@ typedef struct BootMapTable {
> BootMapPointer entry[];
> } __attribute__ ((packed)) BootMapTable;
>
> +#define DER_SIGNATURE_FORMAT 1
> +
> +typedef struct SignatureInformation {
> + uint8_t format;
> + uint8_t reserved[3];
> + uint32_t sig_len;
> +} __attribute__((packed)) SignatureInformation;
> +
> typedef union ComponentEntryData {
> uint64_t load_psw;
> uint64_t load_addr;
> + SignatureInformation sig_info;
> } ComponentEntryData;
>
> typedef struct ComponentEntry {
> diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
> index 11302e004d..8d9fdde30a 100644
> --- a/pc-bios/s390-ccw/iplb.h
> +++ b/pc-bios/s390-ccw/iplb.h
> @@ -16,12 +16,15 @@
> #define QEMU_PACKED __attribute__((packed))
> #endif
>
> +#include <diag320.h>
> +#include <diag508.h>
> #include <qipl.h>
> #include <string.h>
>
> extern QemuIplParameters qipl;
> extern IplParameterBlock *iplb;
> extern bool have_iplb;
> +extern int boot_mode;
>
> struct IplInfoReportBlockHeader {
> uint32_t len;
> @@ -143,4 +146,69 @@ static inline bool load_next_iplb(void)
> return true;
> }
>
> +static inline uint64_t diag320(void *data, unsigned long subcode)
> +{
> + register unsigned long addr asm("0") = (unsigned long)data;
> + register unsigned long rc asm("1") = 0;
> +
> + asm volatile ("diag %0,%2,0x320\n"
> + : "+d" (addr), "+d" (rc)
> + : "d" (subcode)
> + : "memory", "cc");
> + return rc;
> +}
> +
> +static inline uint64_t get_320_subcodes(uint64_t *ism)
> +{
> + return diag320(ism, DIAG_320_SUBC_QUERY_ISM);
> +}
> +
> +static inline bool is_cert_store_facility_supported(void)
> +{
> + uint64_t d320_ism;
> + get_320_subcodes(&d320_ism);
> + return (d320_ism & DIAG_320_ISM_QUERY_VCSI) &&
> + (d320_ism & DIAG_320_ISM_STORE_VC);
> +}
> +
> +static inline uint64_t _diag508(void *data, unsigned long subcode)
> +{
> + register unsigned long addr asm("0") = (unsigned long)data;
> + register unsigned long rc asm("1") = 0;
> +
> + asm volatile ("diag %0,%2,0x508\n"
> + : "+d" (addr), "+d" (rc)
> + : "d" (subcode)
> + : "memory", "cc");
> + return rc;
> +}
> +
> +static inline uint64_t get_508_subcodes(void)
> +{
> + return _diag508(NULL, DIAG_508_SUBC_QUERY_SUBC);
> +}
> +
> +static inline bool is_secure_ipl_extension_supported(void)
> +{
> + uint64_t d508_subcodes;
> +
> + d508_subcodes = get_508_subcodes();
> + return d508_subcodes & DIAG_508_SUBC_SIG_VERIF;
> +}
> +
> +static inline bool verify_signature(uint64_t comp_len, uint64_t comp_addr,
> + uint64_t sig_len, uint64_t sig_addr,
> + uint64_t *cert_len, uint8_t *cert_idx)
> +{
> + Diag508SignatureVerificationBlock svb = {{}, comp_len, comp_addr,
> + sig_len, sig_addr };
> +
> + if (_diag508(&svb, DIAG_508_SUBC_SIG_VERIF) == DIAG_508_RC_OK) {
> + *cert_len = svb.csi.len;
> + *cert_idx = svb.csi.idx;
> + return true;
> + }
> + return false;
> +}
> +
> #endif /* IPLB_H */
> diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
> index c9328f1c51..92004a6f82 100644
> --- a/pc-bios/s390-ccw/main.c
> +++ b/pc-bios/s390-ccw/main.c
> @@ -28,6 +28,7 @@ IplParameterBlock *iplb;
> bool have_iplb;
> static uint16_t cutype;
> LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
> +int boot_mode;
>
> #define LOADPARM_PROMPT "PROMPT "
> #define LOADPARM_EMPTY " "
> @@ -272,9 +273,17 @@ static int virtio_setup(void)
>
> static void ipl_boot_device(void)
> {
> + if (boot_mode == 0) {
> + boot_mode = zipl_mode();
> + }
> +
> switch (cutype) {
> case CU_TYPE_DASD_3990:
> case CU_TYPE_DASD_2107:
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Passthrough (vfio) device does not support secure boot!");
> + }
> +
> dasd_ipl(blk_schid, cutype);
> break;
> case CU_TYPE_VIRTIO:
> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
> index 6cdce3e5e5..68ffbf7bc8 100644
> --- a/pc-bios/s390-ccw/s390-ccw.h
> +++ b/pc-bios/s390-ccw/s390-ccw.h
> @@ -39,6 +39,9 @@ typedef unsigned long long u64;
> #define MIN_NON_ZERO(a, b) ((a) == 0 ? (b) : \
> ((b) == 0 ? (a) : (MIN(a, b))))
> #endif
> +#ifndef ROUND_UP
> +#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
> +#endif
>
> #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
>
> @@ -64,6 +67,8 @@ void sclp_print(const char *string);
> void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
> void sclp_setup(void);
> void sclp_get_loadparm_ascii(char *loadparm);
> +bool sclp_is_diag320_on(void);
> +bool sclp_is_sipl_on(void);
> int sclp_read(char *str, size_t count);
>
> /* virtio.c */
> @@ -76,6 +81,11 @@ int virtio_read(unsigned long sector, void *load_addr);
> /* bootmap.c */
> void zipl_load(void);
>
> +#define ZIPL_NORMAL_MODE 1
> +#define ZIPL_SECURE_AUDIT_MODE 2
> +
> +int zipl_mode(void);
> +
> /* jump2ipl.c */
> void write_reset_psw(uint64_t psw);
> int jump_to_IPL_code(uint64_t address);
> diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
> index 4a07de018d..fd25c83387 100644
> --- a/pc-bios/s390-ccw/sclp.c
> +++ b/pc-bios/s390-ccw/sclp.c
> @@ -113,6 +113,49 @@ void sclp_get_loadparm_ascii(char *loadparm)
> }
> }
>
> +static void sclp_get_fac134(uint8_t *fac134)
> +{
> +
> + ReadInfo *sccb = (void *)_sccb;
> +
> + memset((char *)_sccb, 0, sizeof(ReadInfo));
> + sccb->h.length = SCCB_SIZE;
> + if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
> + *fac134 = sccb->fac134;
> + }
> +}
> +
> +bool sclp_is_diag320_on(void)
> +{
> + uint8_t fac134 = 0;
> +
> + sclp_get_fac134(&fac134);
> + return fac134 & SCCB_FAC134_DIAG320_BIT;
> +}
> +
> +/*
> + * Get cbl (byte 136 and byte 137 of the SCLP Read Info block) for IPL device facilities.
> + */
> +static void sclp_get_cbl(uint16_t *cbl)
> +{
> +
> + ReadInfo *sccb = (void *)_sccb;
> +
> + memset((char *)_sccb, 0, sizeof(ReadInfo));
> + sccb->h.length = SCCB_SIZE;
> + if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
> + *cbl = sccb->cbl;
> + }
> +}
> +
> +bool sclp_is_sipl_on(void)
> +{
> + uint16_t cbl = 0;
> +
> + sclp_get_cbl(&cbl);
> + return cbl & SCCB_CBL_SIPL_BIT;
> +}
> +
> int sclp_read(char *str, size_t count)
> {
> ReadEventData *sccb = (void *)_sccb;
> diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h
> index 64b53cad29..de4141cb86 100644
> --- a/pc-bios/s390-ccw/sclp.h
> +++ b/pc-bios/s390-ccw/sclp.h
> @@ -50,6 +50,8 @@ typedef struct SCCBHeader {
> } __attribute__((packed)) SCCBHeader;
>
> #define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
> +#define SCCB_FAC134_DIAG320_BIT 0x4
> +#define SCCB_CBL_SIPL_BIT 0x4000
>
> typedef struct ReadInfo {
> SCCBHeader h;
> @@ -57,6 +59,10 @@ typedef struct ReadInfo {
> uint8_t rnsize;
> uint8_t reserved[13];
> uint8_t loadparm[LOADPARM_LEN];
> + uint8_t reserved1[102];
> + uint8_t fac134;
> + uint8_t reserved2;
> + uint16_t cbl;
> } __attribute__((packed)) ReadInfo;
>
> typedef struct SCCB {
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode
2025-04-08 15:55 ` [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode Zhuoying Cai
2025-04-13 23:57 ` Jared Rossi
@ 2025-04-17 22:39 ` Collin Walling
1 sibling, 0 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-17 22:39 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> Enable secure IPL in audit mode, which performs signature verification,
> but any error does not terminate the boot process. Only warnings will be
> logged to the console instead.
>
> Add a comp_len variable to store the length of a segment in
> zipl_load_segment. comp_len variable is necessary to store the
> calculated segment length and is used during signature verification.
> Return the length on success, or a negative return code on failure.
>
> Secure IPL in audit mode requires at least one certificate provided in
> the key store along with necessary facilities (Secure IPL Facility,
> Certificate Store Facility and secure IPL extension support).
>
> Note: Secure IPL in audit mode is implemented for the SCSI scheme of
> virtio-blk/virtio-scsi devices.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
By the end of this patch series, the secure IPL related functions end up
comprising about 1/3 of the code in bootmap.c Pretty neat :) I think
that provides a good enough reason to house it all under its own roof,
if possible. Maybe a pc-bios/s390-ccw/secure-ipl.c|h
It might make sense to put the diag320 & 508 code in there as well.
Please keep functions that relate to a specific facility grouped and
aptly named.
I ..think.. zipl_run_secure could go in there as well, not sure atm.
The print_func could be global to the file so you won't need to pass it
as a parameter to various functions.
> ---
> pc-bios/s390-ccw/bootmap.c | 344 +++++++++++++++++++++++++++++++++++-
> pc-bios/s390-ccw/bootmap.h | 9 +
> pc-bios/s390-ccw/iplb.h | 68 +++++++
> pc-bios/s390-ccw/main.c | 9 +
> pc-bios/s390-ccw/s390-ccw.h | 10 ++
> pc-bios/s390-ccw/sclp.c | 43 +++++
> pc-bios/s390-ccw/sclp.h | 6 +
> 7 files changed, 486 insertions(+), 3 deletions(-)
>
> diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
> index 4fb3e99f4b..bdbd6ccd96 100644
> --- a/pc-bios/s390-ccw/bootmap.c
> +++ b/pc-bios/s390-ccw/bootmap.c
> @@ -30,6 +30,13 @@
>
> /* Scratch space */
> static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
> +/* sector for storing certificates */
> +static uint8_t certs_sec[CERT_MAX_SIZE * MAX_CERTIFICATES];
> +/* sector for storing signatures */
> +static uint8_t sig_sec[MAX_SECTOR_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
> +
> +uint8_t vcb_data[MAX_SECTOR_SIZE * 4] __attribute__((__aligned__(PAGE_SIZE)));
> +uint8_t vcssb_data[VCSSB_MAX_LEN] __attribute__((__aligned__(PAGE_SIZE)));
>
> const uint8_t el_torito_magic[] = "EL TORITO SPECIFICATION"
> "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
> @@ -622,6 +629,7 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> int i;
> char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
> char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
> + int comp_len = 0;
>
> blockno = entry->data.blockno;
>
> @@ -660,6 +668,9 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> */
> break;
> }
> +
> + comp_len += (uint64_t)bprs->size * ((uint64_t)bprs[i].blockct + 1);
> +
> address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
> (void *)address);
> if (!address) {
> @@ -669,6 +680,305 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
> }
> } while (blockno);
>
> + return comp_len;
This changes the return behavior of the function without really
explaining it. You refactor it in a previous patch. I would suggest
making the comp_len change there and explain its significance in the
commit message.
> +}
> +
> +int get_vcssb(VerificationCertificateStorageSizeBlock *vcssb)
> +{
> + int rc;
> +
> + /* avoid retrieving vcssb multiple times */
> + if (vcssb->length == VCSSB_MAX_LEN) {
> + return 0;
> + }
> +
> + rc = diag320(vcssb, DIAG_320_SUBC_QUERY_VCSI);
> + if (rc != DIAG_320_RC_OK) {
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static inline uint32_t request_certificate(uint64_t *cert, uint8_t index)
> +{
> + VerificationCertificateStorageSizeBlock *vcssb;
> + VerficationCertificateBlock *vcb;
> + VerificationCertificateEntry *vce;
> + uint64_t rc = 0;
> + uint32_t cert_len = 0;
> +
> + vcssb = (VerificationCertificateStorageSizeBlock *)vcssb_data;
> + vcb = (VerficationCertificateBlock *)vcb_data;
> +
> + /* Get Verification Certificate Storage Size block with DIAG320 subcode 1 */
> + if (get_vcssb(vcssb)) {
> + return 0;
> + }
> +
> + /*
> + * Request single entry
> + * Fill input fields of single-entry VCB
> + */
> + vcb->vcb_hdr.vcbinlen = ROUND_UP(vcssb->largestvcblen, PAGE_SIZE);
> + vcb->vcb_hdr.fvci = index + 1;
> + vcb->vcb_hdr.lvci = index + 1;
> +
> + rc = diag320(vcb, DIAG_320_SUBC_STORE_VC);
> + if (rc == DIAG_320_RC_OK) {
> + vce = (VerificationCertificateEntry *)vcb->vcb_buf;
> + cert_len = vce->vce_hdr.certlen;
> + memcpy(cert, (uint8_t *)vce + vce->vce_hdr.certoffset, vce->vce_hdr.certlen);
> + /* clear out region for next cert(s) */
> + memcpy(vcb_data, 0, sizeof(vcb_data));
> + }
> +
> + return cert_len;
> +}
> +
> +static int cert_table_add(uint64_t **cert_table, uint64_t **cert,
> + uint64_t cert_len, uint8_t cert_idx)
> +{
> + if (request_certificate(*cert, cert_idx)) {
> + /* save certificate address to cert_table */
> + cert_table[cert_idx] = *cert;
> + /* update cert address for the next certificate */
> + *cert += cert_len;
> + } else {
> + puts("Could not get certificate");
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static void cert_list_add(IplSignatureCertificateList *certs, int cert_index,
> + uint64_t *cert, uint64_t cert_len)
> +{
> + if (cert_index > MAX_CERTIFICATES - 1) {
> + printf("Warning: Ignoring cert entry [%d] because it's over 64 entires\n",
> + cert_index + 1);
> + return;
> + }
> +
> + certs->cert_entries[cert_index].addr = (uint64_t)cert;
> + certs->cert_entries[cert_index].len = cert_len;
> + certs->ipl_info_header.len += sizeof(certs->cert_entries[cert_index]);
> +}
> +
> +static void comp_list_add(IplDeviceComponentList *comps, int comp_index,
> + int cert_index, uint64_t comp_addr,
> + uint64_t comp_len, uint8_t flags)
> +{
> + if (comp_index > MAX_CERTIFICATES - 1) {
> + printf("Warning: Ignoring comp entry [%d] because it's over 64 entires\n",
> + comp_index + 1);
> + return;
> + }
> +
> + comps->device_entries[comp_index].addr = comp_addr;
> + comps->device_entries[comp_index].len = comp_len;
> + comps->device_entries[comp_index].flags = flags;
> + comps->device_entries[comp_index].cert_index = cert_index;
> + comps->ipl_info_header.len += sizeof(comps->device_entries[comp_index]);
> +}
> +
> +static int update_iirb(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
> +{
> + IplInfoReportBlock *iirb;
> + IplDeviceComponentList *comp_list;
> + IplSignatureCertificateList *cert_list;
> +
> + if (iplb->len % 8 != 0) {
> + puts("IPL parameter block length field value is not multiple of 8 bytes");
> + return -1;
> + }
Shouldn't this have a greater consequence than return -1 and then the
caller assert/warn? Also, you should add a check somewhere to ensure
there is enough space to hold all components and certs in the IIRB.
> +
> + /* IIRB immediately follows IPLB */
> + iirb = &ipl_data.iirb;
> + iirb->hdr.len = sizeof(IplInfoReportBlockHeader);
> +
> + /* Copy IPL device component list after IIRB Header */
> + comp_list = (IplDeviceComponentList *) iirb->info_blks;
> + memcpy(comp_list, comps, comps->ipl_info_header.len);
I think this would read better as iirb_comps
> +
> + /* Update IIRB length */
> + iirb->hdr.len += comps->ipl_info_header.len;
> +
> + /* Copy IPL sig cert list after IPL device component list */
> + cert_list = (IplSignatureCertificateList *) (iirb->info_blks +
> + comp_list->ipl_info_header.len);> + memcpy(cert_list, certs, certs->ipl_info_header.len);
Same as above: iirb_certs
> +
> + /* Update IIRB length */
> + iirb->hdr.len += certs->ipl_info_header.len;
> +
> + return 0;
> +}
> +
> +static bool secure_ipl_supported(void)
> +{
> + if (!sclp_is_sipl_on()) {
> + puts("Secure IPL Facility is not supported by the hypervisor!");
> + return false;
> + }
> +
> + if (!is_secure_ipl_extension_supported()) {
> + puts("Secure IPL extensions are not supported by the hypervisor!");
> + return false;
> + }
> +
> + if (!(sclp_is_diag320_on() && is_cert_store_facility_supported())) {
> + puts("Certificate Store Facility is not supported by the hypervisor!");
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void init_lists(IplDeviceComponentList *comps, IplSignatureCertificateList *certs)
> +{
> + comps->ipl_info_header.ibt = IPL_IBT_COMPONENTS;
> + comps->ipl_info_header.len = sizeof(comps->ipl_info_header);
> +
> + certs->ipl_info_header.ibt = IPL_IBT_CERTIFICATES;
> + certs->ipl_info_header.len = sizeof(certs->ipl_info_header);
> +}
> +
> +static bool check_sig_entry(ComponentEntry *entry, uint32_t *sig_len)
> +{
> + if ((entry + 1)->component_type != ZIPL_COMP_ENTRY_LOAD) {
> + puts("Next component does not contain signed binary code");
> + return false;
> + }
> +
> + if (zipl_load_segment(entry, (uint64_t)sig_sec) < 0) {
> + return false;
> + };
> +
> + if (entry->compdat.sig_info.format != DER_SIGNATURE_FORMAT) {
> + puts("Signature is not in DER format");
> + return false;
> + }
> +
> + *sig_len = entry->compdat.sig_info.sig_len;
> + return true;
> +}
> +
> +static int perform_sig_verf(uint64_t comp_addr, uint64_t comp_len, uint64_t sig_len,
/s/verf/verif
> + uint64_t *cert_table[], uint64_t **cert,
> + IplDeviceComponentList *comps,
> + IplSignatureCertificateList *certs,
> + int comp_index, int cert_index,
> + void (*print_func)(bool, const char *))
> +{
> + uint64_t cert_len = -1;
> + uint8_t cert_idx = -1;
> + bool verified;
> +
> + verified = verify_signature(comp_len, comp_addr, sig_len, (uint64_t)sig_sec,
> + &cert_len, &cert_idx);
> +
> + if (verified) {
> + if (cert_table[cert_idx] == 0) {
> + if (cert_table_add(cert_table, cert, cert_len, cert_idx)) {
> + return -1;
> + }
> + }
> +
> + puts("Verified component");
> + cert_list_add(certs, cert_index, cert_table[cert_idx], cert_len);
> + comp_list_add(comps, comp_index, cert_index, comp_addr, comp_len,
> + S390_IPL_COMPONENT_FLAG_SC | S390_IPL_COMPONENT_FLAG_CSV);
> + } else {
> + comp_list_add(comps, comp_index, -1, comp_addr, comp_len,
> + S390_IPL_COMPONENT_FLAG_SC);
> + print_func(verified, "Could not verify component");
> + }
> +
> + return 0;
> +}
> +
> +static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
> +{
> + bool found_signature = false;
> + struct IplDeviceComponentList comps;
> + struct IplSignatureCertificateList certs;
> + uint64_t *cert = (uint64_t *)certs_sec;
> + int cert_index = 0;
> + int comp_index = 0;
> + int comp_len;
> + bool valid_sig;
> + uint32_t sig_len;
> + /*
> + * Store address of certificate to prevent allocating
> + * the same certificate multiple times.
> + */
> + uint64_t *cert_table[MAX_CERTIFICATES];
I'm not so sure if we necessarily need to store so much of the cert info
in a separate table in order to avoid duplication within the IIRB's cert
list.
Each component in the comp_list has a field indicating which cert in the
cert_list was used to verify it. If the cert already exists in the
list, then we don't need to add it (in the case where a single cert was
used to verify multiple components).
Since the certs are index-based in the cert store, you could just keep a
table of which indices have already been added to the cert_list. Then,
after diag508 returns the index of the cert used to verify,
cert_list_add just checks the index against the table. If the cert was
already added, skip! Otherwise, retrieve it and add it in. Then store
the comp with the cert idx as you do.
> +
> + void (*print_func)(bool, const char *) = NULL;
> + print_func = &IPL_check;
> +
> + if (!secure_ipl_supported()) {
> + return -1;
> + }
> +
> + init_lists(&comps, &certs);
> +
> + valid_sig = false;
> + while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
> + entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
> +
> + if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
> + valid_sig = check_sig_entry(entry, &sig_len);
> + if (!valid_sig) {
> + return -1;
> + }
> + } else {
> + comp_len = zipl_load_segment(entry, entry->compdat.load_addr);
> + if (comp_len < 0) {
> + return -1;
> + }
> +
> + if (valid_sig) {
> + perform_sig_verf(entry->compdat.load_addr, comp_len, sig_len, cert_table,
> + &cert, &comps, &certs, comp_index, cert_index,
> + print_func);
> +
> + cert_index++;
> + found_signature = true;
> + /*
> + * complete signature verification for current component,
> + * reset variable for the next signature entry.
> + */
> + valid_sig = false;
> + }
> +
> + comp_index++;
> + }
> +
> + entry++;
> +
> + if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
> + puts("Wrong entry value");
> + return -EINVAL;
> + }
> + }
> +
> + if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
> + puts("No EXEC entry");
> + return -EINVAL;
> + }
> +
> + if (!found_signature) {
> + print_func(found_signature, "Secure boot is on, but components are not signed");
> + }
> +
> + if (update_iirb(&comps, &certs)) {
> + print_func(false, "Failed to write IPL Information Report Block");
> + }
> + write_reset_psw(entry->compdat.load_psw);
> +
> return 0;
> }
>
> @@ -683,7 +993,7 @@ static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
> continue;
> }
>
> - if (zipl_load_segment(entry, entry->compdat.load_addr)) {
> + if (zipl_load_segment(entry, entry->compdat.load_addr) < 0) {
> return -1;
> }
>
> @@ -731,8 +1041,17 @@ static int zipl_run(ScsiBlockPtr *pte)
> /* Load image(s) into RAM */
> entry = (ComponentEntry *)(&header[1]);
>
> - if (zipl_run_normal(entry, tmp_sec)) {
> - return -1;
> + switch (boot_mode) {
> + case ZIPL_SECURE_AUDIT_MODE:
> + if (zipl_run_secure(entry, tmp_sec)) {
> + return -1;
> + }
> + break;
> + case ZIPL_NORMAL_MODE:
> + if (zipl_run_normal(entry, tmp_sec)) {
> + return -1;
> + }
> + break;
> }
>
> /* should not return */
> @@ -1091,17 +1410,32 @@ static int zipl_load_vscsi(void)
> * IPL starts here
> */
>
> +int zipl_mode(void)
> +{
> + uint32_t cert_len;
> +
> + cert_len = request_certificate((uint64_t *)certs_sec, 0);
> +
> + return (cert_len > 0) ? ZIPL_SECURE_AUDIT_MODE : ZIPL_NORMAL_MODE;
> +}
> +
I think it will suffice to just check the iplb flags, as you do when you
enable secure mode. IIUC, if only DIAG308_IPIB_FLAGS_IPLIR is set,
we're in audit mode. If DIAG308_IPIB_FLAGS_SIPL is set as well, we're
in secure mode. If neither are set, we're in "normal" mode.
If certs are missing by the time we get to signature-verification, then
that case should be handled accordingly at that time.
Hint: DIAG 508 has a return code for no certs found ;)
(though, DIAG 320 sc 1 has better inspection of the cert store -- handle
it however it make sense)
> void zipl_load(void)
> {
> VDev *vdev = virtio_get_device();
>
> if (vdev->is_cdrom) {
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Secure boot from ISO image is not supported!");
> + }
> ipl_iso_el_torito();
> puts("Failed to IPL this ISO image!");
> return;
> }
>
> if (virtio_get_device_type() == VIRTIO_ID_NET) {
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Virtio net boot device does not support secure boot!");
> + }
> netmain();
> puts("Failed to IPL from this network!");
> return;
> @@ -1112,6 +1446,10 @@ void zipl_load(void)
> return;
> }
>
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("ECKD boot device does not support secure boot!");
> + }
> +
> switch (virtio_get_device_type()) {
> case VIRTIO_ID_BLOCK:
> zipl_load_vblk();
> diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
> index 95943441d3..e48823a835 100644
> --- a/pc-bios/s390-ccw/bootmap.h
> +++ b/pc-bios/s390-ccw/bootmap.h
> @@ -88,9 +88,18 @@ typedef struct BootMapTable {
> BootMapPointer entry[];
> } __attribute__ ((packed)) BootMapTable;
>
> +#define DER_SIGNATURE_FORMAT 1
> +
> +typedef struct SignatureInformation {
> + uint8_t format;
> + uint8_t reserved[3];
> + uint32_t sig_len;
> +} __attribute__((packed)) SignatureInformation;
> +
> typedef union ComponentEntryData {
> uint64_t load_psw;
> uint64_t load_addr;
> + SignatureInformation sig_info;
> } ComponentEntryData;
>
> typedef struct ComponentEntry {
> diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
> index 11302e004d..8d9fdde30a 100644
> --- a/pc-bios/s390-ccw/iplb.h
> +++ b/pc-bios/s390-ccw/iplb.h
> @@ -16,12 +16,15 @@
> #define QEMU_PACKED __attribute__((packed))
> #endif
>
> +#include <diag320.h>
> +#include <diag508.h>
> #include <qipl.h>
> #include <string.h>
>
> extern QemuIplParameters qipl;
> extern IplParameterBlock *iplb;
> extern bool have_iplb;
> +extern int boot_mode;
>
> struct IplInfoReportBlockHeader {
> uint32_t len;
> @@ -143,4 +146,69 @@ static inline bool load_next_iplb(void)
> return true;
> }
>
> +static inline uint64_t diag320(void *data, unsigned long subcode)
> +{
> + register unsigned long addr asm("0") = (unsigned long)data;
> + register unsigned long rc asm("1") = 0;
> +
> + asm volatile ("diag %0,%2,0x320\n"
> + : "+d" (addr), "+d" (rc)
> + : "d" (subcode)
> + : "memory", "cc");
> + return rc;
> +}
> +
> +static inline uint64_t get_320_subcodes(uint64_t *ism)
> +{
> + return diag320(ism, DIAG_320_SUBC_QUERY_ISM);
> +}
> +
> +static inline bool is_cert_store_facility_supported(void)
> +{
> + uint64_t d320_ism;
> + get_320_subcodes(&d320_ism);
> + return (d320_ism & DIAG_320_ISM_QUERY_VCSI) &&
> + (d320_ism & DIAG_320_ISM_STORE_VC);
> +}
> +
> +static inline uint64_t _diag508(void *data, unsigned long subcode)
> +{
> + register unsigned long addr asm("0") = (unsigned long)data;
> + register unsigned long rc asm("1") = 0;
> +
> + asm volatile ("diag %0,%2,0x508\n"
> + : "+d" (addr), "+d" (rc)
> + : "d" (subcode)
> + : "memory", "cc");
> + return rc;
> +}
> +
> +static inline uint64_t get_508_subcodes(void)
> +{
> + return _diag508(NULL, DIAG_508_SUBC_QUERY_SUBC);
> +}
> +
> +static inline bool is_secure_ipl_extension_supported(void)
> +{
> + uint64_t d508_subcodes;
> +
> + d508_subcodes = get_508_subcodes();
> + return d508_subcodes & DIAG_508_SUBC_SIG_VERIF;
> +}
> +
> +static inline bool verify_signature(uint64_t comp_len, uint64_t comp_addr,
> + uint64_t sig_len, uint64_t sig_addr,
> + uint64_t *cert_len, uint8_t *cert_idx)
> +{
> + Diag508SignatureVerificationBlock svb = {{}, comp_len, comp_addr,
> + sig_len, sig_addr };
> +
> + if (_diag508(&svb, DIAG_508_SUBC_SIG_VERIF) == DIAG_508_RC_OK) {
> + *cert_len = svb.csi.len;
> + *cert_idx = svb.csi.idx;
> + return true;
> + }
> + return false;
> +}
> +
> #endif /* IPLB_H */
> diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
> index c9328f1c51..92004a6f82 100644
> --- a/pc-bios/s390-ccw/main.c
> +++ b/pc-bios/s390-ccw/main.c
> @@ -28,6 +28,7 @@ IplParameterBlock *iplb;
> bool have_iplb;
> static uint16_t cutype;
> LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
> +int boot_mode;
>
> #define LOADPARM_PROMPT "PROMPT "
> #define LOADPARM_EMPTY " "
> @@ -272,9 +273,17 @@ static int virtio_setup(void)
>
> static void ipl_boot_device(void)
> {
> + if (boot_mode == 0) {
> + boot_mode = zipl_mode();
> + }
> +
> switch (cutype) {
> case CU_TYPE_DASD_3990:
> case CU_TYPE_DASD_2107:
> + if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
> + panic("Passthrough (vfio) device does not support secure boot!");
> + }
> +
> dasd_ipl(blk_schid, cutype);
> break;
> case CU_TYPE_VIRTIO:
> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
> index 6cdce3e5e5..68ffbf7bc8 100644
> --- a/pc-bios/s390-ccw/s390-ccw.h
> +++ b/pc-bios/s390-ccw/s390-ccw.h
> @@ -39,6 +39,9 @@ typedef unsigned long long u64;
> #define MIN_NON_ZERO(a, b) ((a) == 0 ? (b) : \
> ((b) == 0 ? (a) : (MIN(a, b))))
> #endif
> +#ifndef ROUND_UP
> +#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
> +#endif
>
> #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
>
> @@ -64,6 +67,8 @@ void sclp_print(const char *string);
> void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
> void sclp_setup(void);
> void sclp_get_loadparm_ascii(char *loadparm);
> +bool sclp_is_diag320_on(void);
> +bool sclp_is_sipl_on(void);
> int sclp_read(char *str, size_t count);
>
> /* virtio.c */
> @@ -76,6 +81,11 @@ int virtio_read(unsigned long sector, void *load_addr);
> /* bootmap.c */
> void zipl_load(void);
>
> +#define ZIPL_NORMAL_MODE 1
> +#define ZIPL_SECURE_AUDIT_MODE 2
> +
> +int zipl_mode(void);
I think this would look better as an enum:
enum ZiplBootMode {
ZIPL_BOOT_INVALID = -1
ZIPL_BOOT_NORMAL = 1,
ZIPL_BOOT_AUDIT = 2,
ZIPL_BOOT_SECURE = 3, (later patch)
};
or something like that.
zipl_mode should return a ZiplBootMode
> +
> /* jump2ipl.c */
> void write_reset_psw(uint64_t psw);
> int jump_to_IPL_code(uint64_t address);
> diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
> index 4a07de018d..fd25c83387 100644
> --- a/pc-bios/s390-ccw/sclp.c
> +++ b/pc-bios/s390-ccw/sclp.c
> @@ -113,6 +113,49 @@ void sclp_get_loadparm_ascii(char *loadparm)
> }
> }
>
> +static void sclp_get_fac134(uint8_t *fac134)
> +{
> +
> + ReadInfo *sccb = (void *)_sccb;
> +
> + memset((char *)_sccb, 0, sizeof(ReadInfo));
> + sccb->h.length = SCCB_SIZE;
> + if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
> + *fac134 = sccb->fac134;
> + }
> +}
> +
> +bool sclp_is_diag320_on(void)
> +{
> + uint8_t fac134 = 0;
> +
> + sclp_get_fac134(&fac134);
> + return fac134 & SCCB_FAC134_DIAG320_BIT;
> +}
> +
> +/*
> + * Get cbl (byte 136 and byte 137 of the SCLP Read Info block) for IPL device facilities.
> + */
> +static void sclp_get_cbl(uint16_t *cbl)
> +{
> +
> + ReadInfo *sccb = (void *)_sccb;
> +
> + memset((char *)_sccb, 0, sizeof(ReadInfo));
> + sccb->h.length = SCCB_SIZE;
> + if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
> + *cbl = sccb->cbl;
> + }
> +}
> +
> +bool sclp_is_sipl_on(void)
> +{
> + uint16_t cbl = 0;
> +
> + sclp_get_cbl(&cbl);
> + return cbl & SCCB_CBL_SIPL_BIT;
> +}
> +
> int sclp_read(char *str, size_t count)
> {
> ReadEventData *sccb = (void *)_sccb;
> diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h
> index 64b53cad29..de4141cb86 100644
> --- a/pc-bios/s390-ccw/sclp.h
> +++ b/pc-bios/s390-ccw/sclp.h
> @@ -50,6 +50,8 @@ typedef struct SCCBHeader {
> } __attribute__((packed)) SCCBHeader;
>
> #define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
> +#define SCCB_FAC134_DIAG320_BIT 0x4
> +#define SCCB_CBL_SIPL_BIT 0x4000
>
> typedef struct ReadInfo {
> SCCBHeader h;
> @@ -57,6 +59,10 @@ typedef struct ReadInfo {
> uint8_t rnsize;
> uint8_t reserved[13];
> uint8_t loadparm[LOADPARM_LEN];
> + uint8_t reserved1[102];
> + uint8_t fac134;
> + uint8_t reserved2;
> + uint16_t cbl;
> } __attribute__((packed)) ReadInfo;
>
> typedef struct SCCB {
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF)
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (16 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-17 4:57 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 19/24] pc-bios/s390-ccw: Add additional security checks for secure boot Zhuoying Cai
` (5 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The secure-IPL-code-loading-attributes facility (SCLAF)
provides additional security during IPL.
Availability of SCLAF is determined by byte 136 bit 3 of the
SCLP Read Info block.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
target/s390x/cpu_features.c | 1 +
target/s390x/cpu_features_def.h.inc | 1 +
target/s390x/cpu_models.c | 2 ++
target/s390x/gen-features.c | 1 +
target/s390x/kvm/kvm.c | 3 +++
5 files changed, 8 insertions(+)
diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c
index e9371569cc..ee33a4e2a6 100644
--- a/target/s390x/cpu_features.c
+++ b/target/s390x/cpu_features.c
@@ -151,6 +151,7 @@ void s390_fill_feat_block(const S390FeatBitmap features, S390FeatType type,
break;
case S390_FEAT_TYPE_SCLP_CBL:
clear_be_bit(s390_feat_def(S390_FEAT_SIPL)->bit, data);
+ clear_be_bit(s390_feat_def(S390_FEAT_SCLAF)->bit, data);
break;
default:
return;
diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
index f874b9da6f..31e4efb8dc 100644
--- a/target/s390x/cpu_features_def.h.inc
+++ b/target/s390x/cpu_features_def.h.inc
@@ -142,6 +142,7 @@ DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functio
/* Features exposed via SCLP SCCB Facilities byte 136 - 137 (bit numbers relative to byte-136) */
DEF_FEAT(SIPL, "sipl", SCLP_CBL, 1, "Seucre-IPL facility")
+DEF_FEAT(SCLAF, "sclaf", SCLP_CBL, 3, "Seucre-IPL-code-loading-attributes facility")
/* Features exposed via SCLP CPU info. */
DEF_FEAT(SIE_F2, "sief2", SCLP_CPU, 4, "SIE: interception format 2 (Virtual SIE)")
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index a83c27dcb8..73d2d6247f 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -264,6 +264,7 @@ bool s390_has_feat(S390Feat feat)
case S390_FEAT_SIE_PFMFI:
case S390_FEAT_SIE_IBS:
case S390_FEAT_SIPL:
+ case S390_FEAT_SCLAF:
case S390_FEAT_CONFIGURATION_TOPOLOGY:
return false;
break;
@@ -509,6 +510,7 @@ static void check_consistency(const S390CPUModel *model)
{ S390_FEAT_DIAG_318, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_DIAG_320, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_SIPL, S390_FEAT_EXTENDED_LENGTH_SCCB },
+ { S390_FEAT_SCLAF, S390_FEAT_EXTENDED_LENGTH_SCCB },
{ S390_FEAT_NNPA, S390_FEAT_VECTOR },
{ S390_FEAT_RDP, S390_FEAT_LOCAL_TLB_CLEARING },
{ S390_FEAT_UV_FEAT_AP, S390_FEAT_AP },
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index d973efbf72..1755501fb7 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -698,6 +698,7 @@ static uint16_t full_GEN14_GA1[] = {
S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF,
S390_FEAT_DIAG_320,
S390_FEAT_SIPL,
+ S390_FEAT_SCLAF,
};
#define full_GEN14_GA2 EmptyFeat
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index f418102b7f..1ecb67f398 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -2520,6 +2520,9 @@ bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
/* Secure-IPL facility is handled entirely within QEMU */
set_bit(S390_FEAT_SIPL, model->features);
+ /* Secure-IPL-code-loading-attributes facility is handled entirely within QEMU */
+ set_bit(S390_FEAT_SCLAF, model->features);
+
/* Test for Ultravisor features that influence secure guest behavior */
query_uv_feat_guest(model->features);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF)
2025-04-08 15:55 ` [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
@ 2025-04-17 4:57 ` Thomas Huth
0 siblings, 0 replies; 54+ messages in thread
From: Thomas Huth @ 2025-04-17 4:57 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> The secure-IPL-code-loading-attributes facility (SCLAF)
> provides additional security during IPL.
>
> Availability of SCLAF is determined by byte 136 bit 3 of the
> SCLP Read Info block.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
...
> diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc
> index f874b9da6f..31e4efb8dc 100644
> --- a/target/s390x/cpu_features_def.h.inc
> +++ b/target/s390x/cpu_features_def.h.inc
> @@ -142,6 +142,7 @@ DEF_FEAT(DIAG_320, "diag320", SCLP_FAC134, 5, "Provide Certificate Store functio
>
> /* Features exposed via SCLP SCCB Facilities byte 136 - 137 (bit numbers relative to byte-136) */
> DEF_FEAT(SIPL, "sipl", SCLP_CBL, 1, "Seucre-IPL facility")
> +DEF_FEAT(SCLAF, "sclaf", SCLP_CBL, 3, "Seucre-IPL-code-loading-attributes facility")
s/Seucre/Secure/
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 19/24] pc-bios/s390-ccw: Add additional security checks for secure boot
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (17 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line Zhuoying Cai
` (4 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
Add additional checks to ensure that components do not overlap with
signed components when loaded into memory.
Add additional checks to ensure the load addresses of unsigned components
are greater than or equal to 0x2000.
When the secure IPL code loading attributes facility (SCLAF) is installed,
all signed components must contain a secure code loading attributes block
(SCLAB).
The SCLAB provides further validation of information on where to load the
signed binary code from the load device, and where to start the execution
of the loaded OS code.
When SCLAF is installed, its content must be evaluated during secure IPL.
However, a missing SCLAB will not be reported in audit mode. The SCALB
checking will be skipped in this case.
Add IPL Information Error Indicators (IIEI) and Component Error
Indicators (CEI) for IPL Information Report Block (IIRB).
When SCLAF is installed, additional secure boot checks are performed
during zipl and store results of verification into IIRB.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 281 +++++++++++++++++++++++++++++++++++-
pc-bios/s390-ccw/iplb.h | 43 +++++-
pc-bios/s390-ccw/s390-ccw.h | 1 +
pc-bios/s390-ccw/sclp.c | 8 +
pc-bios/s390-ccw/sclp.h | 1 +
5 files changed, 331 insertions(+), 3 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index bdbd6ccd96..4bc6311802 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -683,6 +683,207 @@ static int zipl_load_segment(ComponentEntry *entry, uint64_t address)
return comp_len;
}
+typedef struct SecureIplCompAddrRange {
+ bool is_signed;
+ uint64_t start_addr;
+ uint64_t end_addr;
+} SecureIplCompAddrRange;
+
+static bool is_comp_overlap(SecureIplCompAddrRange *comp_addr_range, int addr_range_index,
+ uint64_t start_addr, uint64_t end_addr)
+{
+ /* neither a signed nor an unsigned component can overlap with a signed component */
+ for (int i = 0; i < addr_range_index; i++) {
+ if ((comp_addr_range[i].start_addr <= end_addr &&
+ start_addr <= comp_addr_range[i].end_addr) &&
+ comp_addr_range[i].is_signed) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void comp_addr_range_add(SecureIplCompAddrRange *comp_addr_range,
+ int addr_range_index, bool is_signed,
+ uint64_t start_addr, uint64_t end_addr)
+{
+ comp_addr_range[addr_range_index].is_signed = is_signed;
+ comp_addr_range[addr_range_index].start_addr = start_addr;
+ comp_addr_range[addr_range_index].end_addr = end_addr;
+}
+
+static void unsigned_addr_check(uint64_t load_addr, IplDeviceComponentList *comps,
+ int comp_index, void (*print_func)(bool, const char *))
+{
+ bool is_addr_valid;
+
+ is_addr_valid = load_addr >= 0x2000;
+ if (!is_addr_valid) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_INVALID_UNSIGNED_ADDR;
+ print_func(is_addr_valid, "Load address is less than 0x2000");
+ }
+}
+
+static void addr_overlap_check(SecureIplCompAddrRange *comp_addr_range,
+ int *addr_range_index,
+ uint64_t start_addr, uint64_t end_addr,
+ bool is_signed, void (*print_func)(bool, const char *))
+{
+ bool overlap;
+
+ overlap = is_comp_overlap(comp_addr_range, *addr_range_index,
+ start_addr, end_addr);
+ if (!overlap) {
+ comp_addr_range_add(comp_addr_range, *addr_range_index, is_signed,
+ start_addr, end_addr);
+ *addr_range_index += 1;
+ } else {
+ print_func(!overlap, "Component addresses overlap");
+ }
+}
+
+static void valid_sclab_check(SclabOriginLocator *sclab_locator,
+ IplDeviceComponentList *comps, int comp_index,
+ void (*print_func)(bool, const char *))
+{
+ bool is_magic_match;
+ bool is_len_valid;
+
+ /* identifies the presence of SCLAB */
+ is_magic_match = magic_match(sclab_locator->magic, ZIPL_MAGIC);
+ if (!is_magic_match) {
+ comps->device_entries[comp_index].cei |= S390_IPL_COMPONENT_CEI_INVALID_SCLAB;
+
+ /* a missing SCLAB will not be reported in audit mode */
+ return;
+ }
+
+ is_len_valid = sclab_locator->len >= 32;
+ if (!is_len_valid) {
+ comps->device_entries[comp_index].cei |= S390_IPL_COMPONENT_CEI_INVALID_SCLAB_LEN;
+ comps->device_entries[comp_index].cei |= S390_IPL_COMPONENT_CEI_INVALID_SCLAB;
+ print_func(is_len_valid, "Invalid SCLAB length");
+ }
+}
+
+static void sclab_format_check(SecureCodeLoadingAttributesBlock *sclab,
+ IplDeviceComponentList *comps, int comp_index,
+ void (*print_func)(bool, const char *))
+{
+ bool valid_format;
+
+ valid_format = sclab->format == 0;
+ if (!valid_format) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_INVALID_SCLAB_FORMAT;
+ }
+ print_func(valid_format, "Format-0 SCLAB is not being used");
+}
+
+static void sclab_opsw_check(SecureCodeLoadingAttributesBlock *sclab,
+ int *global_sclab_count, uint64_t *sclab_load_psw,
+ IplDeviceComponentList *comps, int comp_index,
+ void (*print_func)(bool, const char *))
+{
+ bool is_load_psw_zero;
+ bool is_ola_on;
+ bool has_one_glob_sclab;
+
+ /* OPSW is zero */
+ if (!(sclab->flags & S390_IPL_SCLAB_FLAG_OPSW)) {
+ is_load_psw_zero = sclab->load_psw == 0;
+ if (!is_load_psw_zero) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_SCLAB_LOAD_PSW_NOT_ZERO;
+ print_func(is_load_psw_zero,
+ "Load PSW is not zero when Override PSW bit is zero");
+ }
+ } else {
+ is_ola_on = sclab->flags & S390_IPL_SCLAB_FLAG_OLA;
+ if (!is_ola_on) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_SCLAB_OLA_NOT_ONE;
+ print_func(is_ola_on,
+ "Override Load Address bit is not set to one in the global SCLAB");
+ }
+
+ *global_sclab_count += 1;
+ if (*global_sclab_count == 1) {
+ *sclab_load_psw = sclab->load_psw;
+ } else {
+ has_one_glob_sclab = false;
+ comps->ipl_info_header.iiei |= S390_IPL_INFO_IIEI_MORE_GLOBAL_SCLAB;
+ print_func(has_one_glob_sclab, "More than one global SCLAB");
+ }
+ }
+}
+
+static void sclab_ola_check(SecureCodeLoadingAttributesBlock *sclab,
+ uint64_t load_addr, IplDeviceComponentList *comps,
+ int comp_index, void (*print_func)(bool, const char *))
+{
+ bool is_load_addr_zero;
+ bool is_matched;
+
+ /* OLA is zero */
+ if (!(sclab->flags & S390_IPL_SCLAB_FLAG_OLA)) {
+ is_load_addr_zero = sclab->load_addr == 0;
+ if (!is_load_addr_zero) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_SCLAB_LOAD_ADDR_NOT_ZERO;
+ print_func(is_load_addr_zero,
+ "Load Address is not zero when Override Load Address bit is zero");
+ }
+ } else {
+ is_matched = sclab->load_addr == load_addr;
+ if (!is_matched) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_UNMATCHED_SCLAB_LOAD_ADDR;
+ print_func(is_matched,
+ "Load Address does not match with component load address");
+ }
+ }
+}
+
+static bool is_psw_valid(uint64_t psw, SecureIplCompAddrRange *comp_addr_range,
+ int range_index)
+{
+ uint32_t addr = psw & 0x3FFFFFFF;
+
+ /* PSW points to the beginning of a signed binary code component */
+ for (int i = 0; i < range_index; i++) {
+ if (comp_addr_range[i].is_signed && comp_addr_range[i].start_addr == addr) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void load_psw_check(SecureIplCompAddrRange *comp_addr_range, int addr_range_index,
+ uint64_t sclab_load_psw, uint64_t load_psw,
+ IplDeviceComponentList *comps, int comp_index,
+ void (*print_func)(bool, const char *))
+{
+ bool is_valid;
+ bool is_matched;
+
+ is_valid = is_psw_valid(sclab_load_psw, comp_addr_range, addr_range_index) &&
+ is_psw_valid(load_psw, comp_addr_range, addr_range_index);
+ if (!is_valid) {
+ comps->device_entries[comp_index].cei |= S390_IPL_COMPONENT_CEI_INVALID_LOAD_PSW;
+ print_func(is_valid, "Invalid PSW");
+ }
+
+ is_matched = load_psw == sclab_load_psw;
+ if (!is_matched) {
+ comps->device_entries[comp_index].cei |=
+ S390_IPL_COMPONENT_CEI_UNMATCHED_SCLAB_LOAD_PSW;
+ print_func(is_matched, "Load PSW does not match with PSW in component");
+ }
+}
+
int get_vcssb(VerificationCertificateStorageSizeBlock *vcssb)
{
int rc;
@@ -833,6 +1034,12 @@ static bool secure_ipl_supported(void)
return false;
}
+ if (!sclp_is_sclaf_on()) {
+ puts("Secure IPL Code Loading Attributes Facility is not supported by" \
+ " the hypervisor!");
+ return false;
+ }
+
return true;
}
@@ -899,6 +1106,39 @@ static int perform_sig_verf(uint64_t comp_addr, uint64_t comp_len, uint64_t sig_
return 0;
}
+static void check_unsigned_comp(uint64_t comp_addr, struct IplDeviceComponentList *comps,
+ int comp_index, int cert_index, uint64_t comp_len,
+ void (*print_func)(bool, const char *))
+{
+ unsigned_addr_check(comp_addr, comps, comp_index, print_func);
+
+ comp_list_add(comps, comp_index, cert_index, comp_addr, comp_len, 0x00);
+}
+
+static void check_sclab(uint64_t comp_addr, struct IplDeviceComponentList *comps,
+ uint64_t comp_len, int comp_index, int *sclab_count,
+ uint64_t *sclab_load_psw, int *global_sclab_count,
+ void (*print_func)(bool, const char *))
+{
+ SclabOriginLocator *sclab_locator;
+ SecureCodeLoadingAttributesBlock *sclab;
+
+ sclab_locator = (SclabOriginLocator *)(comp_addr + comp_len - 8);
+ valid_sclab_check(sclab_locator, comps, comp_index, print_func);
+
+ if ((comps->device_entries[comp_index].cei &
+ S390_IPL_COMPONENT_CEI_INVALID_SCLAB) == 0) {
+ *sclab_count += 1;
+ sclab = (SecureCodeLoadingAttributesBlock *)(comp_addr + comp_len -
+ sclab_locator->len);
+
+ sclab_format_check(sclab, comps, comp_index, print_func);
+ sclab_opsw_check(sclab, global_sclab_count, sclab_load_psw,
+ comps, comp_index, print_func);
+ sclab_ola_check(sclab, comp_addr, comps, comp_index, print_func);
+ }
+}
+
static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
{
bool found_signature = false;
@@ -916,6 +1156,13 @@ static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
*/
uint64_t *cert_table[MAX_CERTIFICATES];
+ int sclab_count = 0;
+ int global_sclab_count = 0;
+ uint64_t sclab_load_psw = 0;
+
+ SecureIplCompAddrRange comp_addr_range[MAX_CERTIFICATES];
+ int addr_range_index = 0;
+
void (*print_func)(bool, const char *) = NULL;
print_func = &IPL_check;
@@ -939,8 +1186,19 @@ static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
if (comp_len < 0) {
return -1;
}
+ addr_overlap_check(comp_addr_range, &addr_range_index,
+ entry->compdat.load_addr,
+ entry->compdat.load_addr + comp_len, valid_sig,
+ print_func);
+
+ if (!valid_sig) {
+ check_unsigned_comp(entry->compdat.load_addr, &comps, comp_index,
+ cert_index, comp_len, print_func);
+ } else {
+ check_sclab(entry->compdat.load_addr, &comps, comp_len, comp_index,
+ &sclab_count, &sclab_load_psw, &global_sclab_count,
+ print_func);
- if (valid_sig) {
perform_sig_verf(entry->compdat.load_addr, comp_len, sig_len, cert_table,
&cert, &comps, &certs, comp_index, cert_index,
print_func);
@@ -970,8 +1228,29 @@ static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
return -EINVAL;
}
+ if (sclab_count == 0) {
+ comps.ipl_info_header.iiei |= S390_IPL_INFO_IIEI_NO_SCLAB;
+ print_func(false, "No recognizable SCLAB");
+ }
+
if (!found_signature) {
+ comps.ipl_info_header.iiei |= S390_IPL_INFO_IIEI_NO_SIGED_COMP;
print_func(found_signature, "Secure boot is on, but components are not signed");
+ } else {
+ /* Verify PSW from the final component entry with PSW from the global SCLAB. */
+ if ((comps.ipl_info_header.iiei & S390_IPL_INFO_IIEI_NO_SCLAB) == 0) {
+ if (global_sclab_count == 0) {
+ comps.ipl_info_header.iiei |= S390_IPL_INFO_IIEI_NO_GLOBAL_SCLAB;
+ print_func(false, "Global SCLAB does not exists");
+ } else if (global_sclab_count == 1 && sclab_load_psw) {
+ load_psw_check(comp_addr_range, addr_range_index,
+ sclab_load_psw, entry->compdat.load_psw,
+ &comps, comp_index, print_func);
+ } else {
+ /* Program will only reach here in audit mode */
+ puts("Multiple global SCLABs");
+ }
+ }
}
if (update_iirb(&comps, &certs)) {
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 8d9fdde30a..42a9e081fe 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -35,11 +35,17 @@ struct IplInfoReportBlockHeader {
} __attribute__ ((packed));
typedef struct IplInfoReportBlockHeader IplInfoReportBlockHeader;
+#define S390_IPL_INFO_IIEI_NO_SIGED_COMP 0x8000 /* bit 0 */
+#define S390_IPL_INFO_IIEI_NO_SCLAB 0x4000 /* bit 1 */
+#define S390_IPL_INFO_IIEI_NO_GLOBAL_SCLAB 0x2000 /* bit 2 */
+#define S390_IPL_INFO_IIEI_MORE_GLOBAL_SCLAB 0x1000 /* bit 3 */
+
struct IplInfoBlockHeader {
uint32_t len;
uint8_t ibt;
uint8_t reserved1[3];
- uint8_t reserved2[8];
+ uint16_t iiei;
+ uint8_t reserved2[6];
} __attribute__ ((packed));
typedef struct IplInfoBlockHeader IplInfoBlockHeader;
@@ -63,13 +69,25 @@ typedef struct IplSignatureCertificateList IplSignatureCertificateList;
#define S390_IPL_COMPONENT_FLAG_SC 0x80
#define S390_IPL_COMPONENT_FLAG_CSV 0x40
+#define S390_IPL_COMPONENT_CEI_INVALID_SCLAB 0x80000000 /* bit 0 */
+#define S390_IPL_COMPONENT_CEI_INVALID_SCLAB_LEN 0x40000000 /* bit 1 */
+#define S390_IPL_COMPONENT_CEI_INVALID_SCLAB_FORMAT 0x20000000 /* bit 2 */
+#define S390_IPL_COMPONENT_CEI_UNMATCHED_SCLAB_LOAD_ADDR 0x10000000 /* bit 3 */
+#define S390_IPL_COMPONENT_CEI_UNMATCHED_SCLAB_LOAD_PSW 0x8000000 /* bit 4 */
+#define S390_IPL_COMPONENT_CEI_INVALID_LOAD_PSW 0x4000000 /* bit 5 */
+#define S390_IPL_COMPONENT_CEI_SCLAB_OLA_NOT_ONE 0x1000000 /* bit 7 */
+#define S390_IPL_COMPONENT_CEI_SCLAB_LOAD_ADDR_NOT_ZERO 0x400000 /* bit 9 */
+#define S390_IPL_COMPONENT_CEI_SCLAB_LOAD_PSW_NOT_ZERO 0x200000 /* bit 10 */
+#define S390_IPL_COMPONENT_CEI_INVALID_UNSIGNED_ADDR 0x100000 /* bit 11 */
+
struct IplDeviceComponentEntry {
uint64_t addr;
uint64_t len;
uint8_t flags;
uint8_t reserved1[5];
uint16_t cert_index;
- uint8_t reserved2[8];
+ uint32_t cei;
+ uint8_t reserved2[4];
} __attribute__ ((packed));
typedef struct IplDeviceComponentEntry IplDeviceComponentEntry;
@@ -96,6 +114,27 @@ typedef struct IplBlocks IplBlocks;
extern IplBlocks ipl_data __attribute__((__aligned__(PAGE_SIZE)));
+#define S390_IPL_SCLAB_FLAG_OPSW 0x8000
+#define S390_IPL_SCLAB_FLAG_OLA 0x4000
+
+struct SecureCodeLoadingAttributesBlock {
+ uint8_t format;
+ uint8_t reserved1;
+ uint16_t flags;
+ uint8_t reserved2[4];
+ uint64_t load_psw;
+ uint64_t load_addr;
+ uint64_t reserved3[];
+} __attribute__ ((packed));
+typedef struct SecureCodeLoadingAttributesBlock SecureCodeLoadingAttributesBlock;
+
+struct SclabOriginLocator {
+ uint8_t reserved[2];
+ uint16_t len;
+ uint8_t magic[4];
+} __attribute__ ((packed));
+typedef struct SclabOriginLocator SclabOriginLocator;
+
#define S390_IPL_TYPE_FCP 0x00
#define S390_IPL_TYPE_CCW 0x02
#define S390_IPL_TYPE_QEMU_SCSI 0xff
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 68ffbf7bc8..20d6827a4c 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -69,6 +69,7 @@ void sclp_setup(void);
void sclp_get_loadparm_ascii(char *loadparm);
bool sclp_is_diag320_on(void);
bool sclp_is_sipl_on(void);
+bool sclp_is_sclaf_on(void);
int sclp_read(char *str, size_t count);
/* virtio.c */
diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
index fd25c83387..ffdcdd7b16 100644
--- a/pc-bios/s390-ccw/sclp.c
+++ b/pc-bios/s390-ccw/sclp.c
@@ -156,6 +156,14 @@ bool sclp_is_sipl_on(void)
return cbl & SCCB_CBL_SIPL_BIT;
}
+bool sclp_is_sclaf_on(void)
+{
+ uint16_t cbl = 0;
+
+ sclp_get_cbl(&cbl);
+ return cbl & SCCB_CBL_SCLAF_BIT;
+}
+
int sclp_read(char *str, size_t count)
{
ReadEventData *sccb = (void *)_sccb;
diff --git a/pc-bios/s390-ccw/sclp.h b/pc-bios/s390-ccw/sclp.h
index de4141cb86..ad34fac20d 100644
--- a/pc-bios/s390-ccw/sclp.h
+++ b/pc-bios/s390-ccw/sclp.h
@@ -52,6 +52,7 @@ typedef struct SCCBHeader {
#define SCCB_DATA_LEN (SCCB_SIZE - sizeof(SCCBHeader))
#define SCCB_FAC134_DIAG320_BIT 0x4
#define SCCB_CBL_SIPL_BIT 0x4000
+#define SCCB_CBL_SCLAF_BIT 0x1000
typedef struct ReadInfo {
SCCBHeader h;
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (18 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 19/24] pc-bios/s390-ccw: Add additional security checks for secure boot Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-11 14:50 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 21/24] hw/s390x/ipl: Set IPIB flags for secure IPL Zhuoying Cai
` (3 subsequent siblings)
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The `-secure-boot on|off` command line option is implemented
to enable secure IPL.
By default, -secure-boot is set to false if not specified in
the command line.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
qemu-options.hx | 8 ++++++++
system/vl.c | 21 +++++++++++++++++++++
2 files changed, 29 insertions(+)
diff --git a/qemu-options.hx b/qemu-options.hx
index b460c63490..02d2f4d513 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1262,6 +1262,14 @@ SRST
A colon may be used to delineate multiple paths.
ERST
+DEF("secure-boot", HAS_ARG, QEMU_OPTION_secure_boot,
+ "-secure-boot on|off\n"
+ " enable/disable secure boot\n", QEMU_ARCH_S390X)
+SRST
+``-secure-boot on|off``
+ Enable/disable secure boot. Default is off.
+ERST
+
DEFHEADING()
DEFHEADING(Block device options:)
diff --git a/system/vl.c b/system/vl.c
index bd6197c887..5bdc35516c 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -524,6 +524,19 @@ static QemuOptsList qemu_boot_certificates_opts = {
},
};
+static QemuOptsList qemu_secure_boot_opts = {
+ .name = "secure-boot",
+ .implied_opt_name = "secure-boot",
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_secure_boot_opts.head),
+ .desc = {
+ {
+ .name = "secure-boot",
+ .type = QEMU_OPT_BOOL,
+ },
+ { /* end of list */ }
+ },
+};
+
const char *qemu_get_vm_name(void)
{
return qemu_name;
@@ -2894,6 +2907,7 @@ void qemu_init(int argc, char **argv)
qemu_add_opts(&qemu_fw_cfg_opts);
qemu_add_opts(&qemu_action_opts);
qemu_add_opts(&qemu_boot_certificates_opts);
+ qemu_add_opts(&qemu_secure_boot_opts);
qemu_add_run_with_opts();
module_call_init(MODULE_INIT_OPTS);
@@ -3046,6 +3060,13 @@ void qemu_init(int argc, char **argv)
exit(1);
}
break;
+ case QEMU_OPTION_secure_boot:
+ opts = qemu_opts_parse_noisily(qemu_find_opts("secure-boot"),
+ optarg, true);
+ if (!opts) {
+ exit(1);
+ }
+ break;
case QEMU_OPTION_fda:
case QEMU_OPTION_fdb:
drive_add(IF_FLOPPY, popt->index - QEMU_OPTION_fda,
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line
2025-04-08 15:55 ` [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line Zhuoying Cai
@ 2025-04-11 14:50 ` Thomas Huth
0 siblings, 0 replies; 54+ messages in thread
From: Thomas Huth @ 2025-04-11 14:50 UTC (permalink / raw)
To: Zhuoying Cai, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 08/04/2025 17.55, Zhuoying Cai wrote:
> The `-secure-boot on|off` command line option is implemented
> to enable secure IPL.
>
> By default, -secure-boot is set to false if not specified in
> the command line.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> qemu-options.hx | 8 ++++++++
> system/vl.c | 21 +++++++++++++++++++++
> 2 files changed, 29 insertions(+)
>
> diff --git a/qemu-options.hx b/qemu-options.hx
> index b460c63490..02d2f4d513 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -1262,6 +1262,14 @@ SRST
> A colon may be used to delineate multiple paths.
> ERST
>
> +DEF("secure-boot", HAS_ARG, QEMU_OPTION_secure_boot,
> + "-secure-boot on|off\n"
> + " enable/disable secure boot\n", QEMU_ARCH_S390X)
> +SRST
> +``-secure-boot on|off``
> + Enable/disable secure boot. Default is off.
> +ERST
As with patch 1, please try to turn this into a machine option instead.
Thanks,
Thomas
^ permalink raw reply [flat|nested] 54+ messages in thread
* [PATCH v1 21/24] hw/s390x/ipl: Set IPIB flags for secure IPL
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (19 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 22/24] pc-bios/s390-ccw: Handle true secure IPL mode Zhuoying Cai
` (2 subsequent siblings)
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
If `-secure-boot on` is specified on the command line option, indicating
true secure IPL enabled, set Secure-IPL bit and IPL-Information-Report
bit on in IPIB Flags field, and trigger true secure IPL in the S390 BIOS.
Any error that occurs during true secure IPL will cause the IPL to
terminate.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.c | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index b646fcc74e..60bafcbd2e 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -438,6 +438,15 @@ static bool s390_has_certificate(void)
return ipl->cert_store.count > 0;
}
+static bool s390_secure_boot_enabled(void)
+{
+ QemuOpts *opts;
+
+ opts = qemu_find_opts_singleton("secure-boot");
+
+ return qemu_opt_get_bool(opts, "secure-boot", false);
+}
+
static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
{
CcwDevice *ccw_dev = NULL;
@@ -495,6 +504,17 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
s390_ipl_convert_loadparm((char *)lp, iplb->loadparm);
iplb->flags |= DIAG308_FLAGS_LP_VALID;
+ /*
+ * If -secure-boot on, then toggle the secure IPL flags to trigger
+ * secure boot in the s390 BIOS.
+ *
+ * Boot process will terminate if any error occurs during secure boot.
+ *
+ * If SIPL is on, IPLIR must also be on.
+ */
+ if (s390_secure_boot_enabled()) {
+ iplb->hdr_flags |= (DIAG308_IPIB_FLAGS_SIPL | DIAG308_IPIB_FLAGS_IPLIR);
+ }
/*
* Secure boot in audit mode will perform
* if certificate(s) exist in the key store.
@@ -504,7 +524,7 @@ static bool s390_build_iplb(DeviceState *dev_st, IplParameterBlock *iplb)
*
* Results of secure boot will be stored in IIRB.
*/
- if (s390_has_certificate()) {
+ else if (s390_has_certificate()) {
iplb->hdr_flags |= DIAG308_IPIB_FLAGS_IPLIR;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 22/24] pc-bios/s390-ccw: Handle true secure IPL mode
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (20 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 21/24] hw/s390x/ipl: Set IPIB flags for secure IPL Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 23/24] pc-bios/s390-ccw: Handle secure boot with multiple boot devices Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
When secure boot is enabled (-secure-boot on) and certificate(s) are
provided, the boot operates in True Secure IPL mode.
Any verification error during True Secure IPL mode will cause the
entire boot process to terminate.
Secure IPL in audit mode requires at least one certificate provided in
the key store along with necessary facilities. If secure boot is enabled
but no certificate is provided, the boot process will also terminate, as
this is not a valid secure boot configuration.
Note: True Secure IPL mode is implemented for the SCSI scheme of
virtio-blk/virtio-scsi devices.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 25 ++++++++++++++++++++-----
pc-bios/s390-ccw/iplb.h | 7 +++++++
pc-bios/s390-ccw/main.c | 6 +++++-
pc-bios/s390-ccw/s390-ccw.h | 2 ++
4 files changed, 34 insertions(+), 6 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index 4bc6311802..a22061e1ad 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -757,6 +757,10 @@ static void valid_sclab_check(SclabOriginLocator *sclab_locator,
comps->device_entries[comp_index].cei |= S390_IPL_COMPONENT_CEI_INVALID_SCLAB;
/* a missing SCLAB will not be reported in audit mode */
+ if (boot_mode == ZIPL_SECURE_MODE) {
+ print_func(is_magic_match, "Magic is not matched. SCLAB does not exist");
+ }
+
return;
}
@@ -1164,7 +1168,11 @@ static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
int addr_range_index = 0;
void (*print_func)(bool, const char *) = NULL;
- print_func = &IPL_check;
+ if (boot_mode == ZIPL_SECURE_MODE) {
+ print_func = &IPL_assert;
+ } else if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ print_func = &IPL_check;
+ }
if (!secure_ipl_supported()) {
return -1;
@@ -1321,6 +1329,7 @@ static int zipl_run(ScsiBlockPtr *pte)
entry = (ComponentEntry *)(&header[1]);
switch (boot_mode) {
+ case ZIPL_SECURE_MODE:
case ZIPL_SECURE_AUDIT_MODE:
if (zipl_run_secure(entry, tmp_sec)) {
return -1;
@@ -1692,10 +1701,16 @@ static int zipl_load_vscsi(void)
int zipl_mode(void)
{
uint32_t cert_len;
+ bool secure;
cert_len = request_certificate((uint64_t *)certs_sec, 0);
+ secure = is_secure_boot_on(iplb->hdr_flags);
- return (cert_len > 0) ? ZIPL_SECURE_AUDIT_MODE : ZIPL_NORMAL_MODE;
+ if (secure) {
+ return (cert_len > 0) ? ZIPL_SECURE_MODE : ZIPL_SECURE_INVALID_MODE;
+ } else {
+ return (cert_len > 0) ? ZIPL_SECURE_AUDIT_MODE : ZIPL_NORMAL_MODE;
+ }
}
void zipl_load(void)
@@ -1703,7 +1718,7 @@ void zipl_load(void)
VDev *vdev = virtio_get_device();
if (vdev->is_cdrom) {
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
panic("Secure boot from ISO image is not supported!");
}
ipl_iso_el_torito();
@@ -1712,7 +1727,7 @@ void zipl_load(void)
}
if (virtio_get_device_type() == VIRTIO_ID_NET) {
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
panic("Virtio net boot device does not support secure boot!");
}
netmain();
@@ -1725,7 +1740,7 @@ void zipl_load(void)
return;
}
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
panic("ECKD boot device does not support secure boot!");
}
diff --git a/pc-bios/s390-ccw/iplb.h b/pc-bios/s390-ccw/iplb.h
index 42a9e081fe..734d049f42 100644
--- a/pc-bios/s390-ccw/iplb.h
+++ b/pc-bios/s390-ccw/iplb.h
@@ -185,6 +185,13 @@ static inline bool load_next_iplb(void)
return true;
}
+static inline bool is_secure_boot_on(uint8_t hdr_flags)
+{
+ /* If secure boot is on, SIPL bit and IPLIR bit must be on. */
+ return (hdr_flags & DIAG308_IPIB_FLAGS_SIPL) &&
+ (hdr_flags & DIAG308_IPIB_FLAGS_IPLIR);
+}
+
static inline uint64_t diag320(void *data, unsigned long subcode)
{
register unsigned long addr asm("0") = (unsigned long)data;
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 92004a6f82..6189a5a7ba 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -277,10 +277,14 @@ static void ipl_boot_device(void)
boot_mode = zipl_mode();
}
+ if (boot_mode == ZIPL_SECURE_INVALID_MODE) {
+ panic("Need at least one certificate for secure boot!");
+ }
+
switch (cutype) {
case CU_TYPE_DASD_3990:
case CU_TYPE_DASD_2107:
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
+ if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
panic("Passthrough (vfio) device does not support secure boot!");
}
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index 20d6827a4c..e8ff3f7883 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -84,6 +84,8 @@ void zipl_load(void);
#define ZIPL_NORMAL_MODE 1
#define ZIPL_SECURE_AUDIT_MODE 2
+#define ZIPL_SECURE_MODE 3
+#define ZIPL_SECURE_INVALID_MODE -1
int zipl_mode(void);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 23/24] pc-bios/s390-ccw: Handle secure boot with multiple boot devices
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (21 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 22/24] pc-bios/s390-ccw: Handle true secure IPL mode Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
23 siblings, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
The current approach to enabling secure boot relies on providing
-secure-boot and -boot-certificates options, which apply to all boot
devices.
With the possibility of multiple boot devices, secure boot expects all
provided devices to be supported and eligible (e.g.,
virtio-blk/virtio-scsi using the SCSI scheme).
If multiple boot devices are provided and include an unsupported (e.g.,
ECKD, VFIO) or a non-eligible (e.g., Net) device, the boot process will
terminate with an error logged to the console.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
pc-bios/s390-ccw/bootmap.c | 28 +++++++++-----
pc-bios/s390-ccw/main.c | 74 ++++++++++++++++++++++++++++++++++---
pc-bios/s390-ccw/s390-ccw.h | 1 +
3 files changed, 88 insertions(+), 15 deletions(-)
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index a22061e1ad..285aae114f 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -1713,23 +1713,35 @@ int zipl_mode(void)
}
}
+int zipl_check_scsi_mbr_magic(void)
+{
+ ScsiMbr *mbr = (void *)sec;
+
+ /* Grab the MBR */
+ memset(sec, FREE_SPACE_FILLER, sizeof(sec));
+ if (virtio_read(0, mbr)) {
+ puts("Cannot read block 0");
+ return -EIO;
+ }
+
+ if (!magic_match(mbr->magic, ZIPL_MAGIC)) {
+ return -1;
+ }
+
+ return 0;
+}
+
void zipl_load(void)
{
VDev *vdev = virtio_get_device();
if (vdev->is_cdrom) {
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
- panic("Secure boot from ISO image is not supported!");
- }
ipl_iso_el_torito();
puts("Failed to IPL this ISO image!");
return;
}
if (virtio_get_device_type() == VIRTIO_ID_NET) {
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
- panic("Virtio net boot device does not support secure boot!");
- }
netmain();
puts("Failed to IPL from this network!");
return;
@@ -1740,10 +1752,6 @@ void zipl_load(void)
return;
}
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
- panic("ECKD boot device does not support secure boot!");
- }
-
switch (virtio_get_device_type()) {
case VIRTIO_ID_BLOCK:
zipl_load_vblk();
diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
index 6189a5a7ba..30cb470ee4 100644
--- a/pc-bios/s390-ccw/main.c
+++ b/pc-bios/s390-ccw/main.c
@@ -271,8 +271,43 @@ static int virtio_setup(void)
return ret;
}
-static void ipl_boot_device(void)
+static void validate_secure_boot_device(void)
+{
+ switch (cutype) {
+ case CU_TYPE_DASD_3990:
+ case CU_TYPE_DASD_2107:
+ panic("Passthrough (vfio) device does not support secure boot!");
+ break;
+ case CU_TYPE_VIRTIO:
+ if (virtio_setup() == 0) {
+ VDev *vdev = virtio_get_device();
+
+ if (vdev->is_cdrom) {
+ panic("Secure boot from ISO image is not supported!");
+ }
+
+ if (virtio_get_device_type() == VIRTIO_ID_NET) {
+ panic("Virtio net boot device does not support secure boot!");
+ }
+
+ if (zipl_check_scsi_mbr_magic()) {
+ panic("ECKD boot device does not support secure boot!");
+ }
+ }
+ break;
+ default:
+ panic("Secure boot from unexpected device type is not supported!");
+ }
+
+ printf("SCSI boot device supports secure boot.\n");
+}
+
+static void check_secure_boot_support(void)
{
+ bool have_iplb_copy;
+ IplParameterBlock *iplb_copy;
+ QemuIplParameters *qipl_copy;
+
if (boot_mode == 0) {
boot_mode = zipl_mode();
}
@@ -281,13 +316,40 @@ static void ipl_boot_device(void)
panic("Need at least one certificate for secure boot!");
}
+ if (boot_mode == ZIPL_NORMAL_MODE) {
+ return;
+ }
+
+ /*
+ * Store copies of have_iplb, iplb and qipl.
+ * They will be updated in load_next_iplb().
+ */
+ have_iplb_copy = have_iplb;
+ iplb_copy = malloc(sizeof(IplParameterBlock));
+ qipl_copy = malloc(sizeof(QemuIplParameters));
+
+ memcpy(qipl_copy, &qipl, sizeof(QemuIplParameters));
+ memcpy(iplb_copy, iplb, sizeof(IplParameterBlock));
+
+ while (have_iplb_copy) {
+ if (have_iplb_copy && find_boot_device()) {
+ validate_secure_boot_device();
+ }
+ have_iplb_copy = load_next_iplb();
+ }
+
+ memcpy(&qipl, qipl_copy, sizeof(QemuIplParameters));
+ memcpy(iplb, iplb_copy, sizeof(IplParameterBlock));
+
+ free(qipl_copy);
+ free(iplb_copy);
+}
+
+static void ipl_boot_device(void)
+{
switch (cutype) {
case CU_TYPE_DASD_3990:
case CU_TYPE_DASD_2107:
- if (boot_mode == ZIPL_SECURE_AUDIT_MODE || boot_mode == ZIPL_SECURE_MODE) {
- panic("Passthrough (vfio) device does not support secure boot!");
- }
-
dasd_ipl(blk_schid, cutype);
break;
case CU_TYPE_VIRTIO:
@@ -337,6 +399,8 @@ void main(void)
probe_boot_device();
}
+ check_secure_boot_support();
+
while (have_iplb) {
boot_setup();
if (have_iplb && find_boot_device()) {
diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
index e8ff3f7883..23b903b9ae 100644
--- a/pc-bios/s390-ccw/s390-ccw.h
+++ b/pc-bios/s390-ccw/s390-ccw.h
@@ -88,6 +88,7 @@ void zipl_load(void);
#define ZIPL_SECURE_INVALID_MODE -1
int zipl_mode(void);
+int zipl_check_scsi_mbr_magic(void);
/* jump2ipl.c */
void write_reset_psw(uint64_t psw);
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
` (22 preceding siblings ...)
2025-04-08 15:55 ` [PATCH v1 23/24] pc-bios/s390-ccw: Handle secure boot with multiple boot devices Zhuoying Cai
@ 2025-04-08 15:55 ` Zhuoying Cai
2025-04-16 22:11 ` Collin Walling
23 siblings, 1 reply; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-08 15:55 UTC (permalink / raw)
To: thuth, richard.henderson, david, pbonzini
Cc: walling, jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel, zycai
If secure boot in audit mode or True Secure IPL mode is enabled without
specifying a boot device, the boot process will terminate with an error.
Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
---
hw/s390x/ipl.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 60bafcbd2e..0510f16a7d 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -767,6 +767,16 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
s390_ipl_create_cert_store(&ipl->cert_store);
if (!ipl->iplb_valid) {
ipl->iplb_valid = s390_init_all_iplbs(ipl);
+
+ /*
+ * Secure IPL without specifying a boot device.
+ * IPLB is not generated if no boot device is defined.
+ */
+ if ((s390_has_certificate() || s390_secure_boot_enabled()) &&
+ !ipl->iplb_valid) {
+ error_report("No boot devicie defined for Secure IPL");
+ exit(1);
+ }
} else {
ipl->qipl.chain_len = 0;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 54+ messages in thread
* Re: [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device
2025-04-08 15:55 ` [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
@ 2025-04-16 22:11 ` Collin Walling
2025-04-17 13:53 ` Jared Rossi
2025-04-17 14:13 ` Zhuoying Cai
0 siblings, 2 replies; 54+ messages in thread
From: Collin Walling @ 2025-04-16 22:11 UTC (permalink / raw)
To: Zhuoying Cai, thuth, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> If secure boot in audit mode or True Secure IPL mode is enabled without
> specifying a boot device, the boot process will terminate with an error.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> hw/s390x/ipl.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
> index 60bafcbd2e..0510f16a7d 100644
> --- a/hw/s390x/ipl.c
> +++ b/hw/s390x/ipl.c
> @@ -767,6 +767,16 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
> s390_ipl_create_cert_store(&ipl->cert_store);
> if (!ipl->iplb_valid) {
> ipl->iplb_valid = s390_init_all_iplbs(ipl);
> +
> + /*
> + * Secure IPL without specifying a boot device.
> + * IPLB is not generated if no boot device is defined.
> + */
> + if ((s390_has_certificate() || s390_secure_boot_enabled()) &&
> + !ipl->iplb_valid) {
> + error_report("No boot devicie defined for Secure IPL");
> + exit(1);
> + }
I'm confused why this check is needed. If there is no valid iplb, won't
boot just fail outright anyway?
> } else {
> ipl->qipl.chain_len = 0;
> }
--
Regards,
Collin
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device
2025-04-16 22:11 ` Collin Walling
@ 2025-04-17 13:53 ` Jared Rossi
2025-04-17 14:13 ` Zhuoying Cai
1 sibling, 0 replies; 54+ messages in thread
From: Jared Rossi @ 2025-04-17 13:53 UTC (permalink / raw)
To: Collin Walling, Zhuoying Cai, thuth, richard.henderson, david,
pbonzini
Cc: jjherne, fiuczy, pasic, borntraeger, farman, iii, qemu-s390x,
qemu-devel
On 4/16/25 6:11 PM, Collin Walling wrote:
> On 4/8/25 11:55 AM, Zhuoying Cai wrote:
>> If secure boot in audit mode or True Secure IPL mode is enabled without
>> specifying a boot device, the boot process will terminate with an error.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>> ---
>> hw/s390x/ipl.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
>> index 60bafcbd2e..0510f16a7d 100644
>> --- a/hw/s390x/ipl.c
>> +++ b/hw/s390x/ipl.c
>> @@ -767,6 +767,16 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
>> s390_ipl_create_cert_store(&ipl->cert_store);
>> if (!ipl->iplb_valid) {
>> ipl->iplb_valid = s390_init_all_iplbs(ipl);
>> +
>> + /*
>> + * Secure IPL without specifying a boot device.
>> + * IPLB is not generated if no boot device is defined.
>> + */
>> + if ((s390_has_certificate() || s390_secure_boot_enabled()) &&
>> + !ipl->iplb_valid) {
>> + error_report("No boot devicie defined for Secure IPL");
>> + exit(1);
>> + }
> I'm confused why this check is needed. If there is no valid iplb, won't
> boot just fail outright anyway?
No. If there is no IPLB (e.g. no device has been assigned a boot
index), we have a rudimentary probing routine that will examine if a
device is potentially bootable, and, if so, try to boot from it.
This check is basically stating that boot device probing is not
supported when using secure IPL, so at least one device must be defined
with a boot index.
>
>> } else {
>> ipl->qipl.chain_len = 0;
>> }
>
^ permalink raw reply [flat|nested] 54+ messages in thread
* Re: [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device
2025-04-16 22:11 ` Collin Walling
2025-04-17 13:53 ` Jared Rossi
@ 2025-04-17 14:13 ` Zhuoying Cai
1 sibling, 0 replies; 54+ messages in thread
From: Zhuoying Cai @ 2025-04-17 14:13 UTC (permalink / raw)
To: Collin Walling, thuth, richard.henderson, david, pbonzini
Cc: jjherne, jrossi, fiuczy, pasic, borntraeger, farman, iii,
qemu-s390x, qemu-devel
On 4/16/25 6:11 PM, Collin Walling wrote:
> On 4/8/25 11:55 AM, Zhuoying Cai wrote:
>> If secure boot in audit mode or True Secure IPL mode is enabled without
>> specifying a boot device, the boot process will terminate with an error.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
>> ---
>> hw/s390x/ipl.c | 10 ++++++++++
>> 1 file changed, 10 insertions(+)
>>
>> diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
>> index 60bafcbd2e..0510f16a7d 100644
>> --- a/hw/s390x/ipl.c
>> +++ b/hw/s390x/ipl.c
>> @@ -767,6 +767,16 @@ void s390_ipl_prepare_cpu(S390CPU *cpu)
>> s390_ipl_create_cert_store(&ipl->cert_store);
>> if (!ipl->iplb_valid) {
>> ipl->iplb_valid = s390_init_all_iplbs(ipl);
>> +
>> + /*
>> + * Secure IPL without specifying a boot device.
>> + * IPLB is not generated if no boot device is defined.
>> + */
>> + if ((s390_has_certificate() || s390_secure_boot_enabled()) &&
>> + !ipl->iplb_valid) {
>> + error_report("No boot devicie defined for Secure IPL");
>> + exit(1);
>> + }
>
> I'm confused why this check is needed. If there is no valid iplb, won't
> boot just fail outright anyway?
>
If no boot device is specified, the BIOS will still scan all channels to
find a bootable device (as defined in probe_boot_device() within
pc-bios/s390-ccw/main.c).
Boot will proceed if a bootable device is found, but the IPLB will not
be generated on the host side in this case. We cannot determine whether
secure boot is enabled, since it's indicated by the IPLB flags.
Therefore, specifying secure IPL without a boot device cause the process
to terminate early.
>> } else {
>> ipl->qipl.chain_len = 0;
>> }
>
>
^ permalink raw reply [flat|nested] 54+ messages in thread