On Wed, 2026-07-01 at 16:48 -0400, Zhuoying Cai wrote: > DIAG 320 subcode 1 provides information needed to determine > the amount of storage to store one or more certificates from the > certificate store. > > Upon successful completion, this subcode returns information of the current > cert store, such as the number of certificates stored and allowed in the cert > store, amount of space may need to be allocate to store a certificate, > etc for verification-certificate blocks (VCBs). > > 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. A VCSSB > length of 4 indicates that no certificate are available in the cert > store. > > Signed-off-by: Zhuoying Cai <[email protected]> > Reviewed-by: Farhan Ali <[email protected]> > Reviewed-by: Collin Walling <[email protected]> > --- > docs/specs/s390x-secure-ipl.rst | 12 ++++++ > hw/s390x/cert-store.h | 3 +- > include/hw/s390x/ipl/diag320.h | 57 +++++++++++++++++++++++++++++ > target/s390x/diag.c | 65 ++++++++++++++++++++++++++++++++- > 4 files changed, 134 insertions(+), 3 deletions(-)
One nit below; but either way... Reviewed-by: Eric Farman <[email protected]> > > diff --git a/docs/specs/s390x-secure-ipl.rst b/docs/specs/s390x-secure-ipl.rst > index 331d793008..d5d4c3a24d 100644 > --- a/docs/specs/s390x-secure-ipl.rst > +++ b/docs/specs/s390x-secure-ipl.rst > @@ -30,3 +30,15 @@ Subcode 0 - query installed subcodes > Returns a 256-bit installed subcodes mask (ISM) stored in the installed > subcodes block (ISB). This mask indicates which subcodes are currently > installed and available for use. > + > +Subcode 1 - query verification certificate storage information > + Provides the information required to determine the amount of memory > needed > + to store one or more verification-certificates (VCs) from the certificate > + store (CS). > + > + Upon successful completion, this subcode returns various storage size > values > + for verification-certificate blocks (VCBs). > + > + The output is returned in the verification-certificate-storage-size block > + (VCSSB). A VCSSB length of 4 indicates that no certificates are available > + in the CS. > diff --git a/hw/s390x/cert-store.h b/hw/s390x/cert-store.h > index 7fc9503cb9..6f5ee63177 100644 > --- a/hw/s390x/cert-store.h > +++ b/hw/s390x/cert-store.h > @@ -11,10 +11,9 @@ > #define HW_S390_CERT_STORE_H > > #include "hw/s390x/ipl/qipl.h" > +#include "hw/s390x/ipl/diag320.h" > #include "crypto/x509-utils.h" > > -#define CERT_NAME_MAX_LEN 64 > - > #define CERT_KEY_ID_LEN QCRYPTO_HASH_DIGEST_LEN_SHA256 > #define CERT_HASH_LEN QCRYPTO_HASH_DIGEST_LEN_SHA256 > > diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h > index aa04b699c6..d37d8eaa86 100644 > --- a/include/hw/s390x/ipl/diag320.h > +++ b/include/hw/s390x/ipl/diag320.h > @@ -11,10 +11,67 @@ > #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_NOT_SUPPORTED 0x0102 > +#define DIAG_320_RC_INVAL_VCSSB_LEN 0x0202 > > #define DIAG_320_ISM_QUERY_SUBCODES 0x80000000 > +#define DIAG_320_ISM_QUERY_VCSI 0x40000000 > + > +#define VCSSB_NO_VC 4 > +#define VCSSB_LEN_VALID 128 > + > +#define CERT_NAME_MAX_LEN 64 > + > +struct VCStorageSizeBlock { > + uint32_t length; > + uint8_t reserved0[3]; > + uint8_t version; > + uint32_t reserved1[6]; > + uint16_t total_vc_ct; > + uint16_t max_vc_ct; > + uint32_t reserved3[11]; > + uint32_t max_single_vcb_len; > + uint32_t total_vcb_len; > + uint32_t reserved4[10]; > +}; > +typedef struct VCStorageSizeBlock VCStorageSizeBlock; > + > +struct VCEntryHeader { > + uint32_t len; > + uint8_t flags; > + uint8_t key_type; > + uint16_t cert_idx; > + uint8_t name[CERT_NAME_MAX_LEN]; > + uint8_t format; > + uint8_t reserved0; > + uint16_t keyid_len; > + uint8_t reserved1; > + uint8_t hash_type; > + uint16_t hash_len; > + uint32_t reserved2; > + uint32_t cert_len; > + uint32_t reserved3[2]; > + uint16_t hash_offset; > + uint16_t cert_offset; > + uint32_t reserved4[7]; > +}; > +typedef struct VCEntryHeader VCEntryHeader; > + > +struct VCBlockHeader { > + uint32_t in_len; > + uint32_t reserved0; > + uint16_t first_vc_index; > + uint16_t last_vc_index; > + uint32_t reserved1[5]; > + uint32_t out_len; > + uint8_t reserved2[4]; > + uint16_t stored_ct; > + uint16_t remain_ct; > + uint32_t reserved3[5]; > +}; > +typedef struct VCBlockHeader VCBlockHeader; > > #endif > diff --git a/target/s390x/diag.c b/target/s390x/diag.c > index 9f98e4b677..2336732c9f 100644 > --- a/target/s390x/diag.c > +++ b/target/s390x/diag.c > @@ -205,11 +205,56 @@ out: > } > } > > +static int handle_diag320_query_vcsi(S390CPU *cpu, uint64_t addr, uint64_t > r1, > + uintptr_t ra, S390IPLCertificateStore > *cs) > +{ > + g_autofree VCStorageSizeBlock *vcssb = NULL; > + > + vcssb = g_new0(VCStorageSizeBlock, 1); > + if (s390_cpu_virt_mem_read(cpu, addr, r1, vcssb, sizeof(*vcssb))) { > + s390_cpu_virt_mem_handle_exc(cpu, ra); > + return -1; > + } > + > + if (be32_to_cpu(vcssb->length) > sizeof(*vcssb)) { > + return DIAG_320_RC_INVAL_VCSSB_LEN; > + } > + > + if (be32_to_cpu(vcssb->length) < VCSSB_LEN_VALID) { > + return DIAG_320_RC_INVAL_VCSSB_LEN; > + } These two checks could be squashed together, because the QEMU_BUILD_BUG below ensures that sizeof(*vcssb) == VCSSB_LEN_VALID, and so vcssb->length should also be only that. > + > + if (!cs->count) { > + vcssb->length = cpu_to_be32(VCSSB_NO_VC); > + } else { > + vcssb->version = 0; > + vcssb->total_vc_ct = cpu_to_be16(cs->count); > + vcssb->max_vc_ct = cpu_to_be16(MAX_CERTIFICATES); > + vcssb->max_single_vcb_len = cpu_to_be32(sizeof(VCBlockHeader) + > + sizeof(VCEntryHeader) + > + cs->largest_cert_size); > + vcssb->total_vcb_len = cpu_to_be32(sizeof(VCBlockHeader) + > + cs->count * sizeof(VCEntryHeader) > + > + cs->total_bytes); > + } > + > + if (s390_cpu_virt_mem_write(cpu, addr, r1, vcssb, > be32_to_cpu(vcssb->length))) { > + s390_cpu_virt_mem_handle_exc(cpu, ra); > + return -1; > + } > + return DIAG_320_RC_OK; > +} > + > +QEMU_BUILD_BUG_MSG(sizeof(VCStorageSizeBlock) != VCSSB_LEN_VALID, > + "size of VCStorageSizeBlock is wrong"); > + > void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t > ra) > { > S390CPU *cpu = env_archcpu(env); > + S390IPLCertificateStore *cs = s390_ipl_get_certificate_store(); > 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); > @@ -231,7 +276,8 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, > uint64_t r3, uintptr_t ra) > * but the current set of subcodes can fit within a single word > * for now. > */ > - uint32_t ism_word0 = cpu_to_be32(DIAG_320_ISM_QUERY_SUBCODES); > + uint32_t ism_word0 = cpu_to_be32(DIAG_320_ISM_QUERY_SUBCODES | > + DIAG_320_ISM_QUERY_VCSI); > > if (s390_cpu_virt_mem_write(cpu, addr, r1, &ism_word0, > sizeof(ism_word0))) { > s390_cpu_virt_mem_handle_exc(cpu, ra); > @@ -240,6 +286,23 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, > uint64_t r3, uintptr_t ra) > > env->regs[r1 + 1] = DIAG_320_RC_OK; > break; > + case DIAG_320_SUBC_QUERY_VCSI: > + if (addr & 0x7) { > + s390_program_interrupt(env, PGM_SPECIFICATION, ra); > + return; > + } > + > + if (!diag_parm_addr_valid(addr, sizeof(VCStorageSizeBlock), true)) { > + s390_program_interrupt(env, PGM_ADDRESSING, ra); > + return; > + } > + > + rc = handle_diag320_query_vcsi(cpu, addr, r1, ra, cs); > + if (rc == -1) { > + return; > + } > + env->regs[r1 + 1] = rc; > + break; > default: > env->regs[r1 + 1] = DIAG_320_RC_NOT_SUPPORTED; > break;
