On 08/12/2025 22.32, Zhuoying Cai wrote:
Create a certificate store for boot certificates used for secure IPL.
Load certificates from the `boot-certs` parameter of s390-ccw-virtio
machine type option into the cert store.
Currently, only X.509 certificates in PEM format are supported, as the
QEMU command line accepts certificates in PEM format only.
Signed-off-by: Zhuoying Cai <[email protected]>
---
...
diff --git a/hw/s390x/cert-store.c b/hw/s390x/cert-store.c
new file mode 100644
index 0000000000..cf16911d09
--- /dev/null
+++ b/hw/s390x/cert-store.c
@@ -0,0 +1,211 @@
+/*
+ * S390 certificate store implementation
+ *
+ * Copyright 2025 IBM Corp.
+ * Author(s): Zhuoying Cai <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cert-store.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/option.h"
+#include "qemu/config-file.h"
+#include "hw/s390x/ebcdic.h"
+#include "hw/s390x/s390-virtio-ccw.h"
+#include "qemu/cutils.h"
+#include "crypto/x509-utils.h"
+#include "qapi/qapi-types-machine-s390x.h"
+
+static BootCertificatesList *s390_get_boot_certs(void)
+{
+ return S390_CCW_MACHINE(qdev_get_machine())->boot_certs;
+}
+
+static S390IPLCertificate *init_cert_x509(size_t size, uint8_t *raw, Error
**errp)
+{
+ S390IPLCertificate *cert = NULL;
+ g_autofree uint8_t *cert_der = NULL;
+ size_t der_len = size;
+ int rc;
+
+ rc = qcrypto_x509_convert_cert_der(raw, size, &cert_der, &der_len, errp);
+ if (rc != 0) {
+ return NULL;
+ }
+
+ cert = g_new0(S390IPLCertificate, 1);
+ cert->size = size;
+ cert->der_size = der_len;
Why is only der_len stored here, but cert_der is silently discarded? Could
you please add a comment explaining this?
+ /* store raw pointer - ownership transfers to cert */
+ cert->raw = raw;
+
+ return cert;
+}
+
+static S390IPLCertificate *init_cert(char *path, Error **errp)
+{
+ char *buf;
+ size_t size;
+ char vc_name[VC_NAME_LEN_BYTES];
+ g_autofree gchar *filename = NULL;
+ S390IPLCertificate *cert = NULL;
+ Error *local_err = NULL;
+
+ filename = g_path_get_basename(path);
+
+ if (!g_file_get_contents(path, &buf, &size, NULL)) {
+ error_setg(errp, "Failed to load certificate: %s", path);
+ return NULL;
+ }
+
+ cert = init_cert_x509(size, (uint8_t *)buf, &local_err);
+ if (cert == NULL) {
+ error_propagate_prepend(errp, local_err,
+ "Failed to initialize certificate: %s: ",
path);
+ g_free(buf);
+ return NULL;
+ }
+
+ /*
+ * 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(cert->vc_name, vc_name, VC_NAME_LEN_BYTES);
+
+ return cert;
+}
+
+static void update_cert_store(S390IPLCertificateStore *cert_store,
+ S390IPLCertificate *cert)
+{
+ size_t data_buf_size;
+ size_t keyid_buf_size;
+ size_t hash_buf_size;
+ size_t cert_buf_size;
+
+ /* length field is word aligned for later DIAG use */
+ keyid_buf_size = ROUND_UP(CERT_KEY_ID_LEN, 4);
+ hash_buf_size = ROUND_UP(CERT_HASH_LEN, 4);
+ cert_buf_size = ROUND_UP(cert->der_size, 4);
+ data_buf_size = keyid_buf_size + hash_buf_size + cert_buf_size;
+
+ if (cert_store->max_cert_size < data_buf_size) {
+ cert_store->max_cert_size = data_buf_size;
+ }
The next line looks like we should do a
g_assert(cert_store->count < MAX_CERTIFICATES)
here first.
+ cert_store->certs[cert_store->count] = *cert;
+ cert_store->total_bytes += data_buf_size;
+ cert_store->count++;
+}
Thomas