17
#include "s390x-internal.h"
18
#include "hw/watchdog/wdt_diag288.h"
19
#include "system/cpus.h"
20
+#include "hw/s390x/cert-store.h"
21
#include "hw/s390x/ipl.h"
22
#include "hw/s390x/ipl/diag320.h"
23
#include "hw/s390x/s390-virtio-ccw.h"
24
#include "system/kvm.h"
25
#include "kvm/kvm_s390x.h"
26
#include "target/s390x/kvm/pv.h"
27
+#include "qapi/error.h"
28
#include "qemu/error-report.h"
29
+#include "crypto/x509-utils.h"
30
31
32
static inline bool diag_parm_addr_valid(uint64_t addr, size_t size, bool write)
244
return DIAG_320_RC_OK;
245
}
246
247
+static bool is_cert_valid(const S390IPLCertificate *cert)
248
+{
249
+ int rc;
250
+ Error *err = NULL;
251
+
252
+ rc = qcrypto_x509_check_cert_times(cert->raw, cert->size, &err);
253
+ if (rc != 0) {
254
+ error_report_err(err);
255
+ return false;
256
+ }
257
+
258
+ return true;
259
+}
260
+
261
+static int handle_key_id(VCEntry *vce, const S390IPLCertificate *cert)
262
+{
263
+ int rc;
264
+ g_autofree unsigned char *key_id_data = NULL;
265
+ size_t key_id_len;
266
+ Error *err = NULL;
267
+
268
+ rc = qcrypto_x509_get_cert_key_id(cert->raw, cert->size,
269
+ QCRYPTO_HASH_ALGO_SHA256,
270
+ &key_id_data, &key_id_len, &err);
271
+ if (rc < 0) {
272
+ error_report_err(err);
273
+ return 0;
274
+ }
275
+
276
+ if (sizeof(VCEntryHeader) + key_id_len > be32_to_cpu(vce->vce_hdr.len)) {
277
+ error_report("Unable to write key ID: exceeds buffer bounds");
278
+ return 0;
279
+ }
280
+
281
+ vce->vce_hdr.keyid_len = cpu_to_be16(key_id_len);
282
+
283
+ memcpy(vce->cert_buf, key_id_data, key_id_len);
284
+
285
+ return ROUND_UP(key_id_len, 4);
286
+}
287
+
288
+static int handle_hash(VCEntry *vce, const S390IPLCertificate *cert,
289
+ uint16_t keyid_field_len)
290
+{
291
+ int rc;
292
+ uint16_t hash_offset;
293
+ g_autofree void *hash_data = NULL;
294
+ size_t hash_len;
295
+ Error *err = NULL;
296
+
297
+ hash_len = CERT_HASH_LEN;
298
+ hash_data = g_malloc0(hash_len);
299
+ rc = qcrypto_get_x509_cert_fingerprint(cert->raw, cert->size,
300
+ QCRYPTO_HASH_ALGO_SHA256,
301
+ hash_data, &hash_len, &err);
302
+ if (rc < 0) {
303
+ error_report_err(err);
304
+ return 0;
305
+ }
306
+
307
+ hash_offset = sizeof(VCEntryHeader) + keyid_field_len;
308
+ if (hash_offset + hash_len > be32_to_cpu(vce->vce_hdr.len)) {
309
+ error_report("Unable to write hash: exceeds buffer bounds");
310
+ return 0;
311
+ }
312
+
313
+ vce->vce_hdr.hash_len = cpu_to_be16(hash_len);
314
+ vce->vce_hdr.hash_type = DIAG_320_VCE_HASHTYPE_SHA2_256;
315
+ vce->vce_hdr.hash_offset = cpu_to_be16(hash_offset);
316
+
317
+ memcpy((uint8_t *)vce + hash_offset, hash_data, hash_len);
318
+
319
+ return ROUND_UP(hash_len, 4);
320
+}
321
+
322
+static int handle_cert(VCEntry *vce, const S390IPLCertificate *cert,
323
+ uint16_t hash_field_len)
324
+{
325
+ int rc;
326
+ uint16_t cert_offset;
327
+ g_autofree uint8_t *cert_der = NULL;
328
+ size_t der_size;
329
+ Error *err = NULL;
330
+
331
+ rc = qcrypto_x509_convert_cert_der(cert->raw, cert->size,
332
+ &cert_der, &der_size, &err);
333
+ if (rc < 0) {
334
+ error_report_err(err);
335
+ return 0;
336
+ }
337
+
338
+ cert_offset = be16_to_cpu(vce->vce_hdr.hash_offset) + hash_field_len;
339
+ if (cert_offset + der_size > be32_to_cpu(vce->vce_hdr.len)) {
340
+ error_report("Unable to write certificate: exceeds buffer bounds");
341
+ return 0;
342
+ }
343
+
344
+ vce->vce_hdr.format = DIAG_320_VCE_FORMAT_X509_DER;
345
+ vce->vce_hdr.cert_len = cpu_to_be32(der_size);
346
+ vce->vce_hdr.cert_offset = cpu_to_be16(cert_offset);
347
+
348
+ memcpy((uint8_t *)vce + cert_offset, cert_der, der_size);
349
+
350
+ return ROUND_UP(der_size, 4);
351
+}
352
+
353
+static int get_key_type(const S390IPLCertificate *cert)
354
+{
355
+ int rc;
356
+ Error *err = NULL;
357
+
358
+ rc = qcrypto_x509_check_ecc_curve_p521(cert->raw, cert->size, &err);
359
+ if (rc == -1) {
360
+ error_report_err(err);
361
+ return -1;
362
+ }
363
+
364
+ return (rc == 1) ? DIAG_320_VCE_KEYTYPE_ECDSA_P521 :
365
+ DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
366
+}
367
+
368
+static int build_vce_header(VCEntry *vce, const S390IPLCertificate *cert, int idx)
369
+{
370
+ int key_type;
371
+
372
+ vce->vce_hdr.len = cpu_to_be32(sizeof(VCEntryHeader));
373
+ vce->vce_hdr.cert_idx = cpu_to_be16(idx + 1);
374
+ memcpy(vce->vce_hdr.name, cert->name, CERT_NAME_MAX_LEN);
375
+
376
+ if (!is_cert_valid(cert)) {
377
+ return -1;
378
+ }
379
+
380
+ key_type = get_key_type(cert);
381
+ if (key_type == -1) {
382
+ return -1;
383
+ }
384
+ vce->vce_hdr.key_type = key_type;
385
+
386
+ return 0;
387
+}
388
+
389
+static int build_vce_data(VCEntry *vce, const S390IPLCertificate *cert,
390
+ uint32_t vce_max_len)
391
+{
392
+ uint16_t keyid_field_len;
393
+ uint16_t hash_field_len;
394
+ uint32_t cert_field_len;
395
+ uint32_t vce_len;
396
+
397
+ vce->vce_hdr.len = cpu_to_be32(vce_max_len);
398
+
399
+ keyid_field_len = handle_key_id(vce, cert);
400
+ if (!keyid_field_len) {
401
+ return -1;
402
+ }
403
+
404
+ hash_field_len = handle_hash(vce, cert, keyid_field_len);
405
+ if (!hash_field_len) {
406
+ return -1;
407
+ }
408
+
409
+ cert_field_len = handle_cert(vce, cert, hash_field_len);
410
+ if (!cert_field_len) {
411
+ return -1;
412
+ }
413
+
414
+ vce_len = sizeof(VCEntryHeader) + keyid_field_len + hash_field_len + cert_field_len;
415
+ if (vce_len > vce_max_len) {
416
+ return -1;
417
+ }
418
+
419
+ vce->vce_hdr.flags |= DIAG_320_VCE_FLAGS_VALID;
420
+
421
+ /* Update vce length to reflect the actual size used by vce */
422
+ vce->vce_hdr.len = cpu_to_be32(vce_len);
423
+
424
+ return 0;
425
+}
426
+
427
+static int handle_diag320_store_vc(S390CPU *cpu, uint64_t addr, uint64_t r1, uintptr_t ra,
428
+ S390IPLCertificateStore *cs)
429
+{
430
+ g_autofree VCBlockHeader *vcb_hdr = NULL;
431
+ size_t remaining_space;
432
+ uint16_t first_vc_index;
433
+ uint16_t last_vc_index;
434
+ int cs_start_index;
435
+ int cs_end_index;
436
+ uint32_t vce_max_len;
437
+ uint32_t vce_len;
438
+ uint32_t in_len;
439
+
440
+ vcb_hdr = g_new0(VCBlockHeader, 1);
441
+ if (s390_cpu_virt_mem_read(cpu, addr, r1, vcb_hdr, sizeof(*vcb_hdr))) {
442
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
443
+ return -1;
444
+ }
445
+
446
+ in_len = be32_to_cpu(vcb_hdr->in_len);
447
+ first_vc_index = be16_to_cpu(vcb_hdr->first_vc_index);
448
+ last_vc_index = be16_to_cpu(vcb_hdr->last_vc_index);
449
+
450
+ if (in_len % TARGET_PAGE_SIZE != 0) {
451
+ return DIAG_320_RC_INVAL_VCB_LEN;
452
+ }
453
+
454
+ if (first_vc_index > last_vc_index) {
455
+ return DIAG_320_RC_BAD_RANGE;
456
+ }
457
+
458
+ vcb_hdr->out_len = sizeof(VCBlockHeader);
459
+
460
+ /*
461
+ * DIAG 320 subcode 2 expects to query a certificate store that
462
+ * maintains an index origin of 1. However, the S390IPLCertificateStore
463
+ * maintains an index origin of 0. Thus, the indices must be adjusted
464
+ * for correct access into the cert store. A couple of special cases
465
+ * must also be accounted for.
466
+ */
467
+
468
+ /* Both indices are 0; return header with no certs */
469
+ if (first_vc_index == 0 && last_vc_index == 0) {
470
+ goto out;
471
+ }
472
+
473
+ /* Normalize indices */
474
+ cs_start_index = (first_vc_index == 0) ? 0 : first_vc_index - 1;
475
+ cs_end_index = last_vc_index - 1;
476
+
477
+ /* Requested range is outside the cert store; return header with no certs */
478
+ if (cs_start_index >= cs->count || cs_end_index >= cs->count) {
479
+ goto out;
480
+ }
481
+
482
+ remaining_space = in_len - sizeof(VCBlockHeader);
483
+
484
+ for (int i = cs_start_index; i <= cs_end_index; i++) {
485
+ const S390IPLCertificate *cert = &cs->certs[i];
486
+ /*
487
+ * Each field of the VCE is word-aligned.
488
+ * Allocate enough space for the largest possible size for this VCE.
489
+ * As the certificate fields (key-id, hash, data) are parsed, the
490
+ * VCE's length field will be updated accordingly.
491
+ */
492
+ vce_max_len = sizeof(VCEntryHeader) + ROUND_UP(CERT_KEY_ID_LEN, 4) +
493
+ ROUND_UP(CERT_HASH_LEN, 4) + ROUND_UP(cert->der_size, 4);
494
+ g_autofree VCEntry *vce = g_malloc0(vce_max_len);
495
+
496
+ /*
497
+ * Bit 0 of the VCE flags indicates whether the certificate is valid.
498
+ * The caller of DIAG320 subcode 2 is responsible for verifying that
499
+ * the VCE contains a valid certificate.
500
+ */
501
+ if (build_vce_header(vce, cert, i) || build_vce_data(vce, cert, vce_max_len)) {
502
+ /*
503
+ * Error occurs - VCE does not contain a valid certificate.
504
+ * Bit 0 of the VCE flags is 0 and the VCE length is set.
505
+ */
506
+ vce->vce_hdr.len = cpu_to_be32(VCE_INVALID_LEN);
507
+ }
508
+ vce_len = be32_to_cpu(vce->vce_hdr.len);
509
+
510
+ /*
511
+ * If there is no more space to store the cert,
512
+ * set the remaining verification cert count and
513
+ * break early.
514
+ */
515
+ if (remaining_space < vce_len) {
516
+ vcb_hdr->remain_ct = cpu_to_be16(last_vc_index - i);
517
+ break;
518
+ }
519
+
520
+ /* Write VCE */
521
+ if (s390_cpu_virt_mem_write(cpu, addr + vcb_hdr->out_len, r1, vce, vce_len)) {
522
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
523
+ return -1;
524
+ }
525
+
526
+ vcb_hdr->out_len += vce_len;
527
+ remaining_space -= vce_len;
528
+ vcb_hdr->stored_ct++;
529
+ }
530
+ vcb_hdr->stored_ct = cpu_to_be16(vcb_hdr->stored_ct);
531
+
532
+out:
533
+ vcb_hdr->out_len = cpu_to_be32(vcb_hdr->out_len);
534
+
535
+ if (s390_cpu_virt_mem_write(cpu, addr, r1, vcb_hdr, sizeof(VCBlockHeader))) {
536
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
537
+ return -1;
538
+ }
539
+
540
+ return DIAG_320_RC_OK;
541
+}
542
+
543
QEMU_BUILD_BUG_MSG(sizeof(VCStorageSizeBlock) != VCSSB_LEN_VALID,
544
"size of VCStorageSizeBlock is wrong");
545
+QEMU_BUILD_BUG_MSG(sizeof(VCBlock) != 64, "size of VCBlock is wrong");
546
+QEMU_BUILD_BUG_MSG(sizeof(VCEntry) != 128, "size of VCEntry is wrong");
547
548
void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
549
{
574
* for now.
575
*/
576
ism_word0 = cpu_to_be32(DIAG_320_ISM_QUERY_SUBCODES |
276
- DIAG_320_ISM_QUERY_VCSI);
577
+ DIAG_320_ISM_QUERY_VCSI |
578
+ DIAG_320_ISM_STORE_VC);
579
580
if (s390_cpu_virt_mem_write(cpu, addr, r1, &ism_word0, sizeof(ism_word0))) {
581
s390_cpu_virt_mem_handle_exc(cpu, ra);
601
}
602
env->regs[r1 + 1] = rc;
603
break;
604
+ case DIAG_320_SUBC_STORE_VC:
605
+ if (addr & ~TARGET_PAGE_MASK) {
606
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
607
+ return;
608
+ }
609
+
610
+ rc = handle_diag320_store_vc(cpu, addr, r1, ra, cs);
611
+ if (rc == -1) {
612
+ return;
613
+ }
614
+ env->regs[r1 + 1] = rc;
615
+ break;
616
default:
617
env->regs[r1 + 1] = DIAG_320_RC_NOT_SUPPORTED;
618
break;