diff --git a/src/arch/s390x/core/prno.c b/src/arch/s390x/core/prno.c new file mode 100644 index 000000000..75e6b7ca5 --- /dev/null +++ b/src/arch/s390x/core/prno.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2026 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Perform Random Number Operation (PRNO) entropy source + * + */ + +#include +#include +#include +#include + +struct entropy_source prno_entropy __entropy_source ( ENTROPY_PREFERRED ); + +/** Colour for debug messages */ +#define colour &prno_entropy + +/** + * Enable entropy gathering + * + * @ret rc Return status code + */ +static int prno_entropy_enable ( void ) { + union prno_parameters params; + struct s390x_scalar_pair dummy1; + struct s390x_scalar_pair dummy2; + register unsigned long fn asm ( "0" ); + register void *pp asm ( "1" ); + + /* Check if PRNO is supported */ + if ( ! facility_is_installed ( FACILITY_MSA5 ) ) { + DBGC ( colour, "PRNO instruction is not supported\n" ); + return -ENOTSUP; + } + DBGC ( colour, "PRNO instruction is supported\n" ); + + /* Check if TRNG is supported */ + __asm__ ( ".machine push\n\t" + ".machine \"z14\"\n\t" + "prno %2, %3\n\t" + ".machine pop\n\t" + : "=r" ( fn ), + "=r" ( pp ), + "=a" ( dummy1 ), + "=a" ( dummy2 ), + "=m" ( params ) + : "0" ( PRNO_FN_QUERY ), + "1" ( ¶ms ) ); + if ( ! prno_is_supported ( ¶ms.supported, PRNO_FN_TRNG ) ) { + DBGC ( colour, "PRNO does not support TRNG (%016llx:%016llx)\n", + params.supported.mask[0], params.supported.mask[1] ); + return -ENOTSUP; + } + DBGC ( colour, "PRNO has support for TRNG\n" ); + + /* Linux assumes that the TRNG produces 100% entropy. Nothing + * seems to actually guarantee this, and we don't need much + * entropy, so assume conservatively that each byte contains + * at least one bit of min-entropy. + */ + entropy_init ( &prno_entropy, MIN_ENTROPY ( 1.0 ) ); + + return 0; +} + +/** + * Get noise sample + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int prno_get_noise ( noise_sample_t *noise ) { + struct s390x_pointer_pair raw = { noise, sizeof ( *noise ) }; + struct s390x_pointer_pair conditioned = { NULL, 0 }; + register unsigned long fn asm ( "0" ); + register void *pp asm ( "1" ); + + /* Generate raw true random numbers */ + __asm__ ( ".machine push\n\t" + ".machine \"z14\"\n\t" + "\n1:\n\t" + "prno %2, %3\n\t" + "jo 1b\n\t" + ".machine pop\n\t" + : "=r" ( fn ), + "=r" ( pp ), + "+a" ( raw ), + "+a" ( conditioned ), + "=m" ( *noise ) + : "0" ( PRNO_FN_TRNG ) ); + + return 0; +} + +/** Hardware entropy source */ +struct entropy_source prno_entropy __entropy_source ( ENTROPY_PREFERRED ) = { + .name = "prno", + .enable = prno_entropy_enable, + .get_noise = prno_get_noise, +}; diff --git a/src/arch/s390x/include/bits/errfile.h b/src/arch/s390x/include/bits/errfile.h index 4f47194d0..a26f33752 100644 --- a/src/arch/s390x/include/bits/errfile.h +++ b/src/arch/s390x/include/bits/errfile.h @@ -14,6 +14,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @{ */ +#define ERRFILE_prno ( ERRFILE_ARCH | ERRFILE_CORE | 0x00010000 ) + /** @} */ #endif /* _BITS_ERRFILE_H */ diff --git a/src/arch/s390x/include/ipxe/facility.h b/src/arch/s390x/include/ipxe/facility.h index bea9fec4f..8e7013a77 100644 --- a/src/arch/s390x/include/ipxe/facility.h +++ b/src/arch/s390x/include/ipxe/facility.h @@ -9,6 +9,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +/** Message-security-assist extension 5 facility ID */ +#define FACILITY_MSA5 57 + /** Installed facilities */ struct s390x_facilities { /** Bit mask of installed facilities */ diff --git a/src/arch/s390x/include/ipxe/prno.h b/src/arch/s390x/include/ipxe/prno.h new file mode 100644 index 000000000..3e16430e3 --- /dev/null +++ b/src/arch/s390x/include/ipxe/prno.h @@ -0,0 +1,46 @@ +#ifndef _IPXE_PRNO_H +#define _IPXE_PRNO_H + +/** @file + * + * Perform random number operation (PRNO) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Query supported functions */ +#define PRNO_FN_QUERY 0UL + +/** Supported function list */ +struct prno_supported { + uint64_t mask[2]; +} __attribute__ (( packed )); + +/** + * Check if function is supported + * + * @v supported Supported function list + * @v func Function number + * @ret is_supported Function is supported + */ +static inline __attribute__ (( always_inline )) int +prno_is_supported ( struct prno_supported *supported, unsigned int func ) { + uint64_t mask = supported->mask[ func / 64 ]; + uint64_t bit = ( 1UL << ( ~func % 64 ) ); + + return ( !! ( mask & bit ) ); +} + +/** True number number generator */ +#define PRNO_FN_TRNG 114UL + +/** Parameter block */ +union prno_parameters { + /** Supported function list */ + struct prno_supported supported; +}; + +#endif /* _IPXE_PRNO_H */ diff --git a/src/config/config_entropy.c b/src/config/config_entropy.c index 494b19f20..b1a2af5c0 100644 --- a/src/config/config_entropy.c +++ b/src/config/config_entropy.c @@ -53,3 +53,6 @@ REQUIRE_OBJECT ( rdrand ); #ifdef ENTROPY_ZKR REQUIRE_OBJECT ( zkr ); #endif +#ifdef ENTROPY_PRNO +REQUIRE_OBJECT ( prno ); +#endif