mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-11-03 07:59:06 +08:00
Compare commits
1 Commits
noinitrd
...
imagecmdli
| Author | SHA1 | Date | |
|---|---|---|---|
| ef77c453f2 |
12
src/arch/arm/include/bits/entropy.h
Normal file
12
src/arch/arm/include/bits/entropy.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef _BITS_ENTROPY_H
|
||||
#define _BITS_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* ARM-specific entropy API implementations
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#endif /* _BITS_ENTROPY_H */
|
||||
12
src/arch/loong64/include/bits/entropy.h
Normal file
12
src/arch/loong64/include/bits/entropy.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef _BITS_ENTROPY_H
|
||||
#define _BITS_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* LoongArch64-specific entropy API implementations
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#endif /* _BITS_ENTROPY_H */
|
||||
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* Hardware random number generator
|
||||
*
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <ipxe/cpuid.h>
|
||||
#include <ipxe/entropy.h>
|
||||
#include <ipxe/drbg.h>
|
||||
|
||||
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED );
|
||||
|
||||
/** Number of times to retry RDRAND instruction */
|
||||
#define RDRAND_RETRY_COUNT 16
|
||||
|
||||
/** Colour for debug messages */
|
||||
#define colour &rdrand_entropy
|
||||
|
||||
/**
|
||||
* Enable entropy gathering
|
||||
*
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int rdrand_entropy_enable ( void ) {
|
||||
struct x86_features features;
|
||||
|
||||
/* Check that RDRAND is supported */
|
||||
x86_features ( &features );
|
||||
if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_RDRAND ) ) {
|
||||
DBGC ( colour, "RDRAND not supported\n" );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Data returned by RDRAND is theoretically full entropy, up
|
||||
* to a security strength of 128 bits, so assume that each
|
||||
* sample contains exactly 8 bits of entropy.
|
||||
*/
|
||||
if ( DRBG_SECURITY_STRENGTH > 128 )
|
||||
return -ENOTSUP;
|
||||
entropy_init ( &rdrand_entropy, MIN_ENTROPY ( 8.0 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int rdrand_get_noise ( noise_sample_t *noise ) {
|
||||
unsigned int result;
|
||||
unsigned int discard_c;
|
||||
unsigned int ok;
|
||||
|
||||
/* Issue RDRAND, retrying until CF is set */
|
||||
__asm__ ( "\n1:\n\t"
|
||||
"rdrand %0\n\t"
|
||||
"sbb %1, %1\n\t"
|
||||
"loopz 1b\n\t"
|
||||
: "=r" ( result ), "=r" ( ok ), "=c" ( discard_c )
|
||||
: "2" ( RDRAND_RETRY_COUNT ) );
|
||||
if ( ! ok ) {
|
||||
DBGC ( colour, "RDRAND failed to become ready\n" );
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
*noise = result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Hardware random number generator entropy source */
|
||||
struct entropy_source rdrand_entropy __entropy_source ( ENTROPY_PREFERRED ) = {
|
||||
.name = "rdrand",
|
||||
.enable = rdrand_entropy_enable,
|
||||
.get_noise = rdrand_get_noise,
|
||||
};
|
||||
14
src/arch/x86/include/bits/entropy.h
Normal file
14
src/arch/x86/include/bits/entropy.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _BITS_ENTROPY_H
|
||||
#define _BITS_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* x86-specific entropy API implementations
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/rtc_entropy.h>
|
||||
|
||||
#endif /* _BITS_ENTROPY_H */
|
||||
@ -28,7 +28,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 )
|
||||
#define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 )
|
||||
#define ERRFILE_acpi_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00130000 )
|
||||
#define ERRFILE_rdrand ( ERRFILE_ARCH | ERRFILE_CORE | 0x00140000 )
|
||||
|
||||
#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
|
||||
#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )
|
||||
|
||||
@ -39,9 +39,6 @@ struct x86_features {
|
||||
/** Get standard features */
|
||||
#define CPUID_FEATURES 0x00000001UL
|
||||
|
||||
/** RDRAND instruction is supported */
|
||||
#define CPUID_FEATURES_INTEL_ECX_RDRAND 0x40000000UL
|
||||
|
||||
/** Hypervisor is present */
|
||||
#define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL
|
||||
|
||||
|
||||
62
src/arch/x86/include/ipxe/rtc_entropy.h
Normal file
62
src/arch/x86/include/ipxe/rtc_entropy.h
Normal file
@ -0,0 +1,62 @@
|
||||
#ifndef _IPXE_RTC_ENTROPY_H
|
||||
#define _IPXE_RTC_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* RTC-based entropy source
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef ENTROPY_RTC
|
||||
#define ENTROPY_PREFIX_rtc
|
||||
#else
|
||||
#define ENTROPY_PREFIX_rtc __rtc_
|
||||
#endif
|
||||
|
||||
/**
|
||||
* min-entropy per sample
|
||||
*
|
||||
* @ret min_entropy min-entropy of each sample
|
||||
*/
|
||||
static inline __always_inline min_entropy_t
|
||||
ENTROPY_INLINE ( rtc, min_entropy_per_sample ) ( void ) {
|
||||
|
||||
/* The min-entropy has been measured on several platforms
|
||||
* using the entropy_sample test code. Modelling the samples
|
||||
* as independent, and using a confidence level of 99.99%, the
|
||||
* measurements were as follows:
|
||||
*
|
||||
* qemu-kvm : 7.38 bits
|
||||
* VMware : 7.46 bits
|
||||
* Physical hardware : 2.67 bits
|
||||
*
|
||||
* We choose the lowest of these (2.67 bits) and apply a 50%
|
||||
* safety margin to allow for some potential non-independence
|
||||
* of samples.
|
||||
*/
|
||||
return MIN_ENTROPY ( 1.3 );
|
||||
}
|
||||
|
||||
extern uint8_t rtc_sample ( void );
|
||||
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static inline __always_inline int
|
||||
ENTROPY_INLINE ( rtc, get_noise ) ( noise_sample_t *noise ) {
|
||||
|
||||
/* Get sample */
|
||||
*noise = rtc_sample();
|
||||
|
||||
/* Always successful */
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _IPXE_RTC_ENTROPY_H */
|
||||
@ -39,8 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/cpuid.h>
|
||||
#include <ipxe/entropy.h>
|
||||
|
||||
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL );
|
||||
|
||||
/** Maximum time to wait for an RTC interrupt, in milliseconds */
|
||||
#define RTC_MAX_WAIT_MS 100
|
||||
|
||||
@ -205,21 +203,6 @@ static int rtc_entropy_enable ( void ) {
|
||||
if ( ( rc = rtc_entropy_check() ) != 0 )
|
||||
goto err_check;
|
||||
|
||||
/* The min-entropy has been measured on several platforms
|
||||
* using the entropy_sample test code. Modelling the samples
|
||||
* as independent, and using a confidence level of 99.99%, the
|
||||
* measurements were as follows:
|
||||
*
|
||||
* qemu-kvm : 7.38 bits
|
||||
* VMware : 7.46 bits
|
||||
* Physical hardware : 2.67 bits
|
||||
*
|
||||
* We choose the lowest of these (2.67 bits) and apply a 50%
|
||||
* safety margin to allow for some potential non-independence
|
||||
* of samples.
|
||||
*/
|
||||
entropy_init ( &rtc_entropy, MIN_ENTROPY ( 1.3 ) );
|
||||
|
||||
return 0;
|
||||
|
||||
err_check:
|
||||
@ -243,12 +226,11 @@ static void rtc_entropy_disable ( void ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get noise sample
|
||||
* Measure a single RTC tick
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
* @ret delta Length of RTC tick (in TSC units)
|
||||
*/
|
||||
static int rtc_get_noise ( noise_sample_t *noise ) {
|
||||
uint8_t rtc_sample ( void ) {
|
||||
uint32_t before;
|
||||
uint32_t after;
|
||||
uint32_t temp;
|
||||
@ -283,14 +265,10 @@ static int rtc_get_noise ( noise_sample_t *noise ) {
|
||||
: "=a" ( after ), "=d" ( before ), "=Q" ( temp )
|
||||
: "2" ( 0 ) );
|
||||
|
||||
*noise = ( after - before );
|
||||
return 0;
|
||||
return ( after - before );
|
||||
}
|
||||
|
||||
/** RTC entropy source */
|
||||
struct entropy_source rtc_entropy __entropy_source ( ENTROPY_NORMAL ) = {
|
||||
.name = "rtc",
|
||||
.enable = rtc_entropy_enable,
|
||||
.disable = rtc_entropy_disable,
|
||||
.get_noise = rtc_get_noise,
|
||||
};
|
||||
PROVIDE_ENTROPY_INLINE ( rtc, min_entropy_per_sample );
|
||||
PROVIDE_ENTROPY ( rtc, entropy_enable, rtc_entropy_enable );
|
||||
PROVIDE_ENTROPY ( rtc, entropy_disable, rtc_entropy_disable );
|
||||
PROVIDE_ENTROPY_INLINE ( rtc, get_noise );
|
||||
|
||||
@ -19,8 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define SMBIOS_EFI
|
||||
#define SANBOOT_EFI
|
||||
#define BOFM_EFI
|
||||
#define ENTROPY_EFITICK
|
||||
#define ENTROPY_EFIRNG
|
||||
#define ENTROPY_EFI
|
||||
#define TIME_EFI
|
||||
#define REBOOT_EFI
|
||||
#define ACPI_EFI
|
||||
@ -51,7 +50,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#if defined ( __i386__ ) || defined ( __x86_64__ )
|
||||
#define IOAPI_X86
|
||||
#define NAP_EFIX86
|
||||
#define ENTROPY_RDRAND
|
||||
#define CPUID_CMD /* x86 CPU feature detection command */
|
||||
#define UNSAFE_STD /* Avoid setting direction flag */
|
||||
#endif
|
||||
|
||||
@ -33,8 +33,4 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||
#define SANBOOT_PROTO_FCP
|
||||
#define SANBOOT_PROTO_HTTP
|
||||
|
||||
#if defined ( __i386__ ) || defined ( __x86_64__ )
|
||||
#define ENTROPY_RDRAND
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_DEFAULTS_LINUX_H */
|
||||
|
||||
@ -20,7 +20,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define SMBIOS_PCBIOS
|
||||
#define SANBOOT_PCBIOS
|
||||
#define ENTROPY_RTC
|
||||
#define ENTROPY_RDRAND
|
||||
#define TIME_RTC
|
||||
#define REBOOT_PCBIOS
|
||||
#define ACPI_RSDP
|
||||
|
||||
@ -51,33 +51,59 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
__einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" )
|
||||
|
||||
/**
|
||||
* Initialise repetition count test
|
||||
* Calculate cutoff value for the repetition count test
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret cutoff Cutoff value
|
||||
*
|
||||
* This is the cutoff value for the Repetition Count Test defined in
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
|
||||
*/
|
||||
static void repetition_count_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_repetition_count_test *test =
|
||||
&source->repetition_count_test;
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
repetition_count_cutoff ( void ) {
|
||||
double max_repetitions;
|
||||
unsigned int cutoff;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( test->repetition_count == 0 );
|
||||
assert ( test->cutoff > 0 );
|
||||
/* The cutoff formula for the repetition test is:
|
||||
*
|
||||
* C = ( 1 + ( -log2(W) / H_min ) )
|
||||
*
|
||||
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
|
||||
* 2011 Draft) Section 8.5.2.1.3.1).
|
||||
*/
|
||||
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
|
||||
min_entropy_per_sample() ) );
|
||||
|
||||
/* Round up to a whole number of repetitions. We don't have
|
||||
* the ceil() function available, so do the rounding by hand.
|
||||
*/
|
||||
cutoff = max_repetitions;
|
||||
if ( cutoff < max_repetitions )
|
||||
cutoff++;
|
||||
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
|
||||
|
||||
/* Floating-point operations are not allowed in iPXE since we
|
||||
* never set up a suitable environment. Abort the build
|
||||
* unless the calculated number of repetitions is a
|
||||
* compile-time constant.
|
||||
*/
|
||||
linker_assert ( __builtin_constant_p ( cutoff ),
|
||||
repetition_count_cutoff_not_constant );
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform repetition count test
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @v sample Noise sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the Repetition Count Test defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 8.5.2.1.2.
|
||||
*/
|
||||
static int repetition_count_test ( struct entropy_source *source,
|
||||
noise_sample_t sample ) {
|
||||
struct entropy_repetition_count_test *test =
|
||||
&source->repetition_count_test;
|
||||
static int repetition_count_test ( noise_sample_t sample ) {
|
||||
static noise_sample_t most_recent_sample;
|
||||
static unsigned int repetition_count = 0;
|
||||
|
||||
/* A = the most recently seen sample value
|
||||
* B = the number of times that value A has been seen in a row
|
||||
@ -90,71 +116,158 @@ static int repetition_count_test ( struct entropy_source *source,
|
||||
* the initial value of most_recent_sample is treated as being
|
||||
* undefined.)
|
||||
*/
|
||||
if ( ( sample == test->most_recent_sample ) &&
|
||||
( test->repetition_count > 0 ) ) {
|
||||
if ( ( sample == most_recent_sample ) && ( repetition_count > 0 ) ) {
|
||||
|
||||
/* a) If the new sample = A, then B is incremented by one. */
|
||||
test->repetition_count++;
|
||||
repetition_count++;
|
||||
|
||||
/* i. If B >= C, then an error condition is raised
|
||||
* due to a failure of the test
|
||||
*/
|
||||
if ( test->repetition_count >= test->cutoff ) {
|
||||
DBGC ( source, "ENTROPY %s excessively repeated "
|
||||
"value %d (%d/%d)\n", source->name, sample,
|
||||
test->repetition_count, test->cutoff );
|
||||
if ( repetition_count >= repetition_count_cutoff() )
|
||||
return -EPIPE_REPETITION_COUNT_TEST;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* b) Else:
|
||||
* i. A = new sample
|
||||
*/
|
||||
test->most_recent_sample = sample;
|
||||
most_recent_sample = sample;
|
||||
|
||||
/* ii. B = 1 */
|
||||
test->repetition_count = 1;
|
||||
repetition_count = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise adaptive proportion test
|
||||
* Window size for the adaptive proportion test
|
||||
*
|
||||
* @v source Entropy source
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
|
||||
* five possible window sizes: 16, 64, 256, 4096 and 65536.
|
||||
*
|
||||
* We expect to generate relatively few (<256) entropy samples during
|
||||
* a typical iPXE run; the use of a large window size would mean that
|
||||
* the test would never complete a single cycle. We use a window size
|
||||
* of 64, which is the smallest window size that permits values of
|
||||
* H_min down to one bit per sample.
|
||||
*/
|
||||
static void adaptive_proportion_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_adaptive_proportion_test *test =
|
||||
&source->adaptive_proportion_test;
|
||||
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( test->sample_count == 0 );
|
||||
assert ( test->repetition_count == 0 );
|
||||
assert ( test->cutoff > 0 );
|
||||
/**
|
||||
* Combine adaptive proportion test window size and min-entropy
|
||||
*
|
||||
* @v n N (window size)
|
||||
* @v h H (min-entropy)
|
||||
* @ret n_h (N,H) combined value
|
||||
*/
|
||||
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
|
||||
|
||||
/* Ensure that a new test run starts immediately */
|
||||
test->sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
|
||||
/**
|
||||
* Define a row of the adaptive proportion cutoff table
|
||||
*
|
||||
* @v h H (min-entropy)
|
||||
* @v c16 Cutoff for N=16
|
||||
* @v c64 Cutoff for N=64
|
||||
* @v c256 Cutoff for N=256
|
||||
* @v c4096 Cutoff for N=4096
|
||||
* @v c65536 Cutoff for N=65536
|
||||
*/
|
||||
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
|
||||
case APC_N_H ( 16, h ) : return c16; \
|
||||
case APC_N_H ( 64, h ) : return c64; \
|
||||
case APC_N_H ( 256, h ) : return c256; \
|
||||
case APC_N_H ( 4096, h ) : return c4096; \
|
||||
case APC_N_H ( 65536, h ) : return c65536;
|
||||
|
||||
/** Value used to represent "N/A" in adaptive proportion cutoff table */
|
||||
#define APC_NA 0
|
||||
|
||||
/**
|
||||
* Look up value in adaptive proportion test cutoff table
|
||||
*
|
||||
* @v n N (window size)
|
||||
* @v h H (min-entropy)
|
||||
* @ret cutoff Cutoff
|
||||
*
|
||||
* This is the table of cutoff values defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
|
||||
switch ( APC_N_H ( n, h ) ) {
|
||||
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
|
||||
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
|
||||
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
|
||||
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
|
||||
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
|
||||
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
|
||||
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
|
||||
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
|
||||
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
|
||||
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
|
||||
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
|
||||
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
|
||||
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
|
||||
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
|
||||
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
|
||||
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
|
||||
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
|
||||
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
|
||||
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
|
||||
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
|
||||
default:
|
||||
return APC_NA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cutoff value for the adaptive proportion test
|
||||
*
|
||||
* @ret cutoff Cutoff value
|
||||
*
|
||||
* This is the cutoff value for the Adaptive Proportion Test defined
|
||||
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
adaptive_proportion_cutoff ( void ) {
|
||||
unsigned int h;
|
||||
unsigned int n;
|
||||
unsigned int cutoff;
|
||||
|
||||
/* Look up cutoff value in cutoff table */
|
||||
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
|
||||
h = ( min_entropy_per_sample() / MIN_ENTROPY_SCALE );
|
||||
cutoff = adaptive_proportion_cutoff_lookup ( n, h );
|
||||
|
||||
/* Fail unless cutoff value is a build-time constant */
|
||||
linker_assert ( __builtin_constant_p ( cutoff ),
|
||||
adaptive_proportion_cutoff_not_constant );
|
||||
|
||||
/* Fail if cutoff value is N/A */
|
||||
linker_assert ( ( cutoff != APC_NA ),
|
||||
adaptive_proportion_cutoff_not_applicable );
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform adaptive proportion test
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @v sample Noise sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the Adaptive Proportion Test for the Most Common Value
|
||||
* defined in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.
|
||||
*/
|
||||
static int adaptive_proportion_test ( struct entropy_source *source,
|
||||
noise_sample_t sample ) {
|
||||
struct entropy_adaptive_proportion_test *test =
|
||||
&source->adaptive_proportion_test;
|
||||
static int adaptive_proportion_test ( noise_sample_t sample ) {
|
||||
static noise_sample_t current_counted_sample;
|
||||
static unsigned int sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
|
||||
static unsigned int repetition_count;
|
||||
|
||||
/* A = the sample value currently being counted
|
||||
* S = the number of samples examined in this run of the test so far
|
||||
* B = the number of samples examined in this run of the test so far
|
||||
* N = the total number of samples that must be observed in
|
||||
* one run of the test, also known as the "window size" of
|
||||
* the test
|
||||
@ -171,41 +284,37 @@ static int adaptive_proportion_test ( struct entropy_source *source,
|
||||
*/
|
||||
|
||||
/* 2. If S = N, then a new run of the test begins: */
|
||||
if ( test->sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
|
||||
if ( sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
|
||||
|
||||
/* a. A = the current sample */
|
||||
test->current_counted_sample = sample;
|
||||
current_counted_sample = sample;
|
||||
|
||||
/* b. S = 0 */
|
||||
test->sample_count = 0;
|
||||
sample_count = 0;
|
||||
|
||||
/* c. B = 0 */
|
||||
test->repetition_count = 0;
|
||||
repetition_count = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Else: (the test is already running)
|
||||
* a. S = S + 1
|
||||
*/
|
||||
test->sample_count++;
|
||||
sample_count++;
|
||||
|
||||
/* b. If A = the current sample, then: */
|
||||
if ( sample == test->current_counted_sample ) {
|
||||
if ( sample == current_counted_sample ) {
|
||||
|
||||
/* i. B = B + 1 */
|
||||
test->repetition_count++;
|
||||
repetition_count++;
|
||||
|
||||
/* ii. If S (sic) > C then raise an error
|
||||
* condition, because the test has
|
||||
* detected a failure
|
||||
*/
|
||||
if ( test->repetition_count > test->cutoff ) {
|
||||
DBGC ( source, "ENTROPY %s excessively "
|
||||
"repeated value %d (%d/%d)\n",
|
||||
source->name, sample,
|
||||
test->repetition_count, test->cutoff );
|
||||
if ( repetition_count > adaptive_proportion_cutoff() )
|
||||
return -EPIPE_ADAPTIVE_PROPORTION_TEST;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,180 +324,62 @@ static int adaptive_proportion_test ( struct entropy_source *source,
|
||||
/**
|
||||
* Get entropy sample
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret entropy Entropy sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the GetEntropy function defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 6.5.1.
|
||||
*/
|
||||
static int get_entropy ( struct entropy_source *source,
|
||||
entropy_sample_t *entropy ) {
|
||||
static int get_entropy ( entropy_sample_t *entropy ) {
|
||||
static int rc = 0;
|
||||
noise_sample_t noise;
|
||||
int rc;
|
||||
|
||||
/* Any failure is permanent */
|
||||
if ( ( rc = source->rc ) != 0 )
|
||||
goto err_broken;
|
||||
if ( rc != 0 )
|
||||
return rc;
|
||||
|
||||
/* Get noise sample */
|
||||
if ( ( rc = get_noise ( source, &noise ) ) != 0 )
|
||||
goto err_get_noise;
|
||||
if ( ( rc = get_noise ( &noise ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Perform Repetition Count Test and Adaptive Proportion Test
|
||||
* as mandated by ANS X9.82 Part 2 (October 2011 Draft)
|
||||
* Section 8.5.2.1.1.
|
||||
*/
|
||||
if ( ( rc = repetition_count_test ( source, noise ) ) != 0 )
|
||||
goto err_repetition_count_test;
|
||||
if ( ( rc = adaptive_proportion_test ( source, noise ) ) != 0 )
|
||||
goto err_adaptive_proportion_test;
|
||||
if ( ( rc = repetition_count_test ( noise ) ) != 0 )
|
||||
return rc;
|
||||
if ( ( rc = adaptive_proportion_test ( noise ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* We do not use any optional conditioning component */
|
||||
*entropy = noise;
|
||||
|
||||
return 0;
|
||||
|
||||
err_adaptive_proportion_test:
|
||||
err_repetition_count_test:
|
||||
err_get_noise:
|
||||
source->rc = rc;
|
||||
err_broken:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise startup test
|
||||
* Calculate number of samples required for startup tests
|
||||
*
|
||||
* @v source Entropy source
|
||||
*/
|
||||
static void startup_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_startup_test *test = &source->startup_test;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( test->tested == 0 );
|
||||
assert ( test->count > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform startup test
|
||||
* @ret num_samples Number of samples required
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret rc Return status code
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
|
||||
* that at least one full cycle of the continuous tests must be
|
||||
* performed at start-up.
|
||||
*/
|
||||
static int startup_test ( struct entropy_source *source ) {
|
||||
struct entropy_startup_test *test = &source->startup_test;
|
||||
entropy_sample_t sample;
|
||||
int rc;
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
startup_test_count ( void ) {
|
||||
unsigned int num_samples;
|
||||
|
||||
/* Perform mandatory number of startup tests */
|
||||
for ( ; test->tested < test->count ; test->tested++ ) {
|
||||
if ( ( rc = get_entropy ( source, &sample ) ) != 0 ) {
|
||||
DBGC ( source, "ENTROPY %s failed: %s\n",
|
||||
source->name, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/* At least max(N,C) samples shall be generated by the noise
|
||||
* source for start-up testing.
|
||||
*/
|
||||
num_samples = repetition_count_cutoff();
|
||||
if ( num_samples < adaptive_proportion_cutoff() )
|
||||
num_samples = adaptive_proportion_cutoff();
|
||||
linker_assert ( __builtin_constant_p ( num_samples ),
|
||||
startup_test_count_not_constant );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable entropy gathering
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int entropy_enable ( struct entropy_source *source ) {
|
||||
int rc;
|
||||
|
||||
/* Refuse to enable a previously failed source */
|
||||
if ( ( rc = source->rc ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Enable entropy source */
|
||||
if ( ( rc = source->enable() ) != 0 ) {
|
||||
DBGC ( source, "ENTROPY %s could not enable: %s\n",
|
||||
source->name, strerror ( rc ) );
|
||||
source->rc = rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
assert ( source->min_entropy_per_sample > 0 );
|
||||
|
||||
/* Initialise test state if this source has not previously been used */
|
||||
if ( source->startup_test.tested == 0 ) {
|
||||
repetition_count_test_init ( source );
|
||||
adaptive_proportion_test_init ( source );
|
||||
startup_test_init ( source );
|
||||
}
|
||||
|
||||
DBGC ( source, "ENTROPY %s enabled\n", source->name );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable and test entropy source
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int entropy_enable_and_test ( struct entropy_source *source ) {
|
||||
int rc;
|
||||
|
||||
/* Enable source */
|
||||
if ( ( rc = entropy_enable ( source ) ) != 0 )
|
||||
goto err_enable;
|
||||
|
||||
/* Test source */
|
||||
if ( ( rc = startup_test ( source ) ) != 0 )
|
||||
goto err_test;
|
||||
|
||||
DBGC ( source, "ENTROPY %s passed %d startup tests\n",
|
||||
source->name, source->startup_test.count );
|
||||
return 0;
|
||||
|
||||
err_test:
|
||||
entropy_disable ( source );
|
||||
err_enable:
|
||||
assert ( source->rc == rc );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable first working entropy source
|
||||
*
|
||||
* @v source Entropy source to fill in
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int entropy_enable_working ( struct entropy_source **source ) {
|
||||
int rc;
|
||||
|
||||
/* Find the first working source */
|
||||
rc = -ENOENT;
|
||||
for_each_table_entry ( *source, ENTROPY_SOURCES ) {
|
||||
if ( ( rc = entropy_enable_and_test ( *source ) ) == 0 )
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBGC ( *source, "ENTROPY has no working sources: %s\n",
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable entropy gathering
|
||||
*
|
||||
* @v source Entropy source
|
||||
*/
|
||||
void entropy_disable ( struct entropy_source *source ) {
|
||||
|
||||
/* Disable entropy gathering, if applicable */
|
||||
if ( source->disable )
|
||||
source->disable();
|
||||
|
||||
DBGC ( source, "ENTROPY %s disabled\n", source->name );
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -411,7 +402,7 @@ static uint32_t make_next_nonce ( void ) {
|
||||
/**
|
||||
* Obtain entropy input temporary buffer
|
||||
*
|
||||
* @v min_entropy Min-entropy required
|
||||
* @v num_samples Number of entropy samples
|
||||
* @v tmp Temporary buffer
|
||||
* @v tmp_len Length of temporary buffer
|
||||
* @ret rc Return status code
|
||||
@ -421,41 +412,47 @@ static uint32_t make_next_nonce ( void ) {
|
||||
* and condensing each entropy source output after each GetEntropy
|
||||
* call) as defined in ANS X9.82 Part 4 (April 2011 Draft) Section
|
||||
* 13.3.4.2.
|
||||
*
|
||||
* To minimise code size, the number of samples required is calculated
|
||||
* at compilation time.
|
||||
*/
|
||||
int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
|
||||
size_t tmp_len ) {
|
||||
struct entropy_source *source;
|
||||
static unsigned int startup_tested = 0;
|
||||
struct {
|
||||
uint32_t nonce;
|
||||
entropy_sample_t sample;
|
||||
} __attribute__ (( packed )) data;;
|
||||
uint8_t df_buf[tmp_len];
|
||||
min_entropy_t entropy_total;
|
||||
unsigned int num_samples;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Enable entropy gathering */
|
||||
if ( ( rc = entropy_enable_working ( &source ) ) != 0 )
|
||||
goto err_enable_working;
|
||||
if ( ( rc = entropy_enable() ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Sanity checks */
|
||||
assert ( source->startup_test.count > 0 );
|
||||
assert ( source->startup_test.tested >= source->startup_test.count );
|
||||
/* Perform mandatory startup tests, if not yet performed */
|
||||
for ( ; startup_tested < startup_test_count() ; startup_tested++ ) {
|
||||
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
|
||||
goto err_get_entropy;
|
||||
}
|
||||
|
||||
/* 3. entropy_total = 0 */
|
||||
entropy_total = MIN_ENTROPY ( 0 );
|
||||
/* 3. entropy_total = 0
|
||||
*
|
||||
* (Nothing to do; the number of entropy samples required has
|
||||
* already been precalculated.)
|
||||
*/
|
||||
|
||||
/* 4. tmp = a fixed n-bit value, such as 0^n */
|
||||
memset ( tmp, 0, tmp_len );
|
||||
|
||||
/* 5. While ( entropy_total < min_entropy ) */
|
||||
for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) {
|
||||
while ( num_samples-- ) {
|
||||
/* 5.1. ( status, entropy_bitstring, assessed_entropy )
|
||||
* = GetEntropy()
|
||||
* 5.2. If status indicates an error, return ( status, Null )
|
||||
*/
|
||||
if ( ( rc = get_entropy ( source, &data.sample ) ) != 0 )
|
||||
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
|
||||
goto err_get_entropy;
|
||||
|
||||
/* 5.3. nonce = MakeNextNonce() */
|
||||
@ -469,26 +466,19 @@ int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
for ( i = 0 ; i < tmp_len ; i++ )
|
||||
tmp[i] ^= df_buf[i];
|
||||
|
||||
/* 5.5. entropy_total = entropy_total + assessed_entropy */
|
||||
entropy_total += source->min_entropy_per_sample;
|
||||
/* 5.5. entropy_total = entropy_total + assessed_entropy
|
||||
*
|
||||
* (Nothing to do; the number of entropy samples
|
||||
* required has already been precalculated.)
|
||||
*/
|
||||
}
|
||||
|
||||
/* Disable entropy gathering */
|
||||
entropy_disable ( source );
|
||||
entropy_disable();
|
||||
|
||||
DBGC ( source, "ENTROPY %s gathered %d bits in %d samples\n",
|
||||
source->name, ( min_entropy / MIN_ENTROPY_SCALE ), num_samples );
|
||||
return 0;
|
||||
|
||||
err_get_entropy:
|
||||
entropy_disable ( source );
|
||||
assert ( source->rc == rc );
|
||||
err_enable_working:
|
||||
entropy_disable();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Drag in objects via entropy_enable */
|
||||
REQUIRING_SYMBOL ( entropy_enable );
|
||||
|
||||
/* Drag in entropy configuration */
|
||||
REQUIRE_OBJECT ( config_entropy );
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* 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 (at your option) any later version.
|
||||
* 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
|
||||
@ -21,31 +23,18 @@
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <config/entropy.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Entropy configuration options
|
||||
* Nonexistent entropy source
|
||||
*
|
||||
*
|
||||
* This source provides no entropy and must NOT be used in a
|
||||
* security-sensitive environment.
|
||||
*/
|
||||
|
||||
PROVIDE_REQUIRING_SYMBOL();
|
||||
#include <ipxe/entropy.h>
|
||||
|
||||
/*
|
||||
* Drag in entropy sources
|
||||
*/
|
||||
#ifdef ENTROPY_RTC
|
||||
REQUIRE_OBJECT ( rtc_entropy );
|
||||
#endif
|
||||
#ifdef ENTROPY_EFITICK
|
||||
REQUIRE_OBJECT ( efi_entropy );
|
||||
#endif
|
||||
#ifdef ENTROPY_EFIRNG
|
||||
REQUIRE_OBJECT ( efi_rng );
|
||||
#endif
|
||||
#ifdef ENTROPY_LINUX
|
||||
REQUIRE_OBJECT ( linux_entropy );
|
||||
#endif
|
||||
#ifdef ENTROPY_RDRAND
|
||||
REQUIRE_OBJECT ( rdrand );
|
||||
#endif
|
||||
PROVIDE_ENTROPY_INLINE ( null, min_entropy_per_sample );
|
||||
PROVIDE_ENTROPY_INLINE ( null, entropy_enable );
|
||||
PROVIDE_ENTROPY_INLINE ( null, entropy_disable );
|
||||
PROVIDE_ENTROPY_INLINE ( null, get_noise );
|
||||
@ -609,7 +609,6 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
|
||||
*/
|
||||
static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
|
||||
int rc ) {
|
||||
struct scsi_device *scsidev = scsicmd->scsidev;
|
||||
struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
|
||||
struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
|
||||
struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
|
||||
@ -646,9 +645,6 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
|
||||
}
|
||||
capacity.max_count = -1U;
|
||||
|
||||
/* Allow transport layer to update capacity */
|
||||
block_capacity ( &scsidev->scsi, &capacity );
|
||||
|
||||
/* Return capacity to caller */
|
||||
block_capacity ( &scsicmd->block, &capacity );
|
||||
|
||||
|
||||
35
src/include/ipxe/efi/efi_entropy.h
Normal file
35
src/include/ipxe/efi/efi_entropy.h
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef _IPXE_EFI_ENTROPY_H
|
||||
#define _IPXE_EFI_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI entropy source
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef ENTROPY_EFI
|
||||
#define ENTROPY_PREFIX_efi
|
||||
#else
|
||||
#define ENTROPY_PREFIX_efi __efi_
|
||||
#endif
|
||||
|
||||
/**
|
||||
* min-entropy per sample
|
||||
*
|
||||
* @ret min_entropy min-entropy of each sample
|
||||
*/
|
||||
static inline __always_inline min_entropy_t
|
||||
ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) {
|
||||
|
||||
/* We use essentially the same mechanism as for the BIOS
|
||||
* RTC-based entropy source, and so assume the same
|
||||
* min-entropy per sample.
|
||||
*/
|
||||
return MIN_ENTROPY ( 1.3 );
|
||||
}
|
||||
|
||||
#endif /* _IPXE_EFI_ENTROPY_H */
|
||||
@ -12,11 +12,40 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ipxe/api.h>
|
||||
#include <ipxe/hash_df.h>
|
||||
#include <ipxe/sha256.h>
|
||||
#include <ipxe/tables.h>
|
||||
#include <config/entropy.h>
|
||||
|
||||
/**
|
||||
* Calculate static inline entropy API function name
|
||||
*
|
||||
* @v _prefix Subsystem prefix
|
||||
* @v _api_func API function
|
||||
* @ret _subsys_func Subsystem API function
|
||||
*/
|
||||
#define ENTROPY_INLINE( _subsys, _api_func ) \
|
||||
SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
|
||||
|
||||
/**
|
||||
* Provide a entropy API implementation
|
||||
*
|
||||
* @v _prefix Subsystem prefix
|
||||
* @v _api_func API function
|
||||
* @v _func Implementing function
|
||||
*/
|
||||
#define PROVIDE_ENTROPY( _subsys, _api_func, _func ) \
|
||||
PROVIDE_SINGLE_API ( ENTROPY_PREFIX_ ## _subsys, _api_func, _func )
|
||||
|
||||
/**
|
||||
* Provide a static inline entropy API implementation
|
||||
*
|
||||
* @v _prefix Subsystem prefix
|
||||
* @v _api_func API function
|
||||
*/
|
||||
#define PROVIDE_ENTROPY_INLINE( _subsys, _api_func ) \
|
||||
PROVIDE_SINGLE_API_INLINE ( ENTROPY_PREFIX_ ## _subsys, _api_func )
|
||||
|
||||
/** A noise sample */
|
||||
typedef uint8_t noise_sample_t;
|
||||
|
||||
@ -42,148 +71,56 @@ typedef unsigned int min_entropy_t;
|
||||
#define MIN_ENTROPY( bits ) \
|
||||
( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) )
|
||||
|
||||
/**
|
||||
* Repetition count test state
|
||||
*
|
||||
* This is the state for the repetition Count Test defined in ANS
|
||||
* X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
|
||||
*/
|
||||
struct entropy_repetition_count_test {
|
||||
/**
|
||||
* A = the most recently seen sample value
|
||||
*/
|
||||
noise_sample_t most_recent_sample;
|
||||
/**
|
||||
* B = the number of times that value A has been seen in a row
|
||||
*/
|
||||
unsigned int repetition_count;
|
||||
/**
|
||||
* C = the cutoff value above which the repetition test should fail
|
||||
*
|
||||
* Filled in by entropy_init().
|
||||
*/
|
||||
unsigned int cutoff;
|
||||
};
|
||||
/* Include all architecture-independent entropy API headers */
|
||||
#include <ipxe/null_entropy.h>
|
||||
#include <ipxe/efi/efi_entropy.h>
|
||||
#include <ipxe/linux/linux_entropy.h>
|
||||
|
||||
/* Include all architecture-dependent entropy API headers */
|
||||
#include <bits/entropy.h>
|
||||
|
||||
/**
|
||||
* Adaptive proportion test state
|
||||
* Enable entropy gathering
|
||||
*
|
||||
* This is the state for the Adaptive Proportion Test for the Most
|
||||
* Common Value defined in ANS X9.82 Part 2 (October 2011 Draft)
|
||||
* Section 8.5.2.1.3.
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
struct entropy_adaptive_proportion_test {
|
||||
/**
|
||||
* A = the sample value currently being counted
|
||||
*/
|
||||
noise_sample_t current_counted_sample;
|
||||
/**
|
||||
* S = the number of samples examined in this run of the test so far
|
||||
*/
|
||||
unsigned int sample_count;
|
||||
/**
|
||||
* B = the current number of times that S (sic) has been seen
|
||||
* in the W (sic) samples examined so far
|
||||
*/
|
||||
unsigned int repetition_count;
|
||||
/**
|
||||
* C = the cutoff value above which the repetition test should fail
|
||||
*
|
||||
* Filled in by entropy_init().
|
||||
*/
|
||||
unsigned int cutoff;
|
||||
};
|
||||
int entropy_enable ( void );
|
||||
|
||||
/**
|
||||
* Startup test state
|
||||
* Disable entropy gathering
|
||||
*
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
|
||||
* that at least one full cycle of the continuous tests must be
|
||||
* performed at start-up.
|
||||
*/
|
||||
struct entropy_startup_test {
|
||||
/** Number of startup tests performed */
|
||||
unsigned int tested;
|
||||
/**
|
||||
* Number of startup tests required for one full cycle
|
||||
*
|
||||
* Filled in by entropy_init().
|
||||
*/
|
||||
unsigned int count;
|
||||
};
|
||||
void entropy_disable ( void );
|
||||
|
||||
/** An entropy source */
|
||||
struct entropy_source {
|
||||
/** Name */
|
||||
const char *name;
|
||||
/**
|
||||
* min-entropy per sample
|
||||
*
|
||||
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
|
||||
* NIST SP 800-90 Appendix C.3 as
|
||||
*
|
||||
* H_min = -log2 ( p_max )
|
||||
*
|
||||
* where p_max is the probability of the most likely sample value.
|
||||
*
|
||||
* Filled in by entropy_init().
|
||||
*/
|
||||
min_entropy_t min_entropy_per_sample;
|
||||
/** Repetition count test state */
|
||||
struct entropy_repetition_count_test repetition_count_test;
|
||||
/** Adaptive proportion test state */
|
||||
struct entropy_adaptive_proportion_test adaptive_proportion_test;
|
||||
/** Startup test state */
|
||||
struct entropy_startup_test startup_test;
|
||||
/**
|
||||
* Failure status (if any)
|
||||
*
|
||||
* Any failure of an entropy source is regarded as permanent.
|
||||
*/
|
||||
int rc;
|
||||
|
||||
/**
|
||||
* Enable entropy gathering
|
||||
*
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int ( * enable ) ( void );
|
||||
/**
|
||||
* Disable entropy gathering
|
||||
*
|
||||
*/
|
||||
void ( * disable ) ( void );
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the GetNoise function defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 6.5.2.
|
||||
*/
|
||||
int ( * get_noise ) ( noise_sample_t *noise );
|
||||
};
|
||||
|
||||
/** Entropy source table */
|
||||
#define ENTROPY_SOURCES __table ( struct entropy_source, "entropy_sources" )
|
||||
|
||||
/** Declare an entropy source */
|
||||
#define __entropy_source( order ) __table_entry ( ENTROPY_SOURCES, order )
|
||||
|
||||
/** @defgroup entropy_source_order Entropy source order
|
||||
/**
|
||||
* min-entropy per sample
|
||||
*
|
||||
* @{
|
||||
* @ret min_entropy min-entropy of each sample
|
||||
*
|
||||
* min-entropy is defined in ANS X9.82 Part 1-2006 Section 8.3 and in
|
||||
* NIST SP 800-90 Appendix C.3 as
|
||||
*
|
||||
* H_min = -log2 ( p_max )
|
||||
*
|
||||
* where p_max is the probability of the most likely sample value.
|
||||
*
|
||||
* This must be a compile-time constant.
|
||||
*/
|
||||
min_entropy_t min_entropy_per_sample ( void );
|
||||
|
||||
#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
|
||||
#define ENTROPY_NORMAL 02 /**< Normal entropy source */
|
||||
#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the GetNoise function defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 6.5.2.
|
||||
*/
|
||||
int get_noise ( noise_sample_t *noise );
|
||||
|
||||
/** @} */
|
||||
|
||||
extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
size_t tmp_len );
|
||||
extern int get_entropy_input_tmp ( unsigned int num_samples,
|
||||
uint8_t *tmp, size_t tmp_len );
|
||||
|
||||
/** Use SHA-256 as the underlying hash algorithm for Hash_df
|
||||
*
|
||||
@ -194,22 +131,6 @@ extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
/** Underlying hash algorithm output length (in bytes) */
|
||||
#define ENTROPY_HASH_DF_OUTLEN_BYTES SHA256_DIGEST_SIZE
|
||||
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* This is the GetNoise function defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 6.5.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
get_noise ( struct entropy_source *source, noise_sample_t *noise ) {
|
||||
|
||||
return source->get_noise ( noise );
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain entropy input
|
||||
*
|
||||
@ -224,8 +145,8 @@ get_noise ( struct entropy_source *source, noise_sample_t *noise ) {
|
||||
* each entropy source output after each GetEntropy call) as defined
|
||||
* in ANS X9.82 Part 4 (April 2011 Draft) Section 13.3.4.2.
|
||||
*
|
||||
* This function is inlined since the entropy amount and length inputs
|
||||
* are always compile-time constants.
|
||||
* To minimise code size, the number of samples required is calculated
|
||||
* at compilation time.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
|
||||
@ -233,16 +154,41 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
|
||||
size_t tmp_len = ( ( ( min_entropy_bits * 2 ) + 7 ) / 8 );
|
||||
uint8_t tmp_buf[ tmp_len ];
|
||||
uint8_t *tmp = ( ( tmp_len > max_len ) ? tmp_buf : data );
|
||||
double min_samples;
|
||||
unsigned int num_samples;
|
||||
unsigned int n;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
/* Sanity checks */
|
||||
linker_assert ( ( min_entropy_per_sample() <=
|
||||
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
|
||||
min_entropy_per_sample_is_impossibly_high );
|
||||
linker_assert ( ( min_entropy_bits <= ( 8 * max_len ) ),
|
||||
entropy_buffer_too_small );
|
||||
|
||||
/* Round up minimum entropy to an integral number of bytes */
|
||||
min_entropy_bits = ( ( min_entropy_bits + 7 ) & ~7 );
|
||||
|
||||
/* Calculate number of samples required to contain sufficient entropy */
|
||||
min_samples = ( MIN_ENTROPY ( min_entropy_bits ) /
|
||||
min_entropy_per_sample() );
|
||||
|
||||
/* Round up to a whole number of samples. We don't have the
|
||||
* ceil() function available, so do the rounding by hand.
|
||||
*/
|
||||
num_samples = min_samples;
|
||||
if ( num_samples < min_samples )
|
||||
num_samples++;
|
||||
linker_assert ( ( num_samples >= min_samples ), rounding_error );
|
||||
|
||||
/* Floating-point operations are not allowed in iPXE since we
|
||||
* never set up a suitable environment. Abort the build
|
||||
* unless the calculated number of samples is a compile-time
|
||||
* constant.
|
||||
*/
|
||||
linker_assert ( __builtin_constant_p ( num_samples ),
|
||||
num_samples_not_constant );
|
||||
|
||||
/* (Unnumbered). The output length of the hash function shall
|
||||
* meet or exceed the security strength indicated by the
|
||||
* min_entropy parameter.
|
||||
@ -272,10 +218,8 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
|
||||
linker_assert ( __builtin_constant_p ( tmp_len ),
|
||||
tmp_len_not_constant );
|
||||
linker_assert ( ( n == ( 8 * tmp_len ) ), tmp_len_mismatch );
|
||||
if ( ( rc = get_entropy_input_tmp ( MIN_ENTROPY ( min_entropy_bits ),
|
||||
tmp, tmp_len ) ) != 0 ) {
|
||||
if ( ( rc = get_entropy_input_tmp ( num_samples, tmp, tmp_len ) ) != 0 )
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 6. If ( n < min_length ), then tmp = tmp || 0^(min_length-n)
|
||||
* 7. If ( n > max_length ), then tmp = df ( tmp, max_length )
|
||||
@ -298,231 +242,4 @@ get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cutoff value for the repetition count test
|
||||
*
|
||||
* @v min_entropy_per_sample Min-entropy per sample
|
||||
* @ret cutoff Cutoff value
|
||||
*
|
||||
* This is the cutoff value for the Repetition Count Test defined in
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
entropy_repetition_count_cutoff ( min_entropy_t min_entropy_per_sample ) {
|
||||
double max_repetitions;
|
||||
unsigned int cutoff;
|
||||
|
||||
/* The cutoff formula for the repetition test is:
|
||||
*
|
||||
* C = ( 1 + ( -log2(W) / H_min ) )
|
||||
*
|
||||
* where W is set at 2^(-30) (in ANS X9.82 Part 2 (October
|
||||
* 2011 Draft) Section 8.5.2.1.3.1).
|
||||
*/
|
||||
max_repetitions = ( 1 + ( MIN_ENTROPY ( 30 ) /
|
||||
min_entropy_per_sample ) );
|
||||
|
||||
/* Round up to a whole number of repetitions. We don't have
|
||||
* the ceil() function available, so do the rounding by hand.
|
||||
*/
|
||||
cutoff = max_repetitions;
|
||||
if ( cutoff < max_repetitions )
|
||||
cutoff++;
|
||||
linker_assert ( ( cutoff >= max_repetitions ), rounding_error );
|
||||
|
||||
/* Floating-point operations are not allowed in iPXE since we
|
||||
* never set up a suitable environment. Abort the build
|
||||
* unless the calculated number of repetitions is a
|
||||
* compile-time constant.
|
||||
*/
|
||||
linker_assert ( __builtin_constant_p ( cutoff ),
|
||||
repetition_count_cutoff_not_constant );
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Window size for the adaptive proportion test
|
||||
*
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.1 allows
|
||||
* five possible window sizes: 16, 64, 256, 4096 and 65536.
|
||||
*
|
||||
* We expect to generate relatively few (<256) entropy samples during
|
||||
* a typical iPXE run; the use of a large window size would mean that
|
||||
* the test would never complete a single cycle. We use a window size
|
||||
* of 64, which is the smallest window size that permits values of
|
||||
* H_min down to one bit per sample.
|
||||
*/
|
||||
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
|
||||
|
||||
/**
|
||||
* Combine adaptive proportion test window size and min-entropy
|
||||
*
|
||||
* @v n N (window size)
|
||||
* @v h H (min-entropy)
|
||||
* @ret n_h (N,H) combined value
|
||||
*/
|
||||
#define APC_N_H( n, h ) ( ( (n) << 8 ) | (h) )
|
||||
|
||||
/**
|
||||
* Define a row of the adaptive proportion cutoff table
|
||||
*
|
||||
* @v h H (min-entropy)
|
||||
* @v c16 Cutoff for N=16
|
||||
* @v c64 Cutoff for N=64
|
||||
* @v c256 Cutoff for N=256
|
||||
* @v c4096 Cutoff for N=4096
|
||||
* @v c65536 Cutoff for N=65536
|
||||
*/
|
||||
#define APC_TABLE_ROW( h, c16, c64, c256, c4096, c65536) \
|
||||
case APC_N_H ( 16, h ) : return c16; \
|
||||
case APC_N_H ( 64, h ) : return c64; \
|
||||
case APC_N_H ( 256, h ) : return c256; \
|
||||
case APC_N_H ( 4096, h ) : return c4096; \
|
||||
case APC_N_H ( 65536, h ) : return c65536;
|
||||
|
||||
/** Value used to represent "N/A" in adaptive proportion cutoff table */
|
||||
#define APC_NA 0
|
||||
|
||||
/**
|
||||
* Look up value in adaptive proportion test cutoff table
|
||||
*
|
||||
* @v n N (window size)
|
||||
* @v h H (min-entropy)
|
||||
* @ret cutoff Cutoff
|
||||
*
|
||||
* This is the table of cutoff values defined in ANS X9.82 Part 2
|
||||
* (October 2011 Draft) Section 8.5.2.1.3.1.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
entropy_adaptive_proportion_cutoff_lookup ( unsigned int n, unsigned int h ) {
|
||||
switch ( APC_N_H ( n, h ) ) {
|
||||
APC_TABLE_ROW ( 1, APC_NA, 51, 168, 2240, 33537 );
|
||||
APC_TABLE_ROW ( 2, APC_NA, 35, 100, 1193, 17053 );
|
||||
APC_TABLE_ROW ( 3, 10, 24, 61, 643, 8705 );
|
||||
APC_TABLE_ROW ( 4, 8, 16, 38, 354, 4473 );
|
||||
APC_TABLE_ROW ( 5, 6, 12, 25, 200, 2321 );
|
||||
APC_TABLE_ROW ( 6, 5, 9, 17, 117, 1220 );
|
||||
APC_TABLE_ROW ( 7, 4, 7, 15, 71, 653 );
|
||||
APC_TABLE_ROW ( 8, 4, 5, 9, 45, 358 );
|
||||
APC_TABLE_ROW ( 9, 3, 4, 7, 30, 202 );
|
||||
APC_TABLE_ROW ( 10, 3, 4, 5, 21, 118 );
|
||||
APC_TABLE_ROW ( 11, 2, 3, 4, 15, 71 );
|
||||
APC_TABLE_ROW ( 12, 2, 3, 4, 11, 45 );
|
||||
APC_TABLE_ROW ( 13, 2, 2, 3, 9, 30 );
|
||||
APC_TABLE_ROW ( 14, 2, 2, 3, 7, 21 );
|
||||
APC_TABLE_ROW ( 15, 1, 2, 2, 6, 15 );
|
||||
APC_TABLE_ROW ( 16, 1, 2, 2, 5, 11 );
|
||||
APC_TABLE_ROW ( 17, 1, 1, 2, 4, 9 );
|
||||
APC_TABLE_ROW ( 18, 1, 1, 2, 4, 7 );
|
||||
APC_TABLE_ROW ( 19, 1, 1, 1, 3, 6 );
|
||||
APC_TABLE_ROW ( 20, 1, 1, 1, 3, 5 );
|
||||
default:
|
||||
return APC_NA;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate cutoff value for the adaptive proportion test
|
||||
*
|
||||
* @v min_entropy_per_sample Min-entropy per sample
|
||||
* @ret cutoff Cutoff value
|
||||
*
|
||||
* This is the cutoff value for the Adaptive Proportion Test defined
|
||||
* in ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.3.1.2.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
entropy_adaptive_proportion_cutoff ( min_entropy_t min_entropy_per_sample ) {
|
||||
unsigned int h;
|
||||
unsigned int n;
|
||||
unsigned int cutoff;
|
||||
|
||||
/* Look up cutoff value in cutoff table */
|
||||
n = ADAPTIVE_PROPORTION_WINDOW_SIZE;
|
||||
h = ( min_entropy_per_sample / MIN_ENTROPY_SCALE );
|
||||
cutoff = entropy_adaptive_proportion_cutoff_lookup ( n, h );
|
||||
|
||||
/* Fail unless cutoff value is a compile-time constant */
|
||||
linker_assert ( __builtin_constant_p ( cutoff ),
|
||||
adaptive_proportion_cutoff_not_constant );
|
||||
|
||||
/* Fail if cutoff value is N/A */
|
||||
linker_assert ( ( cutoff != APC_NA ),
|
||||
adaptive_proportion_cutoff_not_applicable );
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate number of samples required for startup tests
|
||||
*
|
||||
* @v repetition_count_cutoff Repetition count test cutoff value
|
||||
* @v adaptive_proportion_cutoff Adaptive proportion test cutoff value
|
||||
* @ret num_samples Number of samples required
|
||||
*
|
||||
* ANS X9.82 Part 2 (October 2011 Draft) Section 8.5.2.1.5 requires
|
||||
* that at least one full cycle of the continuous tests must be
|
||||
* performed at start-up.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
entropy_startup_test_count ( unsigned int repetition_count_cutoff,
|
||||
unsigned int adaptive_proportion_cutoff ) {
|
||||
unsigned int num_samples;
|
||||
|
||||
/* At least max(N,C) samples shall be generated by the noise
|
||||
* source for start-up testing.
|
||||
*/
|
||||
num_samples = repetition_count_cutoff;
|
||||
if ( num_samples < adaptive_proportion_cutoff )
|
||||
num_samples = adaptive_proportion_cutoff;
|
||||
linker_assert ( __builtin_constant_p ( num_samples ),
|
||||
startup_test_count_not_constant );
|
||||
|
||||
return num_samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise entropy source
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @v min_entropy_per_sample Min-entropy per sample
|
||||
*
|
||||
* The cutoff value calculations for the repetition count test and the
|
||||
* adaptive proportion test are provided as static inline functions
|
||||
* since the results will always be compile-time constants.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) void
|
||||
entropy_init ( struct entropy_source *source,
|
||||
min_entropy_t min_entropy_per_sample ) {
|
||||
unsigned int repetition_count_cutoff;
|
||||
unsigned int adaptive_proportion_cutoff;
|
||||
unsigned int startup_test_count;
|
||||
|
||||
/* Sanity check */
|
||||
linker_assert ( min_entropy_per_sample > MIN_ENTROPY ( 0 ),
|
||||
min_entropy_per_sample_is_zero );
|
||||
linker_assert ( ( min_entropy_per_sample <=
|
||||
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
|
||||
min_entropy_per_sample_is_impossibly_high );
|
||||
|
||||
/* Calculate test cutoff values */
|
||||
repetition_count_cutoff =
|
||||
entropy_repetition_count_cutoff ( min_entropy_per_sample );
|
||||
adaptive_proportion_cutoff =
|
||||
entropy_adaptive_proportion_cutoff ( min_entropy_per_sample );
|
||||
startup_test_count =
|
||||
entropy_startup_test_count ( repetition_count_cutoff,
|
||||
adaptive_proportion_cutoff );
|
||||
|
||||
/* Record min-entropy per sample and test cutoff values */
|
||||
source->min_entropy_per_sample = min_entropy_per_sample;
|
||||
source->repetition_count_test.cutoff = repetition_count_cutoff;
|
||||
source->adaptive_proportion_test.cutoff = adaptive_proportion_cutoff;
|
||||
source->startup_test.count = startup_test_count;
|
||||
}
|
||||
|
||||
extern int entropy_enable ( struct entropy_source *source );
|
||||
extern void entropy_disable ( struct entropy_source *source );
|
||||
extern int get_noise ( struct entropy_source *source, noise_sample_t *noise );
|
||||
|
||||
#endif /* _IPXE_ENTROPY_H */
|
||||
|
||||
@ -403,7 +403,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_pci_cmd ( ERRFILE_OTHER | 0x00590000 )
|
||||
#define ERRFILE_dhe ( ERRFILE_OTHER | 0x005a0000 )
|
||||
#define ERRFILE_efi_cmdline ( ERRFILE_OTHER | 0x005b0000 )
|
||||
#define ERRFILE_efi_rng ( ERRFILE_OTHER | 0x005c0000 )
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -22,15 +22,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
/** Default iSCSI port */
|
||||
#define ISCSI_PORT 3260
|
||||
|
||||
/** Default iSCSI first burst length */
|
||||
#define ISCSI_FIRST_BURST_LEN 65536
|
||||
|
||||
/** Default iSCSI maximum burst length */
|
||||
#define ISCSI_MAX_BURST_LEN 262144
|
||||
|
||||
/** Default iSCSI maximum receive data segment length */
|
||||
#define ISCSI_MAX_RECV_DATA_SEG_LEN 8192
|
||||
|
||||
/**
|
||||
* iSCSI segment lengths
|
||||
*
|
||||
@ -586,9 +577,6 @@ struct iscsi_session {
|
||||
/** CHAP response (used for both initiator and target auth) */
|
||||
struct chap_response chap;
|
||||
|
||||
/** Maximum burst length */
|
||||
size_t max_burst_len;
|
||||
|
||||
/** Initiator session ID (IANA format) qualifier
|
||||
*
|
||||
* This is part of the ISID. It is generated randomly
|
||||
|
||||
34
src/include/ipxe/linux/linux_entropy.h
Normal file
34
src/include/ipxe/linux/linux_entropy.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef _IPXE_LINUX_ENTROPY_H
|
||||
#define _IPXE_LINUX_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* /dev/random-based entropy source
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#ifdef ENTROPY_LINUX
|
||||
#define ENTROPY_PREFIX_linux
|
||||
#else
|
||||
#define ENTROPY_PREFIX_linux __linux_
|
||||
#endif
|
||||
|
||||
/**
|
||||
* min-entropy per sample
|
||||
*
|
||||
* @ret min_entropy min-entropy of each sample
|
||||
*/
|
||||
static inline __always_inline min_entropy_t
|
||||
ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) {
|
||||
|
||||
/* linux_get_noise() reads a single byte from /dev/random,
|
||||
* which is supposed to block until a sufficient amount of
|
||||
* entropy is available. We therefore assume that each sample
|
||||
* contains exactly 8 bits of entropy.
|
||||
*/
|
||||
return MIN_ENTROPY ( 8.0 );
|
||||
}
|
||||
|
||||
#endif /* _IPXE_LINUX_ENTROPY_H */
|
||||
52
src/include/ipxe/null_entropy.h
Normal file
52
src/include/ipxe/null_entropy.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef _IPXE_NULL_ENTROPY_H
|
||||
#define _IPXE_NULL_ENTROPY_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Nonexistent entropy source
|
||||
*
|
||||
* This source provides no entropy and must NOT be used in a
|
||||
* security-sensitive environment.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef ENTROPY_NULL
|
||||
#define ENTROPY_PREFIX_null
|
||||
#else
|
||||
#define ENTROPY_PREFIX_null __null_
|
||||
#endif
|
||||
|
||||
static inline __always_inline int
|
||||
ENTROPY_INLINE ( null, entropy_enable ) ( void ) {
|
||||
/* Do nothing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline __always_inline void
|
||||
ENTROPY_INLINE ( null, entropy_disable ) ( void ) {
|
||||
/* Do nothing */
|
||||
}
|
||||
|
||||
static inline __always_inline min_entropy_t
|
||||
ENTROPY_INLINE ( null, min_entropy_per_sample ) ( void ) {
|
||||
/* Actual amount of min-entropy is zero. To avoid
|
||||
* division-by-zero errors and to allow compilation of
|
||||
* entropy-consuming code, pretend to have 1 bit of entropy in
|
||||
* each sample.
|
||||
*/
|
||||
return MIN_ENTROPY ( 1.0 );
|
||||
}
|
||||
|
||||
static inline __always_inline int
|
||||
ENTROPY_INLINE ( null, get_noise ) ( noise_sample_t *noise ) {
|
||||
|
||||
/* All sample values are constant */
|
||||
*noise = 0x01;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _IPXE_NULL_ENTROPY_H */
|
||||
@ -383,9 +383,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
#define for_each_table_entry( pointer, table ) \
|
||||
for ( (pointer) = table_start ( table ) ; \
|
||||
(pointer) < table_end ( table ) ; \
|
||||
(pointer)++ )
|
||||
for ( pointer = table_start ( table ) ; \
|
||||
pointer < table_end ( table ) ; \
|
||||
pointer++ )
|
||||
|
||||
/**
|
||||
* Iterate through all remaining entries within a linker table
|
||||
@ -412,9 +412,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
#define for_each_table_entry_continue( pointer, table ) \
|
||||
for ( (pointer)++ ; \
|
||||
(pointer) < table_end ( table ) ; \
|
||||
(pointer)++ )
|
||||
for ( pointer++ ; \
|
||||
pointer < table_end ( table ) ; \
|
||||
pointer++ )
|
||||
|
||||
/**
|
||||
* Iterate through all entries within a linker table in reverse order
|
||||
@ -438,9 +438,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
#define for_each_table_entry_reverse( pointer, table ) \
|
||||
for ( (pointer) = ( table_end ( table ) - 1 ) ; \
|
||||
(pointer) >= table_start ( table ) ; \
|
||||
(pointer)-- )
|
||||
for ( pointer = ( table_end ( table ) - 1 ) ; \
|
||||
pointer >= table_start ( table ) ; \
|
||||
pointer-- )
|
||||
|
||||
/**
|
||||
* Iterate through all remaining entries within a linker table in reverse order
|
||||
@ -467,8 +467,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
#define for_each_table_entry_continue_reverse( pointer, table ) \
|
||||
for ( (pointer)-- ; \
|
||||
(pointer) >= table_start ( table ) ; \
|
||||
(pointer)-- )
|
||||
for ( pointer-- ; \
|
||||
pointer >= table_start ( table ) ; \
|
||||
pointer-- )
|
||||
|
||||
#endif /* _IPXE_TABLES_H */
|
||||
|
||||
@ -25,8 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <errno.h>
|
||||
#include <ipxe/entropy.h>
|
||||
#include <ipxe/crc32.h>
|
||||
#include <ipxe/profile.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/Protocol/Rng.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
@ -34,7 +36,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
|
||||
struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK );
|
||||
/** Random number generator protocol */
|
||||
static EFI_RNG_PROTOCOL *efirng;
|
||||
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
|
||||
|
||||
/** Minimum number of bytes to request from RNG
|
||||
*
|
||||
* The UEFI spec states (for no apparently good reason) that "When a
|
||||
* Deterministic Random Bit Generator (DRBG) is used on the output of
|
||||
* a (raw) entropy source, its security level must be at least 256
|
||||
* bits." The EDK2 codebase (mis)interprets this to mean that the
|
||||
* call to GetRNG() should fail if given a buffer less than 32 bytes.
|
||||
*
|
||||
* Incidentally, nothing in the EFI RNG protocol provides any way to
|
||||
* report the actual amount of entropy returned by GetRNG().
|
||||
*/
|
||||
#define EFI_ENTROPY_RNG_LEN 32
|
||||
|
||||
/** Time (in 100ns units) to delay waiting for timer tick
|
||||
*
|
||||
@ -59,6 +76,9 @@ static int efi_entropy_enable ( void ) {
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
DBGC ( &tick, "ENTROPY %s RNG protocol\n",
|
||||
( efirng ? "has" : "has no" ) );
|
||||
|
||||
/* Drop to external TPL to allow timer tick event to take place */
|
||||
bs->RestoreTPL ( efi_external_tpl );
|
||||
|
||||
@ -71,12 +91,6 @@ static int efi_entropy_enable ( void ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* We use essentially the same mechanism as for the BIOS
|
||||
* RTC-based entropy source, and so assume the same
|
||||
* min-entropy per sample.
|
||||
*/
|
||||
entropy_init ( &efitick_entropy, MIN_ENTROPY ( 1.3 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -133,7 +147,7 @@ static int efi_entropy_tick ( void ) {
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_get_noise ( noise_sample_t *noise ) {
|
||||
static int efi_get_noise_ticks ( noise_sample_t *noise ) {
|
||||
int before;
|
||||
int after;
|
||||
int rc;
|
||||
@ -158,10 +172,70 @@ static int efi_get_noise ( noise_sample_t *noise ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** EFI entropy source */
|
||||
struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ) = {
|
||||
.name = "efitick",
|
||||
.enable = efi_entropy_enable,
|
||||
.disable = efi_entropy_disable,
|
||||
.get_noise = efi_get_noise,
|
||||
};
|
||||
/**
|
||||
* Get noise sample from RNG protocol
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_get_noise_rng ( noise_sample_t *noise ) {
|
||||
static uint8_t prev[EFI_ENTROPY_RNG_LEN];
|
||||
uint8_t buf[EFI_ENTROPY_RNG_LEN];
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Fail if we have no EFI RNG protocol */
|
||||
if ( ! efirng )
|
||||
return -ENOTSUP;
|
||||
|
||||
/* Get the minimum allowed number of random bytes */
|
||||
if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN,
|
||||
buf ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &tick, "ENTROPY could not read from RNG: %s\n",
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Fail (and permanently disable the EFI RNG) if we get
|
||||
* consecutive identical results.
|
||||
*/
|
||||
if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) {
|
||||
DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" );
|
||||
DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) );
|
||||
efirng = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
memcpy ( prev, buf, sizeof ( prev ) );
|
||||
|
||||
/* Reduce random bytes to a single noise sample. This seems
|
||||
* like overkill, but we have no way of knowing how much
|
||||
* entropy is actually present in the bytes returned by the
|
||||
* RNG protocol.
|
||||
*/
|
||||
*noise = crc32_le ( 0, buf, sizeof ( buf ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_get_noise ( noise_sample_t *noise ) {
|
||||
int rc;
|
||||
|
||||
/* Try RNG first, falling back to timer ticks */
|
||||
if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) &&
|
||||
( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample );
|
||||
PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable );
|
||||
PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable );
|
||||
PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise );
|
||||
|
||||
@ -43,21 +43,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
|
||||
#include <ipxe/efi/Protocol/BlockIo.h>
|
||||
#include <ipxe/efi/Protocol/DiskIo.h>
|
||||
#include <ipxe/efi/Protocol/LoadFile2.h>
|
||||
#include <ipxe/efi/Guid/FileInfo.h>
|
||||
#include <ipxe/efi/Guid/FileSystemInfo.h>
|
||||
#include <ipxe/efi/efi_strings.h>
|
||||
#include <ipxe/efi/efi_path.h>
|
||||
#include <ipxe/efi/efi_file.h>
|
||||
|
||||
/** EFI media ID */
|
||||
#define EFI_MEDIA_ID_MAGIC 0x69505845
|
||||
|
||||
/** Linux initrd fixed device path vendor GUID */
|
||||
#define LINUX_INITRD_VENDOR_GUID \
|
||||
{ 0x5568e427, 0x68fc, 0x4f3d, \
|
||||
{ 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } }
|
||||
|
||||
/** An EFI virtual file reader */
|
||||
struct efi_file_reader {
|
||||
/** EFI file */
|
||||
@ -76,8 +69,6 @@ struct efi_file {
|
||||
struct refcnt refcnt;
|
||||
/** EFI file protocol */
|
||||
EFI_FILE_PROTOCOL file;
|
||||
/** EFI load file protocol */
|
||||
EFI_LOAD_FILE2_PROTOCOL load;
|
||||
/** Image (if any) */
|
||||
struct image *image;
|
||||
/** Filename */
|
||||
@ -379,8 +370,6 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
|
||||
ref_init ( &file->refcnt, efi_file_free );
|
||||
memcpy ( &new_file->file, &efi_file_root.file,
|
||||
sizeof ( new_file->file ) );
|
||||
memcpy ( &new_file->load, &efi_file_root.load,
|
||||
sizeof ( new_file->load ) );
|
||||
efi_file_image ( new_file, image_get ( image ) );
|
||||
*new = &new_file->file;
|
||||
DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) );
|
||||
@ -539,7 +528,7 @@ static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this,
|
||||
|
||||
/* Read from the file */
|
||||
DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n",
|
||||
efi_file_name ( file ), pos, ( ( size_t ) ( pos + *len ) ) );
|
||||
efi_file_name ( file ), pos, file->pos );
|
||||
*len = file->read ( &reader );
|
||||
assert ( ( pos + *len ) == file->pos );
|
||||
|
||||
@ -695,44 +684,6 @@ static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load file
|
||||
*
|
||||
* @v this EFI file loader
|
||||
* @v path File path
|
||||
* @v boot Boot policy
|
||||
* @v len Buffer size
|
||||
* @v data Buffer, or NULL
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFI_STATUS EFIAPI efi_file_load ( EFI_LOAD_FILE2_PROTOCOL *this,
|
||||
EFI_DEVICE_PATH_PROTOCOL *path __unused,
|
||||
BOOLEAN boot __unused, UINTN *len,
|
||||
VOID *data ) {
|
||||
struct efi_file *file = container_of ( this, struct efi_file, load );
|
||||
size_t max_len;
|
||||
size_t file_len;
|
||||
EFI_STATUS efirc;
|
||||
|
||||
/* Calculate maximum length */
|
||||
max_len = ( data ? *len : 0 );
|
||||
DBGC ( file, "EFIFILE %s load at %p+%#zx\n",
|
||||
efi_file_name ( file ), data, max_len );
|
||||
|
||||
/* Check buffer size */
|
||||
file_len = efi_file_len ( file );
|
||||
if ( file_len > max_len ) {
|
||||
*len = file_len;
|
||||
return EFI_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* Read from file */
|
||||
if ( ( efirc = efi_file_read ( &file->file, len, data ) ) != 0 )
|
||||
return efirc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Root directory */
|
||||
static struct efi_file efi_file_root = {
|
||||
.refcnt = REF_INIT ( ref_no_free ),
|
||||
@ -749,9 +700,6 @@ static struct efi_file efi_file_root = {
|
||||
.SetInfo = efi_file_set_info,
|
||||
.Flush = efi_file_flush,
|
||||
},
|
||||
.load = {
|
||||
.LoadFile = efi_file_load,
|
||||
},
|
||||
.image = NULL,
|
||||
.name = "",
|
||||
};
|
||||
@ -772,34 +720,11 @@ static struct efi_file efi_file_initrd = {
|
||||
.SetInfo = efi_file_set_info,
|
||||
.Flush = efi_file_flush,
|
||||
},
|
||||
.load = {
|
||||
.LoadFile = efi_file_load,
|
||||
},
|
||||
.image = NULL,
|
||||
.name = "initrd.magic",
|
||||
.read = efi_file_read_initrd,
|
||||
};
|
||||
|
||||
/** Linux initrd fixed device path */
|
||||
static struct {
|
||||
VENDOR_DEVICE_PATH vendor;
|
||||
EFI_DEVICE_PATH_PROTOCOL end;
|
||||
} __attribute__ (( packed )) efi_file_initrd_path = {
|
||||
.vendor = {
|
||||
.Header = {
|
||||
.Type = MEDIA_DEVICE_PATH,
|
||||
.SubType = MEDIA_VENDOR_DP,
|
||||
.Length[0] = sizeof ( efi_file_initrd_path.vendor ),
|
||||
},
|
||||
.Guid = LINUX_INITRD_VENDOR_GUID,
|
||||
},
|
||||
.end = {
|
||||
.Type = END_DEVICE_PATH_TYPE,
|
||||
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
||||
.Length[0] = sizeof ( efi_file_initrd_path.end ),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Open root directory
|
||||
*
|
||||
@ -908,110 +833,6 @@ static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = {
|
||||
.WriteDisk = efi_disk_io_write_disk,
|
||||
};
|
||||
|
||||
/**
|
||||
* (Re)install fixed device path file
|
||||
*
|
||||
* @v path Device path
|
||||
* @v load Load file protocol, or NULL to uninstall protocol
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* Linux 5.7 added the ability to autodetect an initrd by searching
|
||||
* for a handle via a fixed vendor-specific "Linux initrd device path"
|
||||
* and then locating and using the EFI_LOAD_FILE2_PROTOCOL instance on
|
||||
* that handle.
|
||||
*
|
||||
* The design choice in Linux of using a single fixed device path
|
||||
* makes this unfortunately messy to support, since device paths must
|
||||
* be unique within a system. When multiple bootloaders are used
|
||||
* (e.g. GRUB loading iPXE loading Linux) then only one bootloader can
|
||||
* ever install the device path onto a handle. Subsequent bootloaders
|
||||
* must locate the existing handle and replace the load file protocol
|
||||
* instance with their own.
|
||||
*/
|
||||
static int efi_file_path_install ( EFI_DEVICE_PATH_PROTOCOL *path,
|
||||
EFI_LOAD_FILE2_PROTOCOL *load ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
EFI_HANDLE handle;
|
||||
VOID *path_copy;
|
||||
VOID *old;
|
||||
size_t path_len;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Locate or install the handle with this device path */
|
||||
end = path;
|
||||
if ( ( ( efirc = bs->LocateDevicePath ( &efi_device_path_protocol_guid,
|
||||
&end, &handle ) ) == 0 ) &&
|
||||
( end->Type == END_DEVICE_PATH_TYPE ) ) {
|
||||
|
||||
/* Exact match: reuse (or uninstall from) this handle */
|
||||
if ( load ) {
|
||||
DBGC ( path, "EFIFILE %s reusing existing handle\n",
|
||||
efi_devpath_text ( path ) );
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* Allocate a permanent copy of the device path, since
|
||||
* this handle will survive after this binary is
|
||||
* unloaded.
|
||||
*/
|
||||
path_len = ( efi_path_len ( path ) + sizeof ( *end ) );
|
||||
if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, path_len,
|
||||
&path_copy ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( path, "EFIFILE %s could not allocate device path: "
|
||||
"%s\n", efi_devpath_text ( path ), strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
memcpy ( path_copy, path, path_len );
|
||||
|
||||
/* Create a new handle with this device path */
|
||||
handle = NULL;
|
||||
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
||||
&handle,
|
||||
&efi_device_path_protocol_guid, path_copy,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( path, "EFIFILE %s could not create handle: %s\n",
|
||||
efi_devpath_text ( path ), strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Uninstall existing load file protocol instance, if any */
|
||||
if ( ( ( efirc = bs->HandleProtocol ( handle, &efi_load_file2_protocol_guid,
|
||||
&old ) ) == 0 ) &&
|
||||
( ( efirc = bs->UninstallMultipleProtocolInterfaces (
|
||||
handle,
|
||||
&efi_load_file2_protocol_guid, old,
|
||||
NULL ) ) != 0 ) ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( path, "EFIFILE %s could not uninstall %s: %s\n",
|
||||
efi_devpath_text ( path ),
|
||||
efi_guid_ntoa ( &efi_load_file2_protocol_guid ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Install new load file protocol instance, if applicable */
|
||||
if ( ( load != NULL ) &&
|
||||
( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
||||
&handle,
|
||||
&efi_load_file2_protocol_guid, load,
|
||||
NULL ) ) != 0 ) ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( path, "EFIFILE %s could not install %s: %s\n",
|
||||
efi_devpath_text ( path ),
|
||||
efi_guid_ntoa ( &efi_load_file2_protocol_guid ),
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install EFI simple file system protocol
|
||||
*
|
||||
@ -1020,7 +841,6 @@ static int efi_file_path_install ( EFI_DEVICE_PATH_PROTOCOL *path,
|
||||
*/
|
||||
int efi_file_install ( EFI_HANDLE handle ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_LOAD_FILE2_PROTOCOL *load;
|
||||
union {
|
||||
EFI_DISK_IO_PROTOCOL *diskio;
|
||||
void *interface;
|
||||
@ -1083,24 +903,8 @@ int efi_file_install ( EFI_HANDLE handle ) {
|
||||
}
|
||||
assert ( diskio.diskio == &efi_disk_io_protocol );
|
||||
|
||||
/* Install Linux initrd fixed device path file
|
||||
*
|
||||
* Install the device path handle unconditionally, since we
|
||||
* are definitively the bootloader providing the initrd, if
|
||||
* any, to the booted image. Install the load file protocol
|
||||
* instance only if the initrd is non-empty, since Linux does
|
||||
* not gracefully handle a zero-length initrd.
|
||||
*/
|
||||
load = ( list_is_singular ( &images ) ? NULL : &efi_file_initrd.load );
|
||||
if ( ( rc = efi_file_path_install ( &efi_file_initrd_path.vendor.Header,
|
||||
load ) ) != 0 ) {
|
||||
goto err_initrd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
efi_file_path_install ( &efi_file_initrd_path.vendor.Header, NULL );
|
||||
err_initrd:
|
||||
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
|
||||
efi_image_handle, handle );
|
||||
err_open:
|
||||
@ -1126,9 +930,6 @@ void efi_file_uninstall ( EFI_HANDLE handle ) {
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Uninstall Linux initrd fixed device path file */
|
||||
efi_file_path_install ( &efi_file_initrd_path.vendor.Header, NULL );
|
||||
|
||||
/* Close our own disk I/O protocol */
|
||||
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
|
||||
efi_image_handle, handle );
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* 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 (at your option) 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 );
|
||||
|
||||
#include <errno.h>
|
||||
#include <ipxe/entropy.h>
|
||||
#include <ipxe/crc32.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/Protocol/Rng.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI random number generator protocol entropy source
|
||||
*
|
||||
*/
|
||||
|
||||
struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL );
|
||||
|
||||
/** Random number generator protocol */
|
||||
static EFI_RNG_PROTOCOL *efirng;
|
||||
EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng );
|
||||
|
||||
/** Minimum number of bytes to request from RNG
|
||||
*
|
||||
* The UEFI spec states (for no apparently good reason) that "When a
|
||||
* Deterministic Random Bit Generator (DRBG) is used on the output of
|
||||
* a (raw) entropy source, its security level must be at least 256
|
||||
* bits." The EDK2 codebase (mis)interprets this to mean that the
|
||||
* call to GetRNG() should fail if given a buffer less than 32 bytes.
|
||||
*
|
||||
* Incidentally, nothing in the EFI RNG protocol provides any way to
|
||||
* report the actual amount of entropy returned by GetRNG().
|
||||
*/
|
||||
#define EFIRNG_LEN 32
|
||||
|
||||
/**
|
||||
* Enable entropy gathering
|
||||
*
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efirng_enable ( void ) {
|
||||
|
||||
/* Check for RNG protocol support */
|
||||
if ( ! efirng ) {
|
||||
DBGC ( &efirng, "EFIRNG has no RNG protocol\n" );
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Nothing in the EFI specification provides any clue as to
|
||||
* how much entropy will be returned by GetRNG(). Make a
|
||||
* totally uninformed (and conservative guess) that each
|
||||
* sample will contain at least one bit of entropy.
|
||||
*/
|
||||
entropy_init ( &efirng_entropy, MIN_ENTROPY ( 1.0 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get noise sample from RNG protocol
|
||||
*
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efirng_get_noise ( noise_sample_t *noise ) {
|
||||
uint8_t buf[EFIRNG_LEN];
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( efirng != NULL );
|
||||
|
||||
/* Get the minimum allowed number of random bytes */
|
||||
if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ),
|
||||
buf ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &efirng, "ENTROPY could not read from RNG: %s\n",
|
||||
strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Reduce random bytes to a single noise sample. This seems
|
||||
* like overkill, but we have no way of knowing how much
|
||||
* entropy is actually present in the bytes returned by the
|
||||
* RNG protocol.
|
||||
*/
|
||||
*noise = crc32_le ( 0, buf, sizeof ( buf ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** EFI random number generator protocol entropy source */
|
||||
struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ) = {
|
||||
.name = "efirng",
|
||||
.enable = efirng_enable,
|
||||
.get_noise = efirng_get_noise,
|
||||
};
|
||||
@ -34,8 +34,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/linux_api.h>
|
||||
#include <ipxe/entropy.h>
|
||||
|
||||
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL );
|
||||
|
||||
/** Entropy source filename */
|
||||
static const char entropy_filename[] = "/dev/random";
|
||||
|
||||
@ -57,13 +55,6 @@ static int linux_entropy_enable ( void ) {
|
||||
return entropy_fd;
|
||||
}
|
||||
|
||||
/* linux_get_noise() reads a single byte from /dev/random,
|
||||
* which is supposed to block until a sufficient amount of
|
||||
* entropy is available. We therefore assume that each sample
|
||||
* contains exactly 8 bits of entropy.
|
||||
*/
|
||||
entropy_init ( &linux_entropy, MIN_ENTROPY ( 8.0 ) );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -104,10 +95,7 @@ static int linux_get_noise ( noise_sample_t *noise ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Linux entropy source */
|
||||
struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ) = {
|
||||
.name = "linux",
|
||||
.enable = linux_entropy_enable,
|
||||
.disable = linux_entropy_disable,
|
||||
.get_noise = linux_get_noise,
|
||||
};
|
||||
PROVIDE_ENTROPY_INLINE ( linux, min_entropy_per_sample );
|
||||
PROVIDE_ENTROPY ( linux, entropy_enable, linux_entropy_enable );
|
||||
PROVIDE_ENTROPY ( linux, entropy_disable, linux_entropy_disable );
|
||||
PROVIDE_ENTROPY ( linux, get_noise, linux_get_noise );
|
||||
|
||||
@ -46,7 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/base16.h>
|
||||
#include <ipxe/base64.h>
|
||||
#include <ipxe/ibft.h>
|
||||
#include <ipxe/blockdev.h>
|
||||
#include <ipxe/efi/efi_path.h>
|
||||
#include <ipxe/iscsi.h>
|
||||
|
||||
@ -87,10 +86,6 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
|
||||
__einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN )
|
||||
#define EINFO_EINVAL_NO_INITIATOR_IQN \
|
||||
__einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )
|
||||
#define EINVAL_MAXBURSTLENGTH \
|
||||
__einfo_error ( EINFO_EINVAL_MAXBURSTLENGTH )
|
||||
#define EINFO_EINVAL_MAXBURSTLENGTH \
|
||||
__einfo_uniqify ( EINFO_EINVAL, 0x06, "Invalid MaxBurstLength" )
|
||||
#define EIO_TARGET_UNAVAILABLE \
|
||||
__einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE )
|
||||
#define EINFO_EIO_TARGET_UNAVAILABLE \
|
||||
@ -286,9 +281,6 @@ static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
|
||||
/* Assign fresh initiator task tag */
|
||||
iscsi_new_itt ( iscsi );
|
||||
|
||||
/* Set default operational parameters */
|
||||
iscsi->max_burst_len = ISCSI_MAX_BURST_LEN;
|
||||
|
||||
/* Initiate login */
|
||||
iscsi_start_login ( iscsi );
|
||||
|
||||
@ -744,20 +736,16 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
||||
"MaxConnections=1%c"
|
||||
"InitialR2T=Yes%c"
|
||||
"ImmediateData=No%c"
|
||||
"MaxRecvDataSegmentLength=%d%c"
|
||||
"MaxBurstLength=%d%c"
|
||||
"FirstBurstLength=%d%c"
|
||||
"MaxRecvDataSegmentLength=8192%c"
|
||||
"MaxBurstLength=262144%c"
|
||||
"FirstBurstLength=65536%c"
|
||||
"DefaultTime2Wait=0%c"
|
||||
"DefaultTime2Retain=0%c"
|
||||
"MaxOutstandingR2T=1%c"
|
||||
"DataPDUInOrder=Yes%c"
|
||||
"DataSequenceInOrder=Yes%c"
|
||||
"ErrorRecoveryLevel=0%c",
|
||||
0, 0, 0, 0, 0,
|
||||
ISCSI_MAX_RECV_DATA_SEG_LEN, 0,
|
||||
ISCSI_MAX_BURST_LEN, 0,
|
||||
ISCSI_FIRST_BURST_LEN, 0,
|
||||
0, 0, 0, 0, 0, 0 );
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
return used;
|
||||
@ -920,31 +908,6 @@ static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle iSCSI MaxBurstLength text value
|
||||
*
|
||||
* @v iscsi iSCSI session
|
||||
* @v value MaxBurstLength value
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int iscsi_handle_maxburstlength_value ( struct iscsi_session *iscsi,
|
||||
const char *value ) {
|
||||
unsigned long max_burst_len;
|
||||
char *end;
|
||||
|
||||
/* Update maximum burst length */
|
||||
max_burst_len = strtoul ( value, &end, 0 );
|
||||
if ( *end ) {
|
||||
DBGC ( iscsi, "iSCSI %p invalid MaxBurstLength \"%s\"\n",
|
||||
iscsi, value );
|
||||
return -EINVAL_MAXBURSTLENGTH;
|
||||
}
|
||||
if ( max_burst_len < iscsi->max_burst_len )
|
||||
iscsi->max_burst_len = max_burst_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle iSCSI CHAP_A text value
|
||||
*
|
||||
@ -1185,7 +1148,6 @@ struct iscsi_string_type {
|
||||
/** iSCSI text strings that we want to handle */
|
||||
static struct iscsi_string_type iscsi_string_types[] = {
|
||||
{ "TargetAddress", iscsi_handle_targetaddress_value },
|
||||
{ "MaxBurstLength", iscsi_handle_maxburstlength_value },
|
||||
{ "AuthMethod", iscsi_handle_authmethod_value },
|
||||
{ "CHAP_A", iscsi_handle_chap_a_value },
|
||||
{ "CHAP_I", iscsi_handle_chap_i_value },
|
||||
@ -1885,24 +1847,6 @@ static int iscsi_scsi_command ( struct iscsi_session *iscsi,
|
||||
return iscsi->itt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update SCSI block device capacity
|
||||
*
|
||||
* @v iscsi iSCSI session
|
||||
* @v capacity Block device capacity
|
||||
*/
|
||||
static void iscsi_scsi_capacity ( struct iscsi_session *iscsi,
|
||||
struct block_device_capacity *capacity ) {
|
||||
unsigned int max_count;
|
||||
|
||||
/* Limit maximum number of blocks per transfer to fit MaxBurstLength */
|
||||
if ( capacity->blksize ) {
|
||||
max_count = ( iscsi->max_burst_len / capacity->blksize );
|
||||
if ( max_count < capacity->max_count )
|
||||
capacity->max_count = max_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iSCSI ACPI descriptor
|
||||
*
|
||||
@ -1918,7 +1862,6 @@ static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) {
|
||||
static struct interface_operation iscsi_control_op[] = {
|
||||
INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ),
|
||||
INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ),
|
||||
INTF_OP ( block_capacity, struct iscsi_session *, iscsi_scsi_capacity ),
|
||||
INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
|
||||
INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ),
|
||||
EFI_INTF_OP ( efi_describe, struct iscsi_session *, efi_iscsi_path ),
|
||||
|
||||
@ -42,9 +42,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
/**
|
||||
* Generate entropy samples for external testing
|
||||
*
|
||||
* @v source Entropy source
|
||||
*/
|
||||
static void entropy_sample ( struct entropy_source *source ) {
|
||||
static void entropy_sample_test_exec ( void ) {
|
||||
static noise_sample_t samples[SAMPLE_BLOCKSIZE];
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
@ -54,35 +53,22 @@ static void entropy_sample ( struct entropy_source *source ) {
|
||||
for ( i = 0 ; i < ( SAMPLE_COUNT / SAMPLE_BLOCKSIZE ) ; i++ ) {
|
||||
|
||||
/* Collect one block of samples */
|
||||
rc = entropy_enable ( source );
|
||||
rc = entropy_enable();
|
||||
ok ( rc == 0 );
|
||||
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
|
||||
rc = get_noise ( source, &samples[j] );
|
||||
rc = get_noise ( &samples[j] );
|
||||
ok ( rc == 0 );
|
||||
}
|
||||
entropy_disable ( source );
|
||||
entropy_disable();
|
||||
|
||||
/* Print out sample values */
|
||||
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
|
||||
printf ( "SAMPLE %s %d %d\n", source->name,
|
||||
( i * SAMPLE_BLOCKSIZE + j ), samples[j] );
|
||||
printf ( "SAMPLE %d %d\n", ( i * SAMPLE_BLOCKSIZE + j ),
|
||||
samples[j] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate entropy samples for external testing
|
||||
*
|
||||
*/
|
||||
static void entropy_sample_test_exec ( void ) {
|
||||
struct entropy_source *source;
|
||||
|
||||
/* Test each entropy source */
|
||||
for_each_table_entry ( source, ENTROPY_SOURCES ) {
|
||||
entropy_sample ( source );
|
||||
}
|
||||
}
|
||||
|
||||
/** Entropy sampling self-test */
|
||||
struct self_test entropy_sample_test __self_test = {
|
||||
.name = "entropy_sample",
|
||||
|
||||
Reference in New Issue
Block a user