mirror of
https://gitlab.com/qemu-project/ipxe.git
synced 2025-10-30 07:56:50 +08:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c4a652929c | |||
| d2e1601cf4 | |||
| 95b8338f0d | |||
| 28184b7c22 | |||
| 3c214f0465 | |||
| ce2200d5fb | |||
| c4a8d90387 | |||
| 79d85e29aa | |||
| d27cd8196d | |||
| 03eea19c19 | |||
| 0bb0aea878 | |||
| f9beb20e99 | |||
| f93e6b712f | |||
| 22cc65535a | |||
| bd13697446 | |||
| 9fb28080d9 | |||
| 1e4c3789e9 | |||
| 0d04635ef0 | |||
| 1d1cf74a5e | |||
| aa368ba529 | |||
| 2c6a15d2a3 | |||
| 09e8a15408 | |||
| bf25e23d07 | |||
| 8f1c120119 | |||
| 54fcb7c29c | |||
| 9e1f7a3659 | |||
| e51e7bbad7 | |||
| 523788ccda | |||
| 96bb6ba441 | |||
| 33cb56cf1b | |||
| 60531ff6e2 | |||
| 04e60a278a | |||
| 471599dc77 | |||
| 7d71cf318a | |||
| 6625e49cea | |||
| 9f17d1116d | |||
| 2733c4763a | |||
| cff857461b | |||
| 6a004be0cc | |||
| cf9ad00afc | |||
| 76a286530a | |||
| 3c83843e11 |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -49,7 +49,8 @@ jobs:
|
||||
sudo apt update
|
||||
sudo apt install -y -o Acquire::Retries=50 \
|
||||
mtools syslinux isolinux \
|
||||
libc6-dev-i386 libc6-dbg:i386 valgrind
|
||||
libc6-dev-i386 valgrind \
|
||||
libgcc-s1:i386 libc6-dbg:i386
|
||||
- name: Build (BIOS)
|
||||
run: |
|
||||
make -j 4 -C src
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
#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 */
|
||||
@ -1,12 +0,0 @@
|
||||
#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 */
|
||||
103
src/arch/x86/core/rdrand.c
Normal file
103
src/arch/x86/core/rdrand.c
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
@ -247,19 +247,17 @@ static void bzimage_update_header ( struct image *image,
|
||||
*
|
||||
* @v image bzImage file
|
||||
* @v bzimg bzImage context
|
||||
* @v cmdline Kernel command line
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int bzimage_parse_cmdline ( struct image *image,
|
||||
struct bzimage_context *bzimg,
|
||||
char *cmdline ) {
|
||||
struct bzimage_context *bzimg ) {
|
||||
const char *vga;
|
||||
const char *mem;
|
||||
char *sep;
|
||||
char *vga;
|
||||
char *mem;
|
||||
char *end;
|
||||
|
||||
/* Look for "vga=" */
|
||||
if ( ( vga = strstr ( cmdline, "vga=" ) ) ) {
|
||||
vga += 4;
|
||||
if ( ( vga = image_argument ( image, "vga=" ) ) ) {
|
||||
sep = strchr ( vga, ' ' );
|
||||
if ( sep )
|
||||
*sep = '\0';
|
||||
@ -270,10 +268,10 @@ static int bzimage_parse_cmdline ( struct image *image,
|
||||
} else if ( strcmp ( vga, "ask" ) == 0 ) {
|
||||
bzimg->vid_mode = BZI_VID_MODE_ASK;
|
||||
} else {
|
||||
bzimg->vid_mode = strtoul ( vga, &vga, 0 );
|
||||
if ( *vga ) {
|
||||
bzimg->vid_mode = strtoul ( vga, &end, 0 );
|
||||
if ( *end ) {
|
||||
DBGC ( image, "bzImage %p strange \"vga=\" "
|
||||
"terminator '%c'\n", image, *vga );
|
||||
"terminator '%c'\n", image, *end );
|
||||
}
|
||||
}
|
||||
if ( sep )
|
||||
@ -281,10 +279,9 @@ static int bzimage_parse_cmdline ( struct image *image,
|
||||
}
|
||||
|
||||
/* Look for "mem=" */
|
||||
if ( ( mem = strstr ( cmdline, "mem=" ) ) ) {
|
||||
mem += 4;
|
||||
bzimg->mem_limit = strtoul ( mem, &mem, 0 );
|
||||
switch ( *mem ) {
|
||||
if ( ( mem = image_argument ( image, "mem=" ) ) ) {
|
||||
bzimg->mem_limit = strtoul ( mem, &end, 0 );
|
||||
switch ( *end ) {
|
||||
case 'G':
|
||||
case 'g':
|
||||
bzimg->mem_limit <<= 10;
|
||||
@ -302,7 +299,7 @@ static int bzimage_parse_cmdline ( struct image *image,
|
||||
break;
|
||||
default:
|
||||
DBGC ( image, "bzImage %p strange \"mem=\" "
|
||||
"terminator '%c'\n", image, *mem );
|
||||
"terminator '%c'\n", image, *end );
|
||||
break;
|
||||
}
|
||||
bzimg->mem_limit -= 1;
|
||||
@ -316,11 +313,10 @@ static int bzimage_parse_cmdline ( struct image *image,
|
||||
*
|
||||
* @v image bzImage image
|
||||
* @v bzimg bzImage context
|
||||
* @v cmdline Kernel command line
|
||||
*/
|
||||
static void bzimage_set_cmdline ( struct image *image,
|
||||
struct bzimage_context *bzimg,
|
||||
const char *cmdline ) {
|
||||
struct bzimage_context *bzimg ) {
|
||||
const char *cmdline = ( image->cmdline ? image->cmdline : "" );
|
||||
size_t cmdline_len;
|
||||
|
||||
/* Copy command line down to real-mode portion */
|
||||
@ -359,8 +355,8 @@ static size_t bzimage_load_initrd ( struct image *image,
|
||||
size_t offset;
|
||||
size_t pad_len;
|
||||
|
||||
/* Do not include kernel image itself as an initrd */
|
||||
if ( initrd == image )
|
||||
/* Skip hidden images */
|
||||
if ( initrd->flags & IMAGE_HIDDEN )
|
||||
return 0;
|
||||
|
||||
/* Create cpio header for non-prebuilt images */
|
||||
@ -410,10 +406,6 @@ static int bzimage_check_initrds ( struct image *image,
|
||||
/* Calculate total loaded length of initrds */
|
||||
for_each_image ( initrd ) {
|
||||
|
||||
/* Skip kernel */
|
||||
if ( initrd == image )
|
||||
continue;
|
||||
|
||||
/* Calculate length */
|
||||
len += bzimage_load_initrd ( image, initrd, UNULL );
|
||||
len = bzimage_align ( len );
|
||||
@ -528,7 +520,6 @@ static void bzimage_load_initrds ( struct image *image,
|
||||
*/
|
||||
static int bzimage_exec ( struct image *image ) {
|
||||
struct bzimage_context bzimg;
|
||||
char *cmdline = ( image->cmdline ? image->cmdline : "" );
|
||||
int rc;
|
||||
|
||||
/* Read and parse header from image */
|
||||
@ -551,7 +542,7 @@ static int bzimage_exec ( struct image *image ) {
|
||||
}
|
||||
|
||||
/* Parse command line for bootloader parameters */
|
||||
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg, cmdline ) ) != 0)
|
||||
if ( ( rc = bzimage_parse_cmdline ( image, &bzimg ) ) != 0)
|
||||
return rc;
|
||||
|
||||
/* Check that initrds can be loaded */
|
||||
@ -568,7 +559,7 @@ static int bzimage_exec ( struct image *image ) {
|
||||
bzimg.rm_filesz, bzimg.pm_sz );
|
||||
|
||||
/* Store command line */
|
||||
bzimage_set_cmdline ( image, &bzimg, cmdline );
|
||||
bzimage_set_cmdline ( image, &bzimg );
|
||||
|
||||
/* Prepare for exiting. Must do this before loading initrds,
|
||||
* since loading the initrds will corrupt the external heap.
|
||||
|
||||
@ -204,8 +204,8 @@ static int multiboot_add_modules ( struct image *image, physaddr_t start,
|
||||
break;
|
||||
}
|
||||
|
||||
/* Do not include kernel image itself as a module */
|
||||
if ( module_image == image )
|
||||
/* Skip hidden images */
|
||||
if ( module_image->flags & IMAGE_HIDDEN )
|
||||
continue;
|
||||
|
||||
/* Page-align the module */
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
#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,6 +28,7 @@ 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,6 +39,9 @@ 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
|
||||
|
||||
|
||||
@ -1,62 +0,0 @@
|
||||
#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,9 +39,14 @@ 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
|
||||
|
||||
/** Number of RTC interrupts to check for */
|
||||
#define RTC_CHECK_COUNT 3
|
||||
|
||||
/** RTC interrupt handler */
|
||||
extern void rtc_isr ( void );
|
||||
|
||||
@ -145,6 +150,7 @@ static void rtc_disable_int ( void ) {
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int rtc_entropy_check ( void ) {
|
||||
unsigned int count = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* Check that RTC interrupts are working */
|
||||
@ -158,14 +164,18 @@ static int rtc_entropy_check ( void ) {
|
||||
"cli\n\t" );
|
||||
|
||||
/* Check for RTC interrupt flag */
|
||||
if ( rtc_flag )
|
||||
return 0;
|
||||
if ( rtc_flag ) {
|
||||
rtc_flag = 0;
|
||||
if ( ++count >= RTC_CHECK_COUNT )
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delay */
|
||||
mdelay ( 1 );
|
||||
}
|
||||
|
||||
DBGC ( &rtc_flag, "RTC timed out waiting for interrupt\n" );
|
||||
DBGC ( &rtc_flag, "RTC timed out waiting for interrupt %d/%d\n",
|
||||
( count + 1 ), RTC_CHECK_COUNT );
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
@ -195,6 +205,21 @@ 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:
|
||||
@ -218,11 +243,12 @@ static void rtc_entropy_disable ( void ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure a single RTC tick
|
||||
* Get noise sample
|
||||
*
|
||||
* @ret delta Length of RTC tick (in TSC units)
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
uint8_t rtc_sample ( void ) {
|
||||
static int rtc_get_noise ( noise_sample_t *noise ) {
|
||||
uint32_t before;
|
||||
uint32_t after;
|
||||
uint32_t temp;
|
||||
@ -257,10 +283,14 @@ uint8_t rtc_sample ( void ) {
|
||||
: "=a" ( after ), "=d" ( before ), "=Q" ( temp )
|
||||
: "2" ( 0 ) );
|
||||
|
||||
return ( after - before );
|
||||
*noise = ( after - before );
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 );
|
||||
/** 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,
|
||||
};
|
||||
|
||||
@ -290,6 +290,9 @@ REQUIRE_OBJECT ( cert_cmd );
|
||||
#ifdef IMAGE_MEM_CMD
|
||||
REQUIRE_OBJECT ( image_mem_cmd );
|
||||
#endif
|
||||
#ifdef SHIM_CMD
|
||||
REQUIRE_OBJECT ( shim_cmd );
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Drag in miscellaneous objects
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
/*
|
||||
* 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 any later version.
|
||||
* 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
|
||||
@ -23,18 +21,31 @@
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <config/entropy.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Nonexistent entropy source
|
||||
* Entropy configuration options
|
||||
*
|
||||
*
|
||||
* This source provides no entropy and must NOT be used in a
|
||||
* security-sensitive environment.
|
||||
*/
|
||||
|
||||
#include <ipxe/entropy.h>
|
||||
PROVIDE_REQUIRING_SYMBOL();
|
||||
|
||||
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 );
|
||||
/*
|
||||
* 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
|
||||
@ -19,7 +19,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define SMBIOS_EFI
|
||||
#define SANBOOT_EFI
|
||||
#define BOFM_EFI
|
||||
#define ENTROPY_EFI
|
||||
#define ENTROPY_EFITICK
|
||||
#define ENTROPY_EFIRNG
|
||||
#define TIME_EFI
|
||||
#define REBOOT_EFI
|
||||
#define ACPI_EFI
|
||||
@ -46,10 +47,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define USB_BLOCK /* USB block devices */
|
||||
|
||||
#define REBOOT_CMD /* Reboot command */
|
||||
#define SHIM_CMD /* EFI shim command */
|
||||
|
||||
#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,4 +33,8 @@ 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,6 +20,7 @@ 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
|
||||
|
||||
@ -150,7 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
//#define POWEROFF_CMD /* Power off command */
|
||||
//#define IMAGE_TRUST_CMD /* Image trust management commands */
|
||||
//#define PCI_CMD /* PCI commands */
|
||||
//#define PARAM_CMD /* Form parameter commands */
|
||||
//#define PARAM_CMD /* Request parameter commands */
|
||||
//#define NEIGHBOUR_CMD /* Neighbour management commands */
|
||||
//#define PING_CMD /* Ping command */
|
||||
//#define CONSOLE_CMD /* Console command */
|
||||
@ -160,6 +160,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
//#define CERT_CMD /* Certificate management commands */
|
||||
//#define IMAGE_MEM_CMD /* Read memory command */
|
||||
#define IMAGE_ARCHIVE_CMD /* Archive image management commands */
|
||||
//#define SHIM_CMD /* EFI shim command */
|
||||
|
||||
/*
|
||||
* ROM-specific options
|
||||
|
||||
@ -77,17 +77,12 @@ size_t cpio_name_len ( struct image *image ) {
|
||||
*/
|
||||
static void cpio_parse_cmdline ( struct image *image,
|
||||
struct cpio_header *cpio ) {
|
||||
const char *cmdline;
|
||||
char *arg;
|
||||
const char *arg;
|
||||
char *end;
|
||||
unsigned int mode;
|
||||
|
||||
/* Skip image filename */
|
||||
cmdline = ( cpio_name ( image ) + cpio_name_len ( image ) );
|
||||
|
||||
/* Look for "mode=" */
|
||||
if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
|
||||
arg += 5;
|
||||
if ( ( arg = image_argument ( image, "mode=" ) ) ) {
|
||||
mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
|
||||
if ( *end && ( *end != ' ' ) ) {
|
||||
DBGC ( image, "CPIO %p strange \"mode=\" "
|
||||
|
||||
114
src/core/image.c
114
src/core/image.c
@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <libgen.h>
|
||||
@ -55,8 +56,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
/** List of registered images */
|
||||
struct list_head images = LIST_HEAD_INIT ( images );
|
||||
|
||||
/** Image selected for execution */
|
||||
struct image_tag selected_image __image_tag = {
|
||||
.name = "SELECTED",
|
||||
};
|
||||
|
||||
/** Currently-executing image */
|
||||
struct image *current_image;
|
||||
struct image_tag current_image __image_tag = {
|
||||
.name = "CURRENT",
|
||||
};
|
||||
|
||||
/** Current image trust requirement */
|
||||
static int require_trusted_images = 0;
|
||||
@ -71,8 +79,13 @@ static int require_trusted_images_permanent = 0;
|
||||
*/
|
||||
static void free_image ( struct refcnt *refcnt ) {
|
||||
struct image *image = container_of ( refcnt, struct image, refcnt );
|
||||
struct image_tag *tag;
|
||||
|
||||
DBGC ( image, "IMAGE %s freed\n", image->name );
|
||||
for_each_table_entry ( tag, IMAGE_TAGS ) {
|
||||
if ( tag->image == image )
|
||||
tag->image = NULL;
|
||||
}
|
||||
free ( image->name );
|
||||
free ( image->cmdline );
|
||||
uri_put ( image->uri );
|
||||
@ -260,12 +273,6 @@ int register_image ( struct image *image ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Avoid ending up with multiple "selected" images on
|
||||
* re-registration
|
||||
*/
|
||||
if ( image_find_selected() )
|
||||
image->flags &= ~IMAGE_SELECTED;
|
||||
|
||||
/* Add to image list */
|
||||
image_get ( image );
|
||||
image->flags |= IMAGE_REGISTERED;
|
||||
@ -311,7 +318,7 @@ void unregister_image ( struct image *image ) {
|
||||
struct image * find_image ( const char *name ) {
|
||||
struct image *image;
|
||||
|
||||
list_for_each_entry ( image, &images, list ) {
|
||||
for_each_image ( image ) {
|
||||
if ( strcmp ( image->name, name ) == 0 )
|
||||
return image;
|
||||
}
|
||||
@ -319,6 +326,23 @@ struct image * find_image ( const char *name ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find image by tag
|
||||
*
|
||||
* @v tag Image tag
|
||||
* @ret image Executable image, or NULL
|
||||
*/
|
||||
struct image * find_image_tag ( struct image_tag *tag ) {
|
||||
struct image *image;
|
||||
|
||||
for_each_image ( image ) {
|
||||
if ( tag->image == image )
|
||||
return image;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute image
|
||||
*
|
||||
@ -345,14 +369,13 @@ int image_exec ( struct image *image ) {
|
||||
if ( image->uri )
|
||||
churi ( image->uri );
|
||||
|
||||
/* Preserve record of any currently-running image */
|
||||
saved_current_image = current_image;
|
||||
/* Set as currently running image */
|
||||
saved_current_image = image_tag ( image, ¤t_image );
|
||||
|
||||
/* Take out a temporary reference to the image. This allows
|
||||
* the image to unregister itself if necessary, without
|
||||
* automatically freeing itself.
|
||||
/* Take out a temporary reference to the image, so that it
|
||||
* does not get freed when temporarily unregistered.
|
||||
*/
|
||||
current_image = image_get ( image );
|
||||
image_get ( image );
|
||||
|
||||
/* Check that this image can be executed */
|
||||
if ( ! ( image->type && image->type->exec ) ) {
|
||||
@ -370,6 +393,9 @@ int image_exec ( struct image *image ) {
|
||||
/* Record boot attempt */
|
||||
syslog ( LOG_NOTICE, "Executing \"%s\"\n", image->name );
|
||||
|
||||
/* Temporarily unregister the image during its execution */
|
||||
unregister_image ( image );
|
||||
|
||||
/* Try executing the image */
|
||||
if ( ( rc = image->type->exec ( image ) ) != 0 ) {
|
||||
DBGC ( image, "IMAGE %s could not execute: %s\n",
|
||||
@ -386,6 +412,10 @@ int image_exec ( struct image *image ) {
|
||||
image->name, strerror ( rc ) );
|
||||
}
|
||||
|
||||
/* Re-register image (unless due to be replaced) */
|
||||
if ( ! image->replacement )
|
||||
register_image ( image );
|
||||
|
||||
/* Pick up replacement image before we drop the original
|
||||
* image's temporary reference. The replacement image must
|
||||
* already be registered, so we don't need to hold a temporary
|
||||
@ -412,7 +442,7 @@ int image_exec ( struct image *image ) {
|
||||
image_put ( image );
|
||||
|
||||
/* Restore previous currently-running image */
|
||||
current_image = saved_current_image;
|
||||
image_tag ( saved_current_image, ¤t_image );
|
||||
|
||||
/* Reset current working directory */
|
||||
churi ( old_cwuri );
|
||||
@ -435,7 +465,7 @@ int image_exec ( struct image *image ) {
|
||||
* registered until the currently-executing image returns.
|
||||
*/
|
||||
int image_replace ( struct image *replacement ) {
|
||||
struct image *image = current_image;
|
||||
struct image *image = current_image.image;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
@ -471,37 +501,17 @@ int image_replace ( struct image *replacement ) {
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int image_select ( struct image *image ) {
|
||||
struct image *tmp;
|
||||
|
||||
/* Unselect all other images */
|
||||
for_each_image ( tmp )
|
||||
tmp->flags &= ~IMAGE_SELECTED;
|
||||
|
||||
/* Check that this image can be executed */
|
||||
if ( ! ( image->type && image->type->exec ) )
|
||||
return -ENOEXEC;
|
||||
|
||||
/* Mark image as selected */
|
||||
image->flags |= IMAGE_SELECTED;
|
||||
image_tag ( image, &selected_image );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find selected image
|
||||
*
|
||||
* @ret image Executable image, or NULL
|
||||
*/
|
||||
struct image * image_find_selected ( void ) {
|
||||
struct image *image;
|
||||
|
||||
for_each_image ( image ) {
|
||||
if ( image->flags & IMAGE_SELECTED )
|
||||
return image;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change image trust requirement
|
||||
*
|
||||
@ -569,3 +579,33 @@ struct image * image_memory ( const char *name, userptr_t data, size_t len ) {
|
||||
err_alloc_image:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find argument within image command line
|
||||
*
|
||||
* @v image Image
|
||||
* @v key Argument search key (including trailing delimiter)
|
||||
* @ret value Argument value, or NULL if not found
|
||||
*/
|
||||
const char * image_argument ( struct image *image, const char *key ) {
|
||||
const char *cmdline = image->cmdline;
|
||||
const char *search;
|
||||
const char *match;
|
||||
const char *next;
|
||||
|
||||
/* Find argument */
|
||||
for ( search = cmdline ; search ; search = next ) {
|
||||
|
||||
/* Find next occurrence, if any */
|
||||
match = strstr ( search, key );
|
||||
if ( ! match )
|
||||
break;
|
||||
next = ( match + strlen ( key ) );
|
||||
|
||||
/* Check preceding delimiter, if any */
|
||||
if ( ( match == cmdline ) || isspace ( match[-1] ) )
|
||||
return next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Form parameters
|
||||
* Request parameters
|
||||
*
|
||||
*/
|
||||
|
||||
@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
static LIST_HEAD ( parameters );
|
||||
|
||||
/**
|
||||
* Free form parameter list
|
||||
* Free request parameter list
|
||||
*
|
||||
* @v refcnt Reference count
|
||||
*/
|
||||
@ -60,7 +60,7 @@ static void free_parameters ( struct refcnt *refcnt ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Find form parameter list by name
|
||||
* Find request parameter list by name
|
||||
*
|
||||
* @v name Parameter list name (may be NULL)
|
||||
* @ret params Parameter list, or NULL if not found
|
||||
@ -78,7 +78,7 @@ struct parameters * find_parameters ( const char *name ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create form parameter list
|
||||
* Create request parameter list
|
||||
*
|
||||
* @v name Parameter list name (may be NULL)
|
||||
* @ret params Parameter list, or NULL on failure
|
||||
@ -118,15 +118,17 @@ struct parameters * create_parameters ( const char *name ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form parameter
|
||||
* Add request parameter
|
||||
*
|
||||
* @v params Parameter list
|
||||
* @v key Parameter key
|
||||
* @v value Parameter value
|
||||
* @v flags Parameter flags
|
||||
* @ret param Parameter, or NULL on failure
|
||||
*/
|
||||
struct parameter * add_parameter ( struct parameters *params,
|
||||
const char *key, const char *value ) {
|
||||
const char *key, const char *value,
|
||||
unsigned int flags ) {
|
||||
struct parameter *param;
|
||||
size_t key_len;
|
||||
size_t value_len;
|
||||
@ -147,11 +149,14 @@ struct parameter * add_parameter ( struct parameters *params,
|
||||
param->key = key_copy;
|
||||
strcpy ( value_copy, value );
|
||||
param->value = value_copy;
|
||||
param->flags = flags;
|
||||
|
||||
/* Add to list of parameters */
|
||||
list_add_tail ( ¶m->list, ¶ms->entries );
|
||||
|
||||
DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"\n",
|
||||
params->name, param->key, param->value );
|
||||
DBGC ( params, "PARAMS \"%s\" added \"%s\"=\"%s\"%s%s\n",
|
||||
params->name, param->key, param->value,
|
||||
( ( param->flags & PARAMETER_FORM ) ? " (form)" : "" ),
|
||||
( ( param->flags & PARAMETER_HEADER ) ? " (header)" : "" ) );
|
||||
return param;
|
||||
}
|
||||
|
||||
@ -302,7 +302,7 @@ int parse_autovivified_setting ( char *text, struct named_setting *setting ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse form parameter list name
|
||||
* Parse request parameter list name
|
||||
*
|
||||
* @v text Text
|
||||
* @ret params Parameter list
|
||||
|
||||
@ -51,59 +51,33 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
__einfo_uniqify ( EINFO_EPIPE, 0x02, "Adaptive proportion test failed" )
|
||||
|
||||
/**
|
||||
* Calculate cutoff value for the repetition count test
|
||||
* Initialise repetition count test
|
||||
*
|
||||
* @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.
|
||||
* @v source Entropy source
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
repetition_count_cutoff ( void ) {
|
||||
double max_repetitions;
|
||||
unsigned int cutoff;
|
||||
static void repetition_count_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_repetition_count_test *test =
|
||||
&source->repetition_count_test;
|
||||
|
||||
/* 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;
|
||||
/* Sanity checks */
|
||||
assert ( test->repetition_count == 0 );
|
||||
assert ( test->cutoff > 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ( noise_sample_t sample ) {
|
||||
static noise_sample_t most_recent_sample;
|
||||
static unsigned int repetition_count = 0;
|
||||
static int repetition_count_test ( struct entropy_source *source,
|
||||
noise_sample_t sample ) {
|
||||
struct entropy_repetition_count_test *test =
|
||||
&source->repetition_count_test;
|
||||
|
||||
/* A = the most recently seen sample value
|
||||
* B = the number of times that value A has been seen in a row
|
||||
@ -116,158 +90,71 @@ static int repetition_count_test ( noise_sample_t sample ) {
|
||||
* the initial value of most_recent_sample is treated as being
|
||||
* undefined.)
|
||||
*/
|
||||
if ( ( sample == most_recent_sample ) && ( repetition_count > 0 ) ) {
|
||||
if ( ( sample == test->most_recent_sample ) &&
|
||||
( test->repetition_count > 0 ) ) {
|
||||
|
||||
/* a) If the new sample = A, then B is incremented by one. */
|
||||
repetition_count++;
|
||||
test->repetition_count++;
|
||||
|
||||
/* i. If B >= C, then an error condition is raised
|
||||
* due to a failure of the test
|
||||
*/
|
||||
if ( repetition_count >= repetition_count_cutoff() )
|
||||
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 );
|
||||
return -EPIPE_REPETITION_COUNT_TEST;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
/* b) Else:
|
||||
* i. A = new sample
|
||||
*/
|
||||
most_recent_sample = sample;
|
||||
test->most_recent_sample = sample;
|
||||
|
||||
/* ii. B = 1 */
|
||||
repetition_count = 1;
|
||||
test->repetition_count = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Window size for the adaptive proportion test
|
||||
* Initialise 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.
|
||||
* @v source Entropy source
|
||||
*/
|
||||
#define ADAPTIVE_PROPORTION_WINDOW_SIZE 64
|
||||
static void adaptive_proportion_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_adaptive_proportion_test *test =
|
||||
&source->adaptive_proportion_test;
|
||||
|
||||
/**
|
||||
* 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) )
|
||||
/* Sanity checks */
|
||||
assert ( test->sample_count == 0 );
|
||||
assert ( test->repetition_count == 0 );
|
||||
assert ( test->cutoff > 0 );
|
||||
|
||||
/**
|
||||
* 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;
|
||||
/* Ensure that a new test run starts immediately */
|
||||
test->sample_count = ADAPTIVE_PROPORTION_WINDOW_SIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ( 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;
|
||||
static int adaptive_proportion_test ( struct entropy_source *source,
|
||||
noise_sample_t sample ) {
|
||||
struct entropy_adaptive_proportion_test *test =
|
||||
&source->adaptive_proportion_test;
|
||||
|
||||
/* A = the sample value currently being counted
|
||||
* B = the number of samples examined in this run of the test so far
|
||||
* S = 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
|
||||
@ -284,37 +171,41 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
|
||||
*/
|
||||
|
||||
/* 2. If S = N, then a new run of the test begins: */
|
||||
if ( sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
|
||||
if ( test->sample_count == ADAPTIVE_PROPORTION_WINDOW_SIZE ) {
|
||||
|
||||
/* a. A = the current sample */
|
||||
current_counted_sample = sample;
|
||||
test->current_counted_sample = sample;
|
||||
|
||||
/* b. S = 0 */
|
||||
sample_count = 0;
|
||||
test->sample_count = 0;
|
||||
|
||||
/* c. B = 0 */
|
||||
repetition_count = 0;
|
||||
test->repetition_count = 0;
|
||||
|
||||
} else {
|
||||
|
||||
/* Else: (the test is already running)
|
||||
* a. S = S + 1
|
||||
*/
|
||||
sample_count++;
|
||||
test->sample_count++;
|
||||
|
||||
/* b. If A = the current sample, then: */
|
||||
if ( sample == current_counted_sample ) {
|
||||
if ( sample == test->current_counted_sample ) {
|
||||
|
||||
/* i. B = B + 1 */
|
||||
repetition_count++;
|
||||
test->repetition_count++;
|
||||
|
||||
/* ii. If S (sic) > C then raise an error
|
||||
* condition, because the test has
|
||||
* detected a failure
|
||||
*/
|
||||
if ( repetition_count > adaptive_proportion_cutoff() )
|
||||
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 );
|
||||
return -EPIPE_ADAPTIVE_PROPORTION_TEST;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,62 +215,180 @@ static int adaptive_proportion_test ( noise_sample_t sample ) {
|
||||
/**
|
||||
* 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 ( entropy_sample_t *entropy ) {
|
||||
static int rc = 0;
|
||||
static int get_entropy ( struct entropy_source *source,
|
||||
entropy_sample_t *entropy ) {
|
||||
noise_sample_t noise;
|
||||
int rc;
|
||||
|
||||
/* Any failure is permanent */
|
||||
if ( rc != 0 )
|
||||
return rc;
|
||||
if ( ( rc = source->rc ) != 0 )
|
||||
goto err_broken;
|
||||
|
||||
/* Get noise sample */
|
||||
if ( ( rc = get_noise ( &noise ) ) != 0 )
|
||||
return rc;
|
||||
if ( ( rc = get_noise ( source, &noise ) ) != 0 )
|
||||
goto err_get_noise;
|
||||
|
||||
/* 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 ( noise ) ) != 0 )
|
||||
return rc;
|
||||
if ( ( rc = adaptive_proportion_test ( noise ) ) != 0 )
|
||||
return rc;
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate number of samples required for startup tests
|
||||
* Initialise startup test
|
||||
*
|
||||
* @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.
|
||||
* @v source Entropy source
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) unsigned int
|
||||
startup_test_count ( void ) {
|
||||
unsigned int num_samples;
|
||||
static void startup_test_init ( struct entropy_source *source ) {
|
||||
struct entropy_startup_test *test = &source->startup_test;
|
||||
|
||||
/* 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 );
|
||||
/* Sanity check */
|
||||
assert ( test->tested == 0 );
|
||||
assert ( test->count > 0 );
|
||||
}
|
||||
|
||||
return num_samples;
|
||||
/**
|
||||
* Perform startup test
|
||||
*
|
||||
* @v source Entropy source
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int startup_test ( struct entropy_source *source ) {
|
||||
struct entropy_startup_test *test = &source->startup_test;
|
||||
entropy_sample_t sample;
|
||||
int rc;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -402,7 +411,7 @@ static uint32_t make_next_nonce ( void ) {
|
||||
/**
|
||||
* Obtain entropy input temporary buffer
|
||||
*
|
||||
* @v num_samples Number of entropy samples
|
||||
* @v min_entropy Min-entropy required
|
||||
* @v tmp Temporary buffer
|
||||
* @v tmp_len Length of temporary buffer
|
||||
* @ret rc Return status code
|
||||
@ -412,47 +421,41 @@ 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 ( unsigned int num_samples, uint8_t *tmp,
|
||||
int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
size_t tmp_len ) {
|
||||
static unsigned int startup_tested = 0;
|
||||
struct entropy_source *source;
|
||||
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() ) != 0 )
|
||||
return rc;
|
||||
if ( ( rc = entropy_enable_working ( &source ) ) != 0 )
|
||||
goto err_enable_working;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
/* Sanity checks */
|
||||
assert ( source->startup_test.count > 0 );
|
||||
assert ( source->startup_test.tested >= source->startup_test.count );
|
||||
|
||||
/* 3. entropy_total = 0
|
||||
*
|
||||
* (Nothing to do; the number of entropy samples required has
|
||||
* already been precalculated.)
|
||||
*/
|
||||
/* 3. entropy_total = 0 */
|
||||
entropy_total = MIN_ENTROPY ( 0 );
|
||||
|
||||
/* 4. tmp = a fixed n-bit value, such as 0^n */
|
||||
memset ( tmp, 0, tmp_len );
|
||||
|
||||
/* 5. While ( entropy_total < min_entropy ) */
|
||||
while ( num_samples-- ) {
|
||||
for ( num_samples = 0 ; entropy_total < min_entropy ; num_samples++ ) {
|
||||
/* 5.1. ( status, entropy_bitstring, assessed_entropy )
|
||||
* = GetEntropy()
|
||||
* 5.2. If status indicates an error, return ( status, Null )
|
||||
*/
|
||||
if ( ( rc = get_entropy ( &data.sample ) ) != 0 )
|
||||
if ( ( rc = get_entropy ( source, &data.sample ) ) != 0 )
|
||||
goto err_get_entropy;
|
||||
|
||||
/* 5.3. nonce = MakeNextNonce() */
|
||||
@ -466,19 +469,26 @@ int get_entropy_input_tmp ( unsigned int num_samples, uint8_t *tmp,
|
||||
for ( i = 0 ; i < tmp_len ; i++ )
|
||||
tmp[i] ^= df_buf[i];
|
||||
|
||||
/* 5.5. entropy_total = entropy_total + assessed_entropy
|
||||
*
|
||||
* (Nothing to do; the number of entropy samples
|
||||
* required has already been precalculated.)
|
||||
*/
|
||||
/* 5.5. entropy_total = entropy_total + assessed_entropy */
|
||||
entropy_total += source->min_entropy_per_sample;
|
||||
}
|
||||
|
||||
/* Disable entropy gathering */
|
||||
entropy_disable();
|
||||
entropy_disable ( source );
|
||||
|
||||
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();
|
||||
entropy_disable ( source );
|
||||
assert ( source->rc == rc );
|
||||
err_enable_working:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Drag in objects via entropy_enable */
|
||||
REQUIRING_SYMBOL ( entropy_enable );
|
||||
|
||||
/* Drag in entropy configuration */
|
||||
REQUIRE_OBJECT ( config_entropy );
|
||||
|
||||
@ -609,6 +609,7 @@ 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;
|
||||
@ -645,6 +646,9 @@ 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 );
|
||||
|
||||
|
||||
@ -290,6 +290,18 @@ static int intel_reset ( struct intel_nic *intel ) {
|
||||
pba, readl ( intel->regs + INTEL_PBA ) );
|
||||
}
|
||||
|
||||
/* The Intel I210's packet buffer size registers reset only on
|
||||
* power up. If an operating system changes these but then
|
||||
* the computer recieves a reset signal without losing power,
|
||||
* the registers will stay the same (but be incompatible with
|
||||
* other register defaults), thus making the device unable to
|
||||
* pass traffic.
|
||||
*/
|
||||
if ( intel->flags & INTEL_PBSIZE_RST ) {
|
||||
writel ( INTEL_RXPBS_I210, intel->regs + INTEL_RXPBS );
|
||||
writel ( INTEL_TXPBS_I210, intel->regs + INTEL_TXPBS );
|
||||
}
|
||||
|
||||
/* Always reset MAC. Required to reset the TX and RX rings. */
|
||||
writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL );
|
||||
mdelay ( INTEL_RESET_DELAY_MS );
|
||||
@ -1139,7 +1151,7 @@ static struct pci_device_id intel_nics[] = {
|
||||
PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1533, "i210", "I210", INTEL_PBSIZE_RST ),
|
||||
PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ),
|
||||
PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
|
||||
@ -1147,7 +1159,7 @@ static struct pci_device_id intel_nics[] = {
|
||||
PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", INTEL_NO_PHY_RST ),
|
||||
PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ),
|
||||
PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ),
|
||||
PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", INTEL_PBSIZE_RST ),
|
||||
PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ),
|
||||
PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ),
|
||||
|
||||
@ -138,6 +138,10 @@ struct intel_descriptor {
|
||||
/** Packet Buffer Size */
|
||||
#define INTEL_PBS 0x01008UL
|
||||
|
||||
/** Receive packet buffer size */
|
||||
#define INTEL_RXPBS 0x02404UL
|
||||
#define INTEL_RXPBS_I210 0x000000a2UL /**< I210 power-up default */
|
||||
|
||||
/** Receive Descriptor register block */
|
||||
#define INTEL_RD 0x02800UL
|
||||
|
||||
@ -154,6 +158,10 @@ struct intel_descriptor {
|
||||
/** Receive buffer length */
|
||||
#define INTEL_RX_MAX_LEN 2048
|
||||
|
||||
/** Transmit packet buffer size */
|
||||
#define INTEL_TXPBS 0x03404UL
|
||||
#define INTEL_TXPBS_I210 0x04000014UL /**< I210 power-up default */
|
||||
|
||||
/** Transmit Descriptor register block */
|
||||
#define INTEL_TD 0x03800UL
|
||||
|
||||
@ -319,6 +327,8 @@ enum intel_flags {
|
||||
INTEL_NO_ASDE = 0x0008,
|
||||
/** Reset may cause a complete device hang */
|
||||
INTEL_RST_HANG = 0x0010,
|
||||
/** PBSIZE registers must be explicitly reset */
|
||||
INTEL_PBSIZE_RST = 0x0020,
|
||||
};
|
||||
|
||||
/** The i219 has a seriously broken reset mechanism */
|
||||
|
||||
@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = {
|
||||
PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x151c, "82599-tn", "82599 (TN)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ),
|
||||
|
||||
@ -129,7 +129,7 @@ static int imgsingle_exec ( int argc, char **argv,
|
||||
&image ) ) != 0 )
|
||||
goto err_acquire;
|
||||
} else {
|
||||
image = image_find_selected();
|
||||
image = find_image_tag ( &selected_image );
|
||||
if ( ! image ) {
|
||||
printf ( "No image selected\n" );
|
||||
goto err_acquire;
|
||||
|
||||
@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Form parameter commands
|
||||
* Request parameter commands
|
||||
*
|
||||
*/
|
||||
|
||||
@ -90,12 +90,16 @@ static int params_exec ( int argc, char **argv ) {
|
||||
struct param_options {
|
||||
/** Parameter list name */
|
||||
char *params;
|
||||
/** Parameter is a header */
|
||||
int header;
|
||||
};
|
||||
|
||||
/** "param" option list */
|
||||
static struct option_descriptor param_opts[] = {
|
||||
OPTION_DESC ( "params", 'p', required_argument,
|
||||
struct param_options, params, parse_string ),
|
||||
OPTION_DESC ( "header", 'H', no_argument,
|
||||
struct param_options, header, parse_flag ),
|
||||
};
|
||||
|
||||
/** "param" command descriptor */
|
||||
@ -114,6 +118,7 @@ static int param_exec ( int argc, char **argv ) {
|
||||
struct param_options opts;
|
||||
char *key;
|
||||
char *value;
|
||||
unsigned int flags;
|
||||
struct parameters *params;
|
||||
struct parameter *param;
|
||||
int rc;
|
||||
@ -132,12 +137,15 @@ static int param_exec ( int argc, char **argv ) {
|
||||
goto err_parse_value;
|
||||
}
|
||||
|
||||
/* Construct flags */
|
||||
flags = ( opts.header ? PARAMETER_HEADER : PARAMETER_FORM );
|
||||
|
||||
/* Identify parameter list */
|
||||
if ( ( rc = parse_parameters ( opts.params, ¶ms ) ) != 0 )
|
||||
goto err_parse_parameters;
|
||||
|
||||
/* Add parameter */
|
||||
param = add_parameter ( params, key, value );
|
||||
param = add_parameter ( params, key, value, flags );
|
||||
if ( ! param ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_add_parameter;
|
||||
@ -154,7 +162,7 @@ static int param_exec ( int argc, char **argv ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Form parameter commands */
|
||||
/** Request parameter commands */
|
||||
struct command param_commands[] __command = {
|
||||
{
|
||||
.name = "params",
|
||||
|
||||
117
src/hci/commands/shim_cmd.c
Normal file
117
src/hci/commands/shim_cmd.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 );
|
||||
|
||||
#include <getopt.h>
|
||||
#include <ipxe/command.h>
|
||||
#include <ipxe/parseopt.h>
|
||||
#include <ipxe/efi/efi_image.h>
|
||||
#include <usr/imgmgmt.h>
|
||||
#include <usr/shimmgmt.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI shim command
|
||||
*
|
||||
*/
|
||||
|
||||
/** "shim" options */
|
||||
struct shim_options {
|
||||
/** Download timeout */
|
||||
unsigned long timeout;
|
||||
/** Require third party loader */
|
||||
int require_loader;
|
||||
/** Allow PXE base code protocol */
|
||||
int allow_pxe;
|
||||
/** Allow SBAT variable access */
|
||||
int allow_sbat;
|
||||
};
|
||||
|
||||
/** "shim" option list */
|
||||
static struct option_descriptor shim_opts[] = {
|
||||
OPTION_DESC ( "timeout", 't', required_argument,
|
||||
struct shim_options, timeout, parse_timeout ),
|
||||
OPTION_DESC ( "require-loader", 'l', no_argument,
|
||||
struct shim_options, require_loader, parse_flag ),
|
||||
OPTION_DESC ( "allow-pxe", 'p', no_argument,
|
||||
struct shim_options, allow_pxe, parse_flag ),
|
||||
OPTION_DESC ( "allow-sbat", 's', no_argument,
|
||||
struct shim_options, allow_sbat, parse_flag ),
|
||||
};
|
||||
|
||||
/** "shim" command descriptor */
|
||||
static struct command_descriptor shim_cmd =
|
||||
COMMAND_DESC ( struct shim_options, shim_opts, 0, 1, NULL );
|
||||
|
||||
/**
|
||||
* The "shim" command
|
||||
*
|
||||
* @v argc Argument count
|
||||
* @v argv Argument list
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int shim_exec ( int argc, char **argv ) {
|
||||
struct shim_options opts;
|
||||
struct image *image = NULL;
|
||||
struct image *kernel;
|
||||
char *name_uri;
|
||||
int download;
|
||||
int rc;
|
||||
|
||||
/* Parse options */
|
||||
if ( ( rc = parse_options ( argc, argv, &shim_cmd, &opts ) ) != 0 )
|
||||
goto err_parse;
|
||||
|
||||
/* Decide whether or not to download images */
|
||||
kernel = find_image_tag ( &selected_image );
|
||||
download = ( ! ( kernel && efi_can_load ( kernel ) ) );
|
||||
|
||||
/* Parse name/URI string */
|
||||
name_uri = argv[optind];
|
||||
|
||||
/* Acquire image, if applicable */
|
||||
if ( download && name_uri &&
|
||||
( ( rc = imgacquire ( name_uri, opts.timeout,
|
||||
&image ) ) != 0 ) ) {
|
||||
goto err_image;
|
||||
}
|
||||
|
||||
/* (Un)register as shim */
|
||||
if ( ( rc = shim ( image, opts.require_loader, opts.allow_pxe,
|
||||
opts.allow_sbat ) ) != 0 )
|
||||
goto err_shim;
|
||||
|
||||
err_shim:
|
||||
err_image:
|
||||
err_parse:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** Shim commands */
|
||||
struct command shim_commands[] __command = {
|
||||
{
|
||||
.name = "shim",
|
||||
.exec = shim_exec,
|
||||
},
|
||||
};
|
||||
@ -31,6 +31,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
|
||||
#include <ipxe/efi/efi_wrap.h>
|
||||
#include <ipxe/efi/efi_pxe.h>
|
||||
#include <ipxe/efi/efi_driver.h>
|
||||
#include <ipxe/efi/efi_image.h>
|
||||
#include <ipxe/efi/efi_shim.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/init.h>
|
||||
#include <ipxe/features.h>
|
||||
@ -109,18 +111,14 @@ efi_image_path ( struct image *image, EFI_DEVICE_PATH_PROTOCOL *parent ) {
|
||||
*/
|
||||
static wchar_t * efi_image_cmdline ( struct image *image ) {
|
||||
wchar_t *cmdline;
|
||||
size_t len;
|
||||
|
||||
len = ( strlen ( image->name ) +
|
||||
( image->cmdline ?
|
||||
( 1 /* " " */ + strlen ( image->cmdline ) ) : 0 ) );
|
||||
cmdline = zalloc ( ( len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
|
||||
if ( ! cmdline )
|
||||
/* Allocate and construct command line */
|
||||
if ( efi_asprintf ( &cmdline, "%s%s%s", image->name,
|
||||
( image->cmdline ? " " : "" ),
|
||||
( image->cmdline ? image->cmdline : "" ) ) < 0 ) {
|
||||
return NULL;
|
||||
efi_snprintf ( cmdline, ( len + 1 /* NUL */ ), "%s%s%s",
|
||||
image->name,
|
||||
( image->cmdline ? " " : "" ),
|
||||
( image->cmdline ? image->cmdline : "" ) );
|
||||
}
|
||||
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
@ -138,47 +136,65 @@ static int efi_image_exec ( struct image *image ) {
|
||||
EFI_LOADED_IMAGE_PROTOCOL *image;
|
||||
void *interface;
|
||||
} loaded;
|
||||
struct image *shim;
|
||||
struct image *exec;
|
||||
EFI_HANDLE handle;
|
||||
EFI_MEMORY_TYPE type;
|
||||
wchar_t *cmdline;
|
||||
unsigned int toggle;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Find an appropriate device handle to use */
|
||||
snpdev = last_opened_snpdev();
|
||||
if ( ! snpdev ) {
|
||||
DBGC ( image, "EFIIMAGE %p could not identify SNP device\n",
|
||||
image );
|
||||
DBGC ( image, "EFIIMAGE %s could not identify SNP device\n",
|
||||
image->name );
|
||||
rc = -ENODEV;
|
||||
goto err_no_snpdev;
|
||||
}
|
||||
|
||||
/* Use shim instead of directly executing image if applicable */
|
||||
shim = ( efi_can_load ( image ) ?
|
||||
NULL : find_image_tag ( &efi_shim ) );
|
||||
exec = ( shim ? shim : image );
|
||||
if ( shim ) {
|
||||
DBGC ( image, "EFIIMAGE %s executing via %s\n",
|
||||
image->name, shim->name );
|
||||
}
|
||||
|
||||
/* Re-register as a hidden image to allow for access via file I/O */
|
||||
toggle = ( ~image->flags & IMAGE_HIDDEN );
|
||||
image->flags |= IMAGE_HIDDEN;
|
||||
if ( ( rc = register_image ( image ) ) != 0 )
|
||||
goto err_register_image;
|
||||
|
||||
/* Install file I/O protocols */
|
||||
if ( ( rc = efi_file_install ( snpdev->handle ) ) != 0 ) {
|
||||
DBGC ( image, "EFIIMAGE %p could not install file protocol: "
|
||||
"%s\n", image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not install file protocol: "
|
||||
"%s\n", image->name, strerror ( rc ) );
|
||||
goto err_file_install;
|
||||
}
|
||||
|
||||
/* Install PXE base code protocol */
|
||||
if ( ( rc = efi_pxe_install ( snpdev->handle, snpdev->netdev ) ) != 0 ){
|
||||
DBGC ( image, "EFIIMAGE %p could not install PXE protocol: "
|
||||
"%s\n", image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not install PXE protocol: "
|
||||
"%s\n", image->name, strerror ( rc ) );
|
||||
goto err_pxe_install;
|
||||
}
|
||||
|
||||
/* Install iPXE download protocol */
|
||||
if ( ( rc = efi_download_install ( snpdev->handle ) ) != 0 ) {
|
||||
DBGC ( image, "EFIIMAGE %p could not install iPXE download "
|
||||
"protocol: %s\n", image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not install iPXE download "
|
||||
"protocol: %s\n", image->name, strerror ( rc ) );
|
||||
goto err_download_install;
|
||||
}
|
||||
|
||||
/* Create device path for image */
|
||||
path = efi_image_path ( image, snpdev->path );
|
||||
path = efi_image_path ( exec, snpdev->path );
|
||||
if ( ! path ) {
|
||||
DBGC ( image, "EFIIMAGE %p could not create device path\n",
|
||||
image );
|
||||
DBGC ( image, "EFIIMAGE %s could not create device path\n",
|
||||
image->name );
|
||||
rc = -ENOMEM;
|
||||
goto err_image_path;
|
||||
}
|
||||
@ -186,21 +202,30 @@ static int efi_image_exec ( struct image *image ) {
|
||||
/* Create command line for image */
|
||||
cmdline = efi_image_cmdline ( image );
|
||||
if ( ! cmdline ) {
|
||||
DBGC ( image, "EFIIMAGE %p could not create command line\n",
|
||||
image );
|
||||
DBGC ( image, "EFIIMAGE %s could not create command line\n",
|
||||
image->name );
|
||||
rc = -ENOMEM;
|
||||
goto err_cmdline;
|
||||
}
|
||||
|
||||
/* Install shim special handling if applicable */
|
||||
if ( shim &&
|
||||
( ( rc = efi_shim_install ( shim, snpdev->handle,
|
||||
&cmdline ) ) != 0 ) ){
|
||||
DBGC ( image, "EFIIMAGE %s could not install shim handling: "
|
||||
"%s\n", image->name, strerror ( rc ) );
|
||||
goto err_shim_install;
|
||||
}
|
||||
|
||||
/* Attempt loading image */
|
||||
handle = NULL;
|
||||
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, path,
|
||||
user_to_virt ( image->data, 0 ),
|
||||
image->len, &handle ) ) != 0 ) {
|
||||
user_to_virt ( exec->data, 0 ),
|
||||
exec->len, &handle ) ) != 0 ) {
|
||||
/* Not an EFI image */
|
||||
rc = -EEFI_LOAD ( efirc );
|
||||
DBGC ( image, "EFIIMAGE %p could not load: %s\n",
|
||||
image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
|
||||
image->name, strerror ( rc ) );
|
||||
if ( efirc == EFI_SECURITY_VIOLATION ) {
|
||||
goto err_load_image_security_violation;
|
||||
} else {
|
||||
@ -220,8 +245,8 @@ static int efi_image_exec ( struct image *image ) {
|
||||
|
||||
/* Some EFI 1.10 implementations seem not to fill in DeviceHandle */
|
||||
if ( loaded.image->DeviceHandle == NULL ) {
|
||||
DBGC ( image, "EFIIMAGE %p filling in missing DeviceHandle\n",
|
||||
image );
|
||||
DBGC ( image, "EFIIMAGE %s filling in missing DeviceHandle\n",
|
||||
image->name );
|
||||
loaded.image->DeviceHandle = snpdev->handle;
|
||||
}
|
||||
|
||||
@ -251,14 +276,14 @@ static int efi_image_exec ( struct image *image ) {
|
||||
/* Start the image */
|
||||
if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
|
||||
rc = -EEFI_START ( efirc );
|
||||
DBGC ( image, "EFIIMAGE %p could not start (or returned with "
|
||||
"error): %s\n", image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not start (or returned with "
|
||||
"error): %s\n", image->name, strerror ( rc ) );
|
||||
goto err_start_image;
|
||||
}
|
||||
|
||||
/* If image was a driver, connect it up to anything available */
|
||||
if ( type == EfiBootServicesCode ) {
|
||||
DBGC ( image, "EFIIMAGE %p connecting drivers\n", image );
|
||||
DBGC ( image, "EFIIMAGE %s connecting drivers\n", image->name );
|
||||
efi_driver_reconnect_all();
|
||||
}
|
||||
|
||||
@ -286,6 +311,9 @@ static int efi_image_exec ( struct image *image ) {
|
||||
if ( rc != 0 )
|
||||
bs->UnloadImage ( handle );
|
||||
err_load_image:
|
||||
if ( shim )
|
||||
efi_shim_uninstall();
|
||||
err_shim_install:
|
||||
free ( cmdline );
|
||||
err_cmdline:
|
||||
free ( path );
|
||||
@ -296,6 +324,9 @@ static int efi_image_exec ( struct image *image ) {
|
||||
err_pxe_install:
|
||||
efi_file_uninstall ( snpdev->handle );
|
||||
err_file_install:
|
||||
unregister_image ( image );
|
||||
err_register_image:
|
||||
image->flags ^= toggle;
|
||||
err_no_snpdev:
|
||||
return rc;
|
||||
}
|
||||
@ -324,8 +355,8 @@ static int efi_image_probe ( struct image *image ) {
|
||||
image->len, &handle ) ) != 0 ) {
|
||||
/* Not an EFI image */
|
||||
rc = -EEFI_LOAD ( efirc );
|
||||
DBGC ( image, "EFIIMAGE %p could not load: %s\n",
|
||||
image, strerror ( rc ) );
|
||||
DBGC ( image, "EFIIMAGE %s could not load: %s\n",
|
||||
image->name, strerror ( rc ) );
|
||||
if ( efirc == EFI_SECURITY_VIOLATION ) {
|
||||
goto err_load_image_security_violation;
|
||||
} else {
|
||||
@ -346,9 +377,75 @@ static int efi_image_probe ( struct image *image ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** EFI image type */
|
||||
struct image_type efi_image_type __image_type ( PROBE_NORMAL ) = {
|
||||
.name = "EFI",
|
||||
.probe = efi_image_probe,
|
||||
.exec = efi_image_exec,
|
||||
/**
|
||||
* Probe EFI PE image
|
||||
*
|
||||
* @v image EFI file
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* The extremely broken UEFI Secure Boot model provides no way for us
|
||||
* to unambiguously determine that a valid EFI executable image was
|
||||
* rejected by LoadImage() because it failed signature verification.
|
||||
* We must therefore use heuristics to guess whether not an image that
|
||||
* was rejected by LoadImage() could still be loaded via a separate PE
|
||||
* loader such as the UEFI shim.
|
||||
*/
|
||||
static int efi_pe_image_probe ( struct image *image ) {
|
||||
const UINT16 magic = ( ( sizeof ( UINTN ) == sizeof ( uint32_t ) ) ?
|
||||
EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC :
|
||||
EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC );
|
||||
union {
|
||||
EFI_IMAGE_DOS_HEADER dos;
|
||||
EFI_IMAGE_OPTIONAL_HEADER_UNION pe;
|
||||
} u;
|
||||
|
||||
/* Check for existence of DOS header */
|
||||
if ( image->len < sizeof ( u.dos ) ) {
|
||||
DBGC ( image, "EFIIMAGE %s too short for DOS header\n",
|
||||
image->name );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
copy_from_user ( &u.dos, image->data, 0, sizeof ( u.dos ) );
|
||||
if ( u.dos.e_magic != EFI_IMAGE_DOS_SIGNATURE ) {
|
||||
DBGC ( image, "EFIIMAGE %s missing MZ signature\n",
|
||||
image->name );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/* Check for existence of PE header */
|
||||
if ( ( image->len < u.dos.e_lfanew ) ||
|
||||
( ( image->len - u.dos.e_lfanew ) < sizeof ( u.pe ) ) ) {
|
||||
DBGC ( image, "EFIIMAGE %s too short for PE header\n",
|
||||
image->name );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
copy_from_user ( &u.pe, image->data, u.dos.e_lfanew, sizeof ( u.pe ) );
|
||||
if ( u.pe.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE ) {
|
||||
DBGC ( image, "EFIIMAGE %s missing PE signature\n",
|
||||
image->name );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/* Check PE header magic */
|
||||
if ( u.pe.Pe32.OptionalHeader.Magic != magic ) {
|
||||
DBGC ( image, "EFIIMAGE %s incorrect magic %04x\n",
|
||||
image->name, u.pe.Pe32.OptionalHeader.Magic );
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** EFI image types */
|
||||
struct image_type efi_image_type[] __image_type ( PROBE_NORMAL ) = {
|
||||
{
|
||||
.name = "EFI",
|
||||
.probe = efi_image_probe,
|
||||
.exec = efi_image_exec,
|
||||
},
|
||||
{
|
||||
.name = "EFIPE",
|
||||
.probe = efi_pe_image_probe,
|
||||
.exec = efi_image_exec,
|
||||
},
|
||||
};
|
||||
|
||||
@ -197,11 +197,6 @@ static int script_exec ( struct image *image ) {
|
||||
size_t saved_offset;
|
||||
int rc;
|
||||
|
||||
/* Temporarily de-register image, so that a "boot" command
|
||||
* doesn't throw us into an execution loop.
|
||||
*/
|
||||
unregister_image ( image );
|
||||
|
||||
/* Preserve state of any currently-running script */
|
||||
saved_offset = script_offset;
|
||||
|
||||
@ -212,10 +207,6 @@ static int script_exec ( struct image *image ) {
|
||||
/* Restore saved state */
|
||||
script_offset = saved_offset;
|
||||
|
||||
/* Re-register image (unless we have been replaced) */
|
||||
if ( ! image->replacement )
|
||||
register_image ( image );
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -320,6 +311,7 @@ static int terminate_on_label_found ( int rc ) {
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int goto_exec ( int argc, char **argv ) {
|
||||
struct image *image = current_image.image;
|
||||
struct goto_options opts;
|
||||
size_t saved_offset;
|
||||
int rc;
|
||||
@ -329,7 +321,7 @@ static int goto_exec ( int argc, char **argv ) {
|
||||
return rc;
|
||||
|
||||
/* Sanity check */
|
||||
if ( ! current_image ) {
|
||||
if ( ! image ) {
|
||||
rc = -ENOTTY;
|
||||
printf ( "Not in a script: %s\n", strerror ( rc ) );
|
||||
return rc;
|
||||
@ -340,10 +332,10 @@ static int goto_exec ( int argc, char **argv ) {
|
||||
|
||||
/* Find label */
|
||||
saved_offset = script_offset;
|
||||
if ( ( rc = process_script ( current_image, goto_find_label,
|
||||
if ( ( rc = process_script ( image, goto_find_label,
|
||||
terminate_on_label_found ) ) != 0 ) {
|
||||
script_offset = saved_offset;
|
||||
DBGC ( current_image, "[%04zx] No such label :%s\n",
|
||||
DBGC ( image, "[%04zx] No such label :%s\n",
|
||||
script_offset, goto_label );
|
||||
return rc;
|
||||
}
|
||||
|
||||
31
src/include/ipxe/efi/Protocol/ShimLock.h
Normal file
31
src/include/ipxe/efi/Protocol/ShimLock.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
|
||||
#define _IPXE_EFI_SHIM_LOCK_PROTOCOL_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI "shim lock" protocol
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( BSD3 );
|
||||
|
||||
#define EFI_SHIM_LOCK_PROTOCOL_GUID \
|
||||
{ 0x605dab50, 0xe046, 0x4300, \
|
||||
{ 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } }
|
||||
|
||||
#define SHIMAPI __asmcall
|
||||
|
||||
typedef
|
||||
EFI_STATUS SHIMAPI
|
||||
(*EFI_SHIM_LOCK_VERIFY) (
|
||||
IN VOID *buffer,
|
||||
IN UINT32 size
|
||||
);
|
||||
|
||||
typedef struct _EFI_SHIM_LOCK_PROTOCOL {
|
||||
EFI_SHIM_LOCK_VERIFY Verify;
|
||||
VOID *Reserved1;
|
||||
VOID *Reserved2;
|
||||
} EFI_SHIM_LOCK_PROTOCOL;
|
||||
|
||||
#endif /*_IPXE_EFI_SHIM_LOCK_PROTOCOL_H */
|
||||
@ -197,6 +197,7 @@ extern EFI_GUID efi_pci_io_protocol_guid;
|
||||
extern EFI_GUID efi_pci_root_bridge_io_protocol_guid;
|
||||
extern EFI_GUID efi_pxe_base_code_protocol_guid;
|
||||
extern EFI_GUID efi_serial_io_protocol_guid;
|
||||
extern EFI_GUID efi_shim_lock_protocol_guid;
|
||||
extern EFI_GUID efi_simple_file_system_protocol_guid;
|
||||
extern EFI_GUID efi_simple_network_protocol_guid;
|
||||
extern EFI_GUID efi_simple_pointer_protocol_guid;
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
#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 */
|
||||
27
src/include/ipxe/efi/efi_image.h
Normal file
27
src/include/ipxe/efi/efi_image.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef _IPXE_EFI_IMAGE_H
|
||||
#define _IPXE_EFI_IMAGE_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI images
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/image.h>
|
||||
|
||||
extern struct image_type efi_image_type[] __image_type ( PROBE_NORMAL );
|
||||
|
||||
/**
|
||||
* Check if EFI image can be loaded directly
|
||||
*
|
||||
* @v image EFI image
|
||||
* @ret can_load EFI image can be loaded directly
|
||||
*/
|
||||
static inline int efi_can_load ( struct image *image ) {
|
||||
|
||||
return ( image->type == efi_image_type );
|
||||
}
|
||||
|
||||
#endif /* _IPXE_EFI_IMAGE_H */
|
||||
24
src/include/ipxe/efi/efi_shim.h
Normal file
24
src/include/ipxe/efi/efi_shim.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _IPXE_EFI_SHIM_H
|
||||
#define _IPXE_EFI_SHIM_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
|
||||
extern int efi_shim_require_loader;
|
||||
extern int efi_shim_allow_pxe;
|
||||
extern int efi_shim_allow_sbat;
|
||||
extern struct image_tag efi_shim __image_tag;
|
||||
|
||||
extern int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
|
||||
wchar_t **cmdline );
|
||||
extern void efi_shim_uninstall ( void );
|
||||
|
||||
#endif /* _IPXE_EFI_SHIM_H */
|
||||
@ -19,6 +19,8 @@ extern int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt,
|
||||
va_list args );
|
||||
extern int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize,
|
||||
const char *fmt, ... );
|
||||
extern int efi_vasprintf ( wchar_t **strp, const char *fmt, va_list args );
|
||||
extern int efi_asprintf ( wchar_t **strp, const char *fmt, ... );
|
||||
|
||||
/**
|
||||
* Write a formatted string to a wide-character buffer
|
||||
|
||||
@ -12,40 +12,11 @@ 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;
|
||||
|
||||
@ -71,56 +42,148 @@ typedef unsigned int min_entropy_t;
|
||||
#define MIN_ENTROPY( bits ) \
|
||||
( ( min_entropy_t ) ( (bits) * MIN_ENTROPY_SCALE ) )
|
||||
|
||||
/* 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>
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable entropy gathering
|
||||
* Adaptive proportion test state
|
||||
*
|
||||
* @ret rc Return status code
|
||||
* 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.
|
||||
*/
|
||||
int entropy_enable ( void );
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable entropy gathering
|
||||
* Startup test state
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
void entropy_disable ( void );
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
* min-entropy per sample
|
||||
/** 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
|
||||
*
|
||||
* @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 );
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
#define ENTROPY_PREFERRED 01 /**< Preferred entropy source */
|
||||
#define ENTROPY_NORMAL 02 /**< Normal entropy source */
|
||||
#define ENTROPY_FALLBACK 03 /**< Fallback entropy source */
|
||||
|
||||
extern int get_entropy_input_tmp ( unsigned int num_samples,
|
||||
uint8_t *tmp, size_t tmp_len );
|
||||
/** @} */
|
||||
|
||||
extern int get_entropy_input_tmp ( min_entropy_t min_entropy, uint8_t *tmp,
|
||||
size_t tmp_len );
|
||||
|
||||
/** Use SHA-256 as the underlying hash algorithm for Hash_df
|
||||
*
|
||||
@ -131,6 +194,22 @@ extern int get_entropy_input_tmp ( unsigned int num_samples,
|
||||
/** 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
|
||||
*
|
||||
@ -145,8 +224,8 @@ extern int get_entropy_input_tmp ( unsigned int num_samples,
|
||||
* 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.
|
||||
* This function is inlined since the entropy amount and length inputs
|
||||
* are always compile-time constants.
|
||||
*/
|
||||
static inline __attribute__ (( always_inline )) int
|
||||
get_entropy_input ( unsigned int min_entropy_bits, void *data, size_t min_len,
|
||||
@ -154,41 +233,16 @@ 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 checks */
|
||||
linker_assert ( ( min_entropy_per_sample() <=
|
||||
MIN_ENTROPY ( 8 * sizeof ( noise_sample_t ) ) ),
|
||||
min_entropy_per_sample_is_impossibly_high );
|
||||
/* Sanity check */
|
||||
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.
|
||||
@ -218,8 +272,10 @@ 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 ( num_samples, tmp, tmp_len ) ) != 0 )
|
||||
if ( ( rc = get_entropy_input_tmp ( MIN_ENTROPY ( min_entropy_bits ),
|
||||
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 )
|
||||
@ -242,4 +298,231 @@ 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 */
|
||||
|
||||
@ -78,6 +78,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
|
||||
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
|
||||
#define ERRFILE_acpimac ( ERRFILE_CORE | 0x00280000 )
|
||||
#define ERRFILE_efi_strings ( ERRFILE_CORE | 0x00290000 )
|
||||
|
||||
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
|
||||
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
|
||||
@ -403,6 +404,8 @@ 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 )
|
||||
#define ERRFILE_efi_shim ( ERRFILE_OTHER | 0x005d0000 )
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
@ -61,16 +61,16 @@ struct image {
|
||||
};
|
||||
|
||||
/** Image is registered */
|
||||
#define IMAGE_REGISTERED 0x00001
|
||||
|
||||
/** Image is selected for execution */
|
||||
#define IMAGE_SELECTED 0x0002
|
||||
#define IMAGE_REGISTERED 0x0001
|
||||
|
||||
/** Image is trusted */
|
||||
#define IMAGE_TRUSTED 0x0004
|
||||
#define IMAGE_TRUSTED 0x0002
|
||||
|
||||
/** Image will be automatically unregistered after execution */
|
||||
#define IMAGE_AUTO_UNREGISTER 0x0008
|
||||
#define IMAGE_AUTO_UNREGISTER 0x0004
|
||||
|
||||
/** Image will be hidden from enumeration */
|
||||
#define IMAGE_HIDDEN 0x0008
|
||||
|
||||
/** An executable image type */
|
||||
struct image_type {
|
||||
@ -150,8 +150,23 @@ struct image_type {
|
||||
/** An executable image type */
|
||||
#define __image_type( probe_order ) __table_entry ( IMAGE_TYPES, probe_order )
|
||||
|
||||
/** An image tag */
|
||||
struct image_tag {
|
||||
/** Name */
|
||||
const char *name;
|
||||
/** Image (weak reference, nullified when image is freed) */
|
||||
struct image *image;
|
||||
};
|
||||
|
||||
/** Image tag table */
|
||||
#define IMAGE_TAGS __table ( struct image_tag, "image_tags" )
|
||||
|
||||
/** An image tag */
|
||||
#define __image_tag __table_entry ( IMAGE_TAGS, 01 )
|
||||
|
||||
extern struct list_head images;
|
||||
extern struct image *current_image;
|
||||
extern struct image_tag current_image;
|
||||
extern struct image_tag selected_image;
|
||||
|
||||
/** Iterate over all registered images */
|
||||
#define for_each_image( image ) \
|
||||
@ -161,15 +176,6 @@ extern struct image *current_image;
|
||||
#define for_each_image_safe( image, tmp ) \
|
||||
list_for_each_entry_safe ( (image), (tmp), &images, list )
|
||||
|
||||
/**
|
||||
* Test for existence of images
|
||||
*
|
||||
* @ret existence Some images exist
|
||||
*/
|
||||
static inline int have_images ( void ) {
|
||||
return ( ! list_empty ( &images ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve first image
|
||||
*
|
||||
@ -187,14 +193,15 @@ extern int image_set_len ( struct image *image, size_t len );
|
||||
extern int image_set_data ( struct image *image, userptr_t data, size_t len );
|
||||
extern int register_image ( struct image *image );
|
||||
extern void unregister_image ( struct image *image );
|
||||
struct image * find_image ( const char *name );
|
||||
extern struct image * find_image ( const char *name );
|
||||
extern struct image * find_image_tag ( struct image_tag *tag );
|
||||
extern int image_exec ( struct image *image );
|
||||
extern int image_replace ( struct image *replacement );
|
||||
extern int image_select ( struct image *image );
|
||||
extern struct image * image_find_selected ( void );
|
||||
extern int image_set_trust ( int require_trusted, int permanent );
|
||||
extern struct image * image_memory ( const char *name, userptr_t data,
|
||||
size_t len );
|
||||
extern const char * image_argument ( struct image *image, const char *key );
|
||||
extern int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf );
|
||||
extern int image_asn1 ( struct image *image, size_t offset,
|
||||
struct asn1_cursor **cursor );
|
||||
@ -249,4 +256,28 @@ static inline void image_untrust ( struct image *image ) {
|
||||
image->flags &= ~IMAGE_TRUSTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark image as hidden
|
||||
*
|
||||
* @v image Image
|
||||
*/
|
||||
static inline void image_hide ( struct image *image ) {
|
||||
image->flags |= IMAGE_HIDDEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag image
|
||||
*
|
||||
* @v image Image
|
||||
* @v tag Image tag
|
||||
* @ret prev Previous tagged image (if any)
|
||||
*/
|
||||
static inline struct image * image_tag ( struct image *image,
|
||||
struct image_tag *tag ) {
|
||||
struct image *prev = tag->image;
|
||||
|
||||
tag->image = image;
|
||||
return prev;
|
||||
}
|
||||
|
||||
#endif /* _IPXE_IMAGE_H */
|
||||
|
||||
@ -22,6 +22,15 @@ 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
|
||||
*
|
||||
@ -577,6 +586,9 @@ 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
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
#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 */
|
||||
@ -1,52 +0,0 @@
|
||||
#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 */
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Form parameters
|
||||
* Request parameters
|
||||
*
|
||||
*/
|
||||
|
||||
@ -12,7 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/list.h>
|
||||
#include <ipxe/refcnt.h>
|
||||
|
||||
/** A form parameter list */
|
||||
/** A request parameter list */
|
||||
struct parameters {
|
||||
/** Reference count */
|
||||
struct refcnt refcnt;
|
||||
@ -24,18 +24,26 @@ struct parameters {
|
||||
struct list_head entries;
|
||||
};
|
||||
|
||||
/** A form parameter */
|
||||
/** A request parameter */
|
||||
struct parameter {
|
||||
/** List of form parameters */
|
||||
/** List of request parameters */
|
||||
struct list_head list;
|
||||
/** Key */
|
||||
const char *key;
|
||||
/** Value */
|
||||
const char *value;
|
||||
/** Flags */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** Request parameter is a form parameter */
|
||||
#define PARAMETER_FORM 0x0001
|
||||
|
||||
/** Request parameter is a header parameter */
|
||||
#define PARAMETER_HEADER 0x0002
|
||||
|
||||
/**
|
||||
* Increment form parameter list reference count
|
||||
* Increment request parameter list reference count
|
||||
*
|
||||
* @v params Parameter list, or NULL
|
||||
* @ret params Parameter list as passed in
|
||||
@ -47,7 +55,7 @@ params_get ( struct parameters *params ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrement form parameter list reference count
|
||||
* Decrement request parameter list reference count
|
||||
*
|
||||
* @v params Parameter list, or NULL
|
||||
*/
|
||||
@ -57,7 +65,7 @@ params_put ( struct parameters *params ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim ownership of form parameter list
|
||||
* Claim ownership of request parameter list
|
||||
*
|
||||
* @v params Parameter list
|
||||
* @ret params Parameter list
|
||||
@ -71,13 +79,14 @@ claim_parameters ( struct parameters *params ) {
|
||||
return params;
|
||||
}
|
||||
|
||||
/** Iterate over all form parameters in a list */
|
||||
/** Iterate over all request parameters in a list */
|
||||
#define for_each_param( param, params ) \
|
||||
list_for_each_entry ( (param), &(params)->entries, list )
|
||||
|
||||
extern struct parameters * find_parameters ( const char *name );
|
||||
extern struct parameters * create_parameters ( const char *name );
|
||||
extern struct parameter * add_parameter ( struct parameters *params,
|
||||
const char *key, const char *value );
|
||||
const char *key, const char *value,
|
||||
unsigned int flags );
|
||||
|
||||
#endif /* _IPXE_PARAMS_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 */
|
||||
|
||||
@ -52,6 +52,9 @@ struct tls_header {
|
||||
/** Change cipher content type */
|
||||
#define TLS_TYPE_CHANGE_CIPHER 20
|
||||
|
||||
/** Change cipher spec magic byte */
|
||||
#define TLS_CHANGE_CIPHER_SPEC 1
|
||||
|
||||
/** Alert content type */
|
||||
#define TLS_TYPE_ALERT 21
|
||||
|
||||
@ -395,6 +398,8 @@ struct tls_connection {
|
||||
struct io_buffer rx_header_iobuf;
|
||||
/** List of received data buffers */
|
||||
struct list_head rx_data;
|
||||
/** Received handshake fragment */
|
||||
struct io_buffer *rx_handshake;
|
||||
};
|
||||
|
||||
/** RX I/O buffer size
|
||||
|
||||
@ -84,7 +84,7 @@ struct uri {
|
||||
const char *equery;
|
||||
/** Fragment (with original URI encoding) */
|
||||
const char *efragment;
|
||||
/** Form parameters */
|
||||
/** Request parameters */
|
||||
struct parameters *params;
|
||||
} __attribute__ (( packed ));
|
||||
|
||||
|
||||
17
src/include/usr/shimmgmt.h
Normal file
17
src/include/usr/shimmgmt.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef _USR_SHIMMGMT_H
|
||||
#define _USR_SHIMMGMT_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI shim management
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <ipxe/image.h>
|
||||
|
||||
extern int shim ( struct image *image, int require_loader, int allow_pxe,
|
||||
int allow_sbat );
|
||||
|
||||
#endif /* _USR_SHIMMGMT_H */
|
||||
@ -143,6 +143,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = {
|
||||
"PxeBaseCode" },
|
||||
{ &efi_serial_io_protocol_guid,
|
||||
"SerialIo" },
|
||||
{ &efi_shim_lock_protocol_guid,
|
||||
"ShimLock" },
|
||||
{ &efi_simple_file_system_protocol_guid,
|
||||
"SimpleFileSystem" },
|
||||
{ &efi_simple_network_protocol_guid,
|
||||
|
||||
@ -25,10 +25,8 @@ 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
|
||||
*
|
||||
@ -36,22 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
*
|
||||
*/
|
||||
|
||||
/** 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
|
||||
struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK );
|
||||
|
||||
/** Time (in 100ns units) to delay waiting for timer tick
|
||||
*
|
||||
@ -76,9 +59,6 @@ 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 );
|
||||
|
||||
@ -91,6 +71,12 @@ 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;
|
||||
}
|
||||
|
||||
@ -147,7 +133,7 @@ static int efi_entropy_tick ( void ) {
|
||||
* @ret noise Noise sample
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_get_noise_ticks ( noise_sample_t *noise ) {
|
||||
static int efi_get_noise ( noise_sample_t *noise ) {
|
||||
int before;
|
||||
int after;
|
||||
int rc;
|
||||
@ -172,70 +158,10 @@ static int efi_get_noise_ticks ( noise_sample_t *noise ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
/** 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,
|
||||
};
|
||||
|
||||
@ -43,14 +43,21 @@ 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 */
|
||||
@ -69,6 +76,8 @@ 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 */
|
||||
@ -84,8 +93,18 @@ struct efi_file {
|
||||
size_t ( * read ) ( struct efi_file_reader *reader );
|
||||
};
|
||||
|
||||
/** An EFI fixed device path file */
|
||||
struct efi_file_path {
|
||||
/** EFI file */
|
||||
struct efi_file file;
|
||||
/** Device path */
|
||||
EFI_DEVICE_PATH_PROTOCOL *path;
|
||||
/** EFI handle */
|
||||
EFI_HANDLE handle;
|
||||
};
|
||||
|
||||
static struct efi_file efi_file_root;
|
||||
static struct efi_file efi_file_initrd;
|
||||
static struct efi_file_path efi_file_initrd;
|
||||
|
||||
/**
|
||||
* Free EFI file
|
||||
@ -121,7 +140,7 @@ static struct image * efi_file_find ( const char *name ) {
|
||||
struct image *image;
|
||||
|
||||
/* Find image */
|
||||
list_for_each_entry ( image, &images, list ) {
|
||||
for_each_image ( image ) {
|
||||
if ( strcasecmp ( image->name, name ) == 0 )
|
||||
return image;
|
||||
}
|
||||
@ -231,8 +250,8 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
|
||||
len = 0;
|
||||
for_each_image ( image ) {
|
||||
|
||||
/* Ignore currently executing image */
|
||||
if ( image == current_image )
|
||||
/* Skip hidden images */
|
||||
if ( image->flags & IMAGE_HIDDEN )
|
||||
continue;
|
||||
|
||||
/* Pad to alignment boundary */
|
||||
@ -276,10 +295,12 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
|
||||
* Open fixed file
|
||||
*
|
||||
* @v file EFI file
|
||||
* @v wname Filename
|
||||
* @v new New EFI file
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFI_STATUS efi_file_open_fixed ( struct efi_file *file,
|
||||
const wchar_t *wname,
|
||||
EFI_FILE_PROTOCOL **new ) {
|
||||
|
||||
/* Increment reference count */
|
||||
@ -288,7 +309,8 @@ static EFI_STATUS efi_file_open_fixed ( struct efi_file *file,
|
||||
/* Return opened file */
|
||||
*new = &file->file;
|
||||
|
||||
DBGC ( file, "EFIFILE %s opened\n", efi_file_name ( file ) );
|
||||
DBGC ( file, "EFIFILE %s opened via %ls\n",
|
||||
efi_file_name ( file ), wname );
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -305,6 +327,36 @@ static void efi_file_image ( struct efi_file *file, struct image *image ) {
|
||||
file->read = efi_file_read_image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open image-backed file
|
||||
*
|
||||
* @v image Image
|
||||
* @v wname Filename
|
||||
* @v new New EFI file
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFI_STATUS efi_file_open_image ( struct image *image,
|
||||
const wchar_t *wname,
|
||||
EFI_FILE_PROTOCOL **new ) {
|
||||
struct efi_file *file;
|
||||
|
||||
/* Allocate and initialise file */
|
||||
file = zalloc ( sizeof ( *file ) );
|
||||
if ( ! file )
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
ref_init ( &file->refcnt, efi_file_free );
|
||||
memcpy ( &file->file, &efi_file_root.file, sizeof ( file->file ) );
|
||||
memcpy ( &file->load, &efi_file_root.load, sizeof ( file->load ) );
|
||||
efi_file_image ( file, image_get ( image ) );
|
||||
|
||||
/* Return opened file */
|
||||
*new = &file->file;
|
||||
|
||||
DBGC ( file, "EFIFILE %s opened via %ls\n",
|
||||
efi_file_name ( file ), wname );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open file
|
||||
*
|
||||
@ -320,9 +372,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
|
||||
CHAR16 *wname, UINT64 mode, UINT64 attributes __unused ) {
|
||||
struct efi_file *file = container_of ( this, struct efi_file, file );
|
||||
char buf[ wcslen ( wname ) + 1 /* NUL */ ];
|
||||
struct efi_file *new_file;
|
||||
struct image *image;
|
||||
char *name;
|
||||
char *sep;
|
||||
|
||||
/* Convert name to ASCII */
|
||||
snprintf ( buf, sizeof ( buf ), "%ls", wname );
|
||||
@ -336,7 +388,7 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
|
||||
|
||||
/* Allow root directory itself to be opened */
|
||||
if ( ( name[0] == '\0' ) || ( name[0] == '.' ) )
|
||||
return efi_file_open_fixed ( &efi_file_root, new );
|
||||
return efi_file_open_fixed ( &efi_file_root, wname, new );
|
||||
|
||||
/* Fail unless opening from the root */
|
||||
if ( file != &efi_file_root ) {
|
||||
@ -352,29 +404,28 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
|
||||
return EFI_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
/* Allow magic initrd to be opened */
|
||||
if ( strcasecmp ( name, efi_file_initrd.name ) == 0 )
|
||||
return efi_file_open_fixed ( &efi_file_initrd, new );
|
||||
/* Allow registered images to be opened */
|
||||
if ( ( image = efi_file_find ( name ) ) != NULL )
|
||||
return efi_file_open_image ( image, wname, new );
|
||||
|
||||
/* Identify image */
|
||||
image = efi_file_find ( name );
|
||||
if ( ! image ) {
|
||||
DBGC ( file, "EFIFILE %s does not exist\n", name );
|
||||
return EFI_NOT_FOUND;
|
||||
/* Allow magic initrd to be opened */
|
||||
if ( strcasecmp ( name, efi_file_initrd.file.name ) == 0 ) {
|
||||
return efi_file_open_fixed ( &efi_file_initrd.file, wname,
|
||||
new );
|
||||
}
|
||||
|
||||
/* Allocate and initialise file */
|
||||
new_file = zalloc ( sizeof ( *new_file ) );
|
||||
if ( ! new_file )
|
||||
return EFI_OUT_OF_RESOURCES;
|
||||
ref_init ( &file->refcnt, efi_file_free );
|
||||
memcpy ( &new_file->file, &efi_file_root.file,
|
||||
sizeof ( new_file->file ) );
|
||||
efi_file_image ( new_file, image_get ( image ) );
|
||||
*new = &new_file->file;
|
||||
DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) );
|
||||
/* Allow currently selected image to be opened as "grub*.efi",
|
||||
* to work around buggy versions of the UEFI shim.
|
||||
*/
|
||||
if ( ( strncasecmp ( name, "grub", 4 ) == 0 ) &&
|
||||
( ( sep = strrchr ( name, '.' ) ) != NULL ) &&
|
||||
( strcasecmp ( sep, ".efi" ) == 0 ) &&
|
||||
( ( image = find_image_tag ( &selected_image ) ) != NULL ) ) {
|
||||
return efi_file_open_image ( image, wname, new );
|
||||
}
|
||||
|
||||
return 0;
|
||||
DBGC ( file, "EFIFILE %ls does not exist\n", wname );
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -488,13 +539,21 @@ static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len,
|
||||
/* Construct directory entries for image-backed files */
|
||||
index = file->pos;
|
||||
for_each_image ( image ) {
|
||||
if ( index-- == 0 ) {
|
||||
efi_file_image ( &entry, image );
|
||||
efirc = efi_file_info ( &entry, len, data );
|
||||
if ( efirc == 0 )
|
||||
file->pos++;
|
||||
return efirc;
|
||||
}
|
||||
|
||||
/* Skip hidden images */
|
||||
if ( image->flags & IMAGE_HIDDEN )
|
||||
continue;
|
||||
|
||||
/* Skip preceding images */
|
||||
if ( index-- )
|
||||
continue;
|
||||
|
||||
/* Construct directory entry */
|
||||
efi_file_image ( &entry, image );
|
||||
efirc = efi_file_info ( &entry, len, data );
|
||||
if ( efirc == 0 )
|
||||
file->pos++;
|
||||
return efirc;
|
||||
}
|
||||
|
||||
/* No more entries */
|
||||
@ -528,7 +587,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, file->pos );
|
||||
efi_file_name ( file ), pos, ( ( size_t ) ( pos + *len ) ) );
|
||||
*len = file->read ( &reader );
|
||||
assert ( ( pos + *len ) == file->pos );
|
||||
|
||||
@ -684,6 +743,44 @@ 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 ),
|
||||
@ -700,29 +797,58 @@ static struct efi_file efi_file_root = {
|
||||
.SetInfo = efi_file_set_info,
|
||||
.Flush = efi_file_flush,
|
||||
},
|
||||
.load = {
|
||||
.LoadFile = efi_file_load,
|
||||
},
|
||||
.image = NULL,
|
||||
.name = "",
|
||||
};
|
||||
|
||||
/** Magic initrd file */
|
||||
static struct efi_file efi_file_initrd = {
|
||||
.refcnt = REF_INIT ( ref_no_free ),
|
||||
.file = {
|
||||
.Revision = EFI_FILE_PROTOCOL_REVISION,
|
||||
.Open = efi_file_open,
|
||||
.Close = efi_file_close,
|
||||
.Delete = efi_file_delete,
|
||||
.Read = efi_file_read,
|
||||
.Write = efi_file_write,
|
||||
.GetPosition = efi_file_get_position,
|
||||
.SetPosition = efi_file_set_position,
|
||||
.GetInfo = efi_file_get_info,
|
||||
.SetInfo = efi_file_set_info,
|
||||
.Flush = efi_file_flush,
|
||||
/** 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,
|
||||
},
|
||||
.image = NULL,
|
||||
.name = "initrd.magic",
|
||||
.read = efi_file_read_initrd,
|
||||
.end = {
|
||||
.Type = END_DEVICE_PATH_TYPE,
|
||||
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
||||
.Length[0] = sizeof ( efi_file_initrd_path.end ),
|
||||
},
|
||||
};
|
||||
|
||||
/** Magic initrd file */
|
||||
static struct efi_file_path efi_file_initrd = {
|
||||
.file = {
|
||||
.refcnt = REF_INIT ( ref_no_free ),
|
||||
.file = {
|
||||
.Revision = EFI_FILE_PROTOCOL_REVISION,
|
||||
.Open = efi_file_open,
|
||||
.Close = efi_file_close,
|
||||
.Delete = efi_file_delete,
|
||||
.Read = efi_file_read,
|
||||
.Write = efi_file_write,
|
||||
.GetPosition = efi_file_get_position,
|
||||
.SetPosition = efi_file_set_position,
|
||||
.GetInfo = efi_file_get_info,
|
||||
.SetInfo = efi_file_set_info,
|
||||
.Flush = efi_file_flush,
|
||||
},
|
||||
.load = {
|
||||
.LoadFile = efi_file_load,
|
||||
},
|
||||
.image = NULL,
|
||||
.name = "initrd.magic",
|
||||
.read = efi_file_read_initrd,
|
||||
},
|
||||
.path = &efi_file_initrd_path.vendor.Header,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -737,7 +863,7 @@ efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused,
|
||||
EFI_FILE_PROTOCOL **file ) {
|
||||
|
||||
DBGC ( &efi_file_root, "EFIFILE open volume\n" );
|
||||
return efi_file_open_fixed ( &efi_file_root, file );
|
||||
return efi_file_open_fixed ( &efi_file_root, L"<volume>", file );
|
||||
}
|
||||
|
||||
/** EFI simple file system protocol */
|
||||
@ -833,6 +959,151 @@ static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = {
|
||||
.WriteDisk = efi_disk_io_write_disk,
|
||||
};
|
||||
|
||||
/**
|
||||
* Claim use of fixed device path
|
||||
*
|
||||
* @v file Fixed device path file
|
||||
* @ret rc Return status code
|
||||
*
|
||||
* The design choice in Linux of using a single fixed device path is
|
||||
* 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. Bootloaders must therefore
|
||||
* be prepared to locate an existing handle and uninstall its device
|
||||
* path protocol instance before installing a new handle with the
|
||||
* required device path.
|
||||
*/
|
||||
static int efi_file_path_claim ( struct efi_file_path *file ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_DEVICE_PATH_PROTOCOL *end;
|
||||
EFI_HANDLE handle;
|
||||
VOID *old;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( file->handle == NULL );
|
||||
|
||||
/* Locate handle with this device path, if any */
|
||||
end = file->path;
|
||||
if ( ( ( efirc = bs->LocateDevicePath ( &efi_device_path_protocol_guid,
|
||||
&end, &handle ) ) != 0 ) ||
|
||||
( end->Type != END_DEVICE_PATH_TYPE ) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Locate device path protocol on this handle */
|
||||
if ( ( ( efirc = bs->HandleProtocol ( handle,
|
||||
&efi_device_path_protocol_guid,
|
||||
&old ) ) != 0 ) ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( file, "EFIFILE %s could not locate %s: %s\n",
|
||||
efi_file_name ( &file->file ),
|
||||
efi_devpath_text ( file->path ), strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Uninstall device path protocol, leaving other protocols untouched */
|
||||
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
|
||||
handle,
|
||||
&efi_device_path_protocol_guid, old,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( file, "EFIFILE %s could not claim %s: %s\n",
|
||||
efi_file_name ( &file->file ),
|
||||
efi_devpath_text ( file->path ), strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
DBGC ( file, "EFIFILE %s claimed %s",
|
||||
efi_file_name ( &file->file ), efi_devpath_text ( file->path ) );
|
||||
DBGC ( file, " from %s\n", efi_handle_name ( handle ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install fixed device path file
|
||||
*
|
||||
* @v file Fixed device path file
|
||||
* @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.
|
||||
*/
|
||||
static int efi_file_path_install ( struct efi_file_path *file ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Sanity check */
|
||||
assert ( file->handle == NULL );
|
||||
|
||||
/* Create a new handle with this device path */
|
||||
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
|
||||
&file->handle,
|
||||
&efi_device_path_protocol_guid, file->path,
|
||||
&efi_load_file2_protocol_guid, &file->file.load,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( file, "EFIFILE %s could not install %s: %s\n",
|
||||
efi_file_name ( &file->file ),
|
||||
efi_devpath_text ( file->path ), strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
DBGC ( file, "EFIFILE %s installed as %s\n",
|
||||
efi_file_name ( &file->file ), efi_devpath_text ( file->path ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall fixed device path file
|
||||
*
|
||||
* @v file Fixed device path file
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static void efi_file_path_uninstall ( struct efi_file_path *file ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Do nothing if file is already uninstalled */
|
||||
if ( ! file->handle )
|
||||
return;
|
||||
|
||||
/* Uninstall protocols. Do this via two separate calls, in
|
||||
* case another executable has already uninstalled the device
|
||||
* path protocol from our handle.
|
||||
*/
|
||||
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
|
||||
file->handle,
|
||||
&efi_device_path_protocol_guid, file->path,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n",
|
||||
efi_file_name ( &file->file ),
|
||||
efi_devpath_text ( file->path ), strerror ( rc ) );
|
||||
/* Continue uninstalling */
|
||||
}
|
||||
if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
|
||||
file->handle,
|
||||
&efi_load_file2_protocol_guid, &file->file.load,
|
||||
NULL ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n",
|
||||
efi_file_name ( &file->file ),
|
||||
efi_guid_ntoa ( &efi_load_file2_protocol_guid ),
|
||||
strerror ( rc ) );
|
||||
/* Continue uninstalling */
|
||||
}
|
||||
|
||||
/* Mark handle as uninstalled */
|
||||
file->handle = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install EFI simple file system protocol
|
||||
*
|
||||
@ -845,6 +1116,7 @@ int efi_file_install ( EFI_HANDLE handle ) {
|
||||
EFI_DISK_IO_PROTOCOL *diskio;
|
||||
void *interface;
|
||||
} diskio;
|
||||
struct image *image;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
@ -903,8 +1175,24 @@ int efi_file_install ( EFI_HANDLE handle ) {
|
||||
}
|
||||
assert ( diskio.diskio == &efi_disk_io_protocol );
|
||||
|
||||
/* Claim Linux initrd fixed device path */
|
||||
if ( ( rc = efi_file_path_claim ( &efi_file_initrd ) ) != 0 )
|
||||
goto err_initrd_claim;
|
||||
|
||||
/* Install Linux initrd fixed device path file if non-empty */
|
||||
for_each_image ( image ) {
|
||||
if ( image->flags & IMAGE_HIDDEN )
|
||||
continue;
|
||||
if ( ( rc = efi_file_path_install ( &efi_file_initrd ) ) != 0 )
|
||||
goto err_initrd_install;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
efi_file_path_uninstall ( &efi_file_initrd );
|
||||
err_initrd_install:
|
||||
err_initrd_claim:
|
||||
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
|
||||
efi_image_handle, handle );
|
||||
err_open:
|
||||
@ -930,6 +1218,9 @@ void efi_file_uninstall ( EFI_HANDLE handle ) {
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Uninstall Linux initrd fixed device path file */
|
||||
efi_file_path_uninstall ( &efi_file_initrd );
|
||||
|
||||
/* Close our own disk I/O protocol */
|
||||
bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
|
||||
efi_image_handle, handle );
|
||||
|
||||
@ -54,6 +54,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
|
||||
#include <ipxe/efi/Protocol/PxeBaseCode.h>
|
||||
#include <ipxe/efi/Protocol/SerialIo.h>
|
||||
#include <ipxe/efi/Protocol/ShimLock.h>
|
||||
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
|
||||
#include <ipxe/efi/Protocol/SimpleNetwork.h>
|
||||
#include <ipxe/efi/Protocol/SimplePointer.h>
|
||||
@ -227,6 +228,10 @@ EFI_GUID efi_pxe_base_code_protocol_guid
|
||||
EFI_GUID efi_serial_io_protocol_guid
|
||||
= EFI_SERIAL_IO_PROTOCOL_GUID;
|
||||
|
||||
/** Shim lock protocol GUID */
|
||||
EFI_GUID efi_shim_lock_protocol_guid
|
||||
= EFI_SHIM_LOCK_PROTOCOL_GUID;
|
||||
|
||||
/** Simple file system protocol GUID */
|
||||
EFI_GUID efi_simple_file_system_protocol_guid
|
||||
= EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
|
||||
|
||||
118
src/interface/efi/efi_rng.c
Normal file
118
src/interface/efi/efi_rng.c
Normal file
@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
402
src/interface/efi/efi_shim.c
Normal file
402
src/interface/efi/efi_shim.c
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/image.h>
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/efi_strings.h>
|
||||
#include <ipxe/efi/efi_shim.h>
|
||||
#include <ipxe/efi/Protocol/PxeBaseCode.h>
|
||||
#include <ipxe/efi/Protocol/ShimLock.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
/**
|
||||
* Require use of a third party loader binary
|
||||
*
|
||||
* The UEFI shim is gradually becoming less capable of directly
|
||||
* executing a Linux kernel image, due to an ever increasing list of
|
||||
* assumptions that it will only ever be used in conjunction with a
|
||||
* second stage loader binary such as GRUB.
|
||||
*
|
||||
* For example: shim will erroneously complain if the image that it
|
||||
* loads and executes does not in turn call in to the "shim lock
|
||||
* protocol" to verify a separate newly loaded binary before calling
|
||||
* ExitBootServices(), even if no such separate binary is used or
|
||||
* required.
|
||||
*
|
||||
* Experience shows that there is unfortunately no point in trying to
|
||||
* get a fix for this upstreamed into shim. We therefore default to
|
||||
* reducing the Secure Boot attack surface by removing, where
|
||||
* possible, this spurious requirement for the use of an additional
|
||||
* second stage loader.
|
||||
*
|
||||
* This option may be used to require the use of an additional second
|
||||
* stage loader binary, in case this behaviour is ever desirable.
|
||||
*/
|
||||
int efi_shim_require_loader = 0;
|
||||
|
||||
/**
|
||||
* Allow use of PXE base code protocol
|
||||
*
|
||||
* We provide shim with access to all of the relevant downloaded files
|
||||
* via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim
|
||||
* will instead try to redownload the files via TFTP since it prefers
|
||||
* to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle.
|
||||
*
|
||||
* Experience shows that there is unfortunately no point in trying to
|
||||
* get a fix for this upstreamed into shim. We therefore default to
|
||||
* working around this undesirable behaviour by stopping the PXE base
|
||||
* code protocol before invoking shim.
|
||||
*
|
||||
* This option may be used to allow shim to use the PXE base code
|
||||
* protocol, in case this behaviour is ever desirable.
|
||||
*/
|
||||
int efi_shim_allow_pxe = 0;
|
||||
|
||||
/**
|
||||
* Allow SBAT variable access
|
||||
*
|
||||
* The UEFI shim implements a fairly nicely designed revocation
|
||||
* mechanism designed around the concept of security generations.
|
||||
* Unfortunately nobody in the shim community has thus far added the
|
||||
* relevant metadata to the Linux kernel, with the result that current
|
||||
* versions of shim are incapable of booting current versions of the
|
||||
* Linux kernel.
|
||||
*
|
||||
* Experience shows that there is unfortunately no point in trying to
|
||||
* get a fix for this upstreamed into shim. We therefore default to
|
||||
* working around this undesirable behaviour by patching data read
|
||||
* from the "SbatLevel" variable used to hold SBAT configuration.
|
||||
*
|
||||
* This option may be used to allow shim unpatched access to the
|
||||
* "SbatLevel" variable, in case this behaviour is ever desirable.
|
||||
*/
|
||||
int efi_shim_allow_sbat = 0;
|
||||
|
||||
/** UEFI shim image */
|
||||
struct image_tag efi_shim __image_tag = {
|
||||
.name = "SHIM",
|
||||
};
|
||||
|
||||
/** Original GetMemoryMap() function */
|
||||
static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map;
|
||||
|
||||
/** Original ExitBootServices() function */
|
||||
static EFI_EXIT_BOOT_SERVICES efi_shim_orig_exit_boot_services;
|
||||
|
||||
/** Original SetVariable() function */
|
||||
static EFI_SET_VARIABLE efi_shim_orig_set_variable;
|
||||
|
||||
/** Original GetVariable() function */
|
||||
static EFI_GET_VARIABLE efi_shim_orig_get_variable;
|
||||
|
||||
/** Verify read from SbatLevel variable */
|
||||
static int efi_shim_sbatlevel_verify;
|
||||
|
||||
/**
|
||||
* Check if variable is SbatLevel
|
||||
*
|
||||
* @v name Variable name
|
||||
* @v guid Variable namespace GUID
|
||||
* @ret is_sbatlevel Variable is SbatLevel
|
||||
*/
|
||||
static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) {
|
||||
static CHAR16 sbatlevel[] = L"SbatLevel";
|
||||
EFI_GUID *shimlock = &efi_shim_lock_protocol_guid;
|
||||
|
||||
return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) &&
|
||||
( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock UEFI shim
|
||||
*
|
||||
*/
|
||||
static void efi_shim_unlock ( void ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
uint8_t empty[0];
|
||||
union {
|
||||
EFI_SHIM_LOCK_PROTOCOL *lock;
|
||||
void *interface;
|
||||
} u;
|
||||
EFI_STATUS efirc;
|
||||
|
||||
/* Locate shim lock protocol */
|
||||
if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid,
|
||||
NULL, &u.interface ) ) == 0 ) {
|
||||
u.lock->Verify ( empty, sizeof ( empty ) );
|
||||
DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap GetMemoryMap()
|
||||
*
|
||||
* @v len Memory map size
|
||||
* @v map Memory map
|
||||
* @v key Memory map key
|
||||
* @v desclen Descriptor size
|
||||
* @v descver Descriptor version
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFIAPI EFI_STATUS efi_shim_get_memory_map ( UINTN *len,
|
||||
EFI_MEMORY_DESCRIPTOR *map,
|
||||
UINTN *key, UINTN *desclen,
|
||||
UINT32 *descver ) {
|
||||
|
||||
/* Unlock shim */
|
||||
if ( ! efi_shim_require_loader )
|
||||
efi_shim_unlock();
|
||||
|
||||
/* Hand off to original GetMemoryMap() */
|
||||
return efi_shim_orig_get_memory_map ( len, map, key, desclen,
|
||||
descver );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap ExitBootServices()
|
||||
*
|
||||
* @v handle Image handle
|
||||
* @v key Memory map key
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFIAPI EFI_STATUS efi_shim_exit_boot_services ( EFI_HANDLE handle,
|
||||
UINTN key ) {
|
||||
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
|
||||
|
||||
/* Restore original runtime services functions */
|
||||
rs->GetVariable = efi_shim_orig_get_variable;
|
||||
rs->SetVariable = efi_shim_orig_set_variable;
|
||||
|
||||
/* Hand off to original ExitBootServices() */
|
||||
return efi_shim_orig_exit_boot_services ( handle, key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap SetVariable()
|
||||
*
|
||||
* @v name Variable name
|
||||
* @v guid Variable namespace GUID
|
||||
* @v attrs Attributes
|
||||
* @v len Buffer size
|
||||
* @v data Data buffer
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFI_STATUS EFIAPI
|
||||
efi_shim_set_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 attrs,
|
||||
UINTN len, VOID *data ) {
|
||||
EFI_STATUS efirc;
|
||||
|
||||
/* Call original SetVariable() */
|
||||
efirc = efi_shim_orig_set_variable ( name, guid, attrs, len, data );
|
||||
|
||||
/* Allow verification of SbatLevel variable content */
|
||||
if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) {
|
||||
DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name );
|
||||
DBGC_HDA ( &efi_shim, 0, data, len );
|
||||
efi_shim_sbatlevel_verify = 1;
|
||||
}
|
||||
|
||||
return efirc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap GetVariable()
|
||||
*
|
||||
* @v name Variable name
|
||||
* @v guid Variable namespace GUID
|
||||
* @v attrs Attributes to fill in
|
||||
* @v len Buffer size
|
||||
* @v data Data buffer
|
||||
* @ret efirc EFI status code
|
||||
*/
|
||||
static EFI_STATUS EFIAPI
|
||||
efi_shim_get_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 *attrs,
|
||||
UINTN *len, VOID *data ) {
|
||||
char *value = data;
|
||||
EFI_STATUS efirc;
|
||||
|
||||
/* Call original GetVariable() */
|
||||
efirc = efi_shim_orig_get_variable ( name, guid, attrs, len, data );
|
||||
|
||||
/* Patch SbatLevel variable if applicable */
|
||||
if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) {
|
||||
if ( efi_shim_allow_sbat ) {
|
||||
DBGC ( &efi_shim, "SHIM allowing read from %ls:\n",
|
||||
name );
|
||||
} else if ( efi_shim_sbatlevel_verify ) {
|
||||
DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n",
|
||||
name );
|
||||
efi_shim_sbatlevel_verify = 0;
|
||||
} else {
|
||||
DBGC ( &efi_shim, "SHIM patching read from %ls:\n",
|
||||
name );
|
||||
value[0] = '\0';
|
||||
}
|
||||
DBGC_HDA ( &efi_shim, 0, data, *len );
|
||||
}
|
||||
|
||||
return efirc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inhibit use of PXE base code
|
||||
*
|
||||
* @v handle EFI handle
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_shim_inhibit_pxe ( EFI_HANDLE handle ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
union {
|
||||
EFI_PXE_BASE_CODE_PROTOCOL *pxe;
|
||||
void *interface;
|
||||
} u;
|
||||
EFI_STATUS efirc;
|
||||
int rc;
|
||||
|
||||
/* Locate PXE base code */
|
||||
if ( ( efirc = bs->OpenProtocol ( handle,
|
||||
&efi_pxe_base_code_protocol_guid,
|
||||
&u.interface, efi_image_handle, NULL,
|
||||
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n",
|
||||
strerror ( rc ) );
|
||||
goto err_no_base;
|
||||
}
|
||||
|
||||
/* Stop PXE base code */
|
||||
if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) {
|
||||
rc = -EEFI ( efirc );
|
||||
DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n",
|
||||
strerror ( rc ) );
|
||||
goto err_stop;
|
||||
}
|
||||
|
||||
/* Success */
|
||||
rc = 0;
|
||||
DBGC ( &efi_shim, "SHIM stopped PXE base code\n" );
|
||||
|
||||
err_stop:
|
||||
bs->CloseProtocol ( handle, &efi_pxe_base_code_protocol_guid,
|
||||
efi_image_handle, NULL );
|
||||
err_no_base:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update command line
|
||||
*
|
||||
* @v shim Shim image
|
||||
* @v cmdline Command line to update
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) {
|
||||
wchar_t *shimcmdline;
|
||||
int len;
|
||||
int rc;
|
||||
|
||||
/* Construct new command line */
|
||||
len = ( shim->cmdline ?
|
||||
efi_asprintf ( &shimcmdline, "%s %s", shim->name,
|
||||
shim->cmdline ) :
|
||||
efi_asprintf ( &shimcmdline, "%s %ls", shim->name,
|
||||
*cmdline ) );
|
||||
if ( len < 0 ) {
|
||||
rc = len;
|
||||
DBGC ( &efi_shim, "SHIM could not construct command line: "
|
||||
"%s\n", strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Replace command line */
|
||||
free ( *cmdline );
|
||||
*cmdline = shimcmdline;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install UEFI shim special handling
|
||||
*
|
||||
* @v shim Shim image
|
||||
* @v handle EFI device handle
|
||||
* @v cmdline Command line to update
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int efi_shim_install ( struct image *shim, EFI_HANDLE handle,
|
||||
wchar_t **cmdline ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
|
||||
int rc;
|
||||
|
||||
/* Stop PXE base code */
|
||||
if ( ( ! efi_shim_allow_pxe ) &&
|
||||
( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Update command line */
|
||||
if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Record original boot and runtime services functions */
|
||||
efi_shim_orig_get_memory_map = bs->GetMemoryMap;
|
||||
efi_shim_orig_exit_boot_services = bs->ExitBootServices;
|
||||
efi_shim_orig_set_variable = rs->SetVariable;
|
||||
efi_shim_orig_get_variable = rs->GetVariable;
|
||||
|
||||
/* Wrap relevant boot and runtime services functions */
|
||||
bs->GetMemoryMap = efi_shim_get_memory_map;
|
||||
bs->ExitBootServices = efi_shim_exit_boot_services;
|
||||
rs->SetVariable = efi_shim_set_variable;
|
||||
rs->GetVariable = efi_shim_get_variable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall UEFI shim special handling
|
||||
*
|
||||
*/
|
||||
void efi_shim_uninstall ( void ) {
|
||||
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
|
||||
EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
|
||||
|
||||
/* Restore original boot and runtime services functions */
|
||||
bs->GetMemoryMap = efi_shim_orig_get_memory_map;
|
||||
bs->ExitBootServices = efi_shim_orig_exit_boot_services;
|
||||
rs->SetVariable = efi_shim_orig_set_variable;
|
||||
rs->GetVariable = efi_shim_orig_get_variable;
|
||||
}
|
||||
@ -25,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ipxe/vsprintf.h>
|
||||
#include <ipxe/efi/efi_strings.h>
|
||||
|
||||
@ -150,3 +152,45 @@ int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ) {
|
||||
va_end ( args );
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a formatted string to newly allocated memory
|
||||
*
|
||||
* @v wstrp Pointer to hold allocated string
|
||||
* @v fmt Format string
|
||||
* @v args Arguments corresponding to the format string
|
||||
* @ret len Length of formatted string (in wide characters)
|
||||
*/
|
||||
int efi_vasprintf ( wchar_t **wstrp, const char *fmt, va_list args ) {
|
||||
size_t len;
|
||||
va_list args_tmp;
|
||||
|
||||
/* Calculate length needed for string */
|
||||
va_copy ( args_tmp, args );
|
||||
len = ( efi_vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 );
|
||||
va_end ( args_tmp );
|
||||
|
||||
/* Allocate and fill string */
|
||||
*wstrp = malloc ( len * sizeof ( **wstrp ) );
|
||||
if ( ! *wstrp )
|
||||
return -ENOMEM;
|
||||
return efi_vsnprintf ( *wstrp, len, fmt, args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a formatted string to newly allocated memory
|
||||
*
|
||||
* @v wstrp Pointer to hold allocated string
|
||||
* @v fmt Format string
|
||||
* @v ... Arguments corresponding to the format string
|
||||
* @ret len Length of formatted string (in wide characters)
|
||||
*/
|
||||
int efi_asprintf ( wchar_t **wstrp, const char *fmt, ... ) {
|
||||
va_list args;
|
||||
int len;
|
||||
|
||||
va_start ( args, fmt );
|
||||
len = efi_vasprintf ( wstrp, fmt, args );
|
||||
va_end ( args );
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ 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";
|
||||
|
||||
@ -55,6 +57,13 @@ 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;
|
||||
}
|
||||
|
||||
@ -95,7 +104,10 @@ static int linux_get_noise ( noise_sample_t *noise ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 );
|
||||
/** 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,
|
||||
};
|
||||
|
||||
@ -830,7 +830,9 @@ static int http_transfer_complete ( struct http_transaction *http ) {
|
||||
*/
|
||||
static int http_format_headers ( struct http_transaction *http, char *buf,
|
||||
size_t len ) {
|
||||
struct parameters *params = http->uri->params;
|
||||
struct http_request_header *header;
|
||||
struct parameter *param;
|
||||
size_t used;
|
||||
size_t remaining;
|
||||
char *line;
|
||||
@ -844,7 +846,7 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
|
||||
DBGC2 ( http, "HTTP %p TX %s\n", http, buf );
|
||||
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
|
||||
|
||||
/* Construct all headers */
|
||||
/* Construct all fixed headers */
|
||||
for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) {
|
||||
|
||||
/* Determine header value length */
|
||||
@ -869,6 +871,23 @@ static int http_format_headers ( struct http_transaction *http, char *buf,
|
||||
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
|
||||
}
|
||||
|
||||
/* Construct parameter headers, if any */
|
||||
if ( params ) {
|
||||
|
||||
/* Construct all parameter headers */
|
||||
for_each_param ( param, params ) {
|
||||
|
||||
/* Skip non-header parameters */
|
||||
if ( ! ( param->flags & PARAMETER_HEADER ) )
|
||||
continue;
|
||||
|
||||
/* Add parameter */
|
||||
used += ssnprintf ( ( buf + used ), ( len - used ),
|
||||
"%s: %s\r\n", param->key,
|
||||
param->value );
|
||||
}
|
||||
}
|
||||
|
||||
/* Construct terminating newline */
|
||||
used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" );
|
||||
|
||||
@ -1851,14 +1870,15 @@ static struct http_state http_trailers = {
|
||||
*/
|
||||
|
||||
/**
|
||||
* Construct HTTP parameter list
|
||||
* Construct HTTP form parameter list
|
||||
*
|
||||
* @v params Parameter list
|
||||
* @v buf Buffer to contain HTTP POST parameters
|
||||
* @v len Length of buffer
|
||||
* @ret len Length of parameter list (excluding terminating NUL)
|
||||
*/
|
||||
static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
|
||||
static size_t http_form_params ( struct parameters *params, char *buf,
|
||||
size_t len ) {
|
||||
struct parameter *param;
|
||||
ssize_t remaining = len;
|
||||
size_t frag_len;
|
||||
@ -1867,6 +1887,10 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
|
||||
len = 0;
|
||||
for_each_param ( param, params ) {
|
||||
|
||||
/* Skip non-form parameters */
|
||||
if ( ! ( param->flags & PARAMETER_FORM ) )
|
||||
continue;
|
||||
|
||||
/* Add the "&", if applicable */
|
||||
if ( len ) {
|
||||
if ( remaining > 0 )
|
||||
@ -1904,53 +1928,59 @@ static size_t http_params ( struct parameters *params, char *buf, size_t len ) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open HTTP transaction for simple GET URI
|
||||
* Open HTTP transaction for simple URI
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v uri Request URI
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) {
|
||||
|
||||
return http_open ( xfer, &http_get, uri, NULL, NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Open HTTP transaction for simple POST URI
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v uri Request URI
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
|
||||
int http_open_uri ( struct interface *xfer, struct uri *uri ) {
|
||||
struct parameters *params = uri->params;
|
||||
struct http_request_content content;
|
||||
struct http_method *method;
|
||||
const char *type;
|
||||
void *data;
|
||||
size_t len;
|
||||
size_t check_len;
|
||||
int rc;
|
||||
|
||||
/* Calculate length of parameter list */
|
||||
len = http_params ( params, NULL, 0 );
|
||||
/* Calculate length of form parameter list, if any */
|
||||
len = ( params ? http_form_params ( params, NULL, 0 ) : 0 );
|
||||
|
||||
/* Allocate temporary parameter list */
|
||||
data = zalloc ( len + 1 /* NUL */ );
|
||||
if ( ! data ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
/* Use POST if and only if there are form parameters */
|
||||
if ( len ) {
|
||||
|
||||
/* Use POST */
|
||||
method = &http_post;
|
||||
type = "application/x-www-form-urlencoded";
|
||||
|
||||
/* Allocate temporary form parameter list */
|
||||
data = zalloc ( len + 1 /* NUL */ );
|
||||
if ( ! data ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
/* Construct temporary form parameter list */
|
||||
check_len = http_form_params ( params, data,
|
||||
( len + 1 /* NUL */ ) );
|
||||
assert ( check_len == len );
|
||||
|
||||
} else {
|
||||
|
||||
/* Use GET */
|
||||
method = &http_get;
|
||||
type = NULL;
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
/* Construct temporary parameter list */
|
||||
check_len = http_params ( params, data, ( len + 1 /* NUL */ ) );
|
||||
assert ( check_len == len );
|
||||
|
||||
/* Construct request content */
|
||||
content.type = "application/x-www-form-urlencoded";
|
||||
content.type = type;
|
||||
content.data = data;
|
||||
content.len = len;
|
||||
|
||||
/* Open HTTP transaction */
|
||||
if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 )
|
||||
if ( ( rc = http_open ( xfer, method, uri, NULL, &content ) ) != 0 )
|
||||
goto err_open;
|
||||
|
||||
err_open:
|
||||
@ -1959,23 +1989,6 @@ static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open HTTP transaction for simple URI
|
||||
*
|
||||
* @v xfer Data transfer interface
|
||||
* @v uri Request URI
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int http_open_uri ( struct interface *xfer, struct uri *uri ) {
|
||||
|
||||
/* Open GET/POST URI as applicable */
|
||||
if ( uri->params ) {
|
||||
return http_open_post_uri ( xfer, uri );
|
||||
} else {
|
||||
return http_open_get_uri ( xfer, uri );
|
||||
}
|
||||
}
|
||||
|
||||
/* Drag in HTTP extensions */
|
||||
REQUIRING_SYMBOL ( http_open );
|
||||
REQUIRE_OBJECT ( config_http );
|
||||
|
||||
@ -46,6 +46,7 @@ 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>
|
||||
|
||||
@ -86,6 +87,10 @@ 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 \
|
||||
@ -281,6 +286,9 @@ 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 );
|
||||
|
||||
@ -736,16 +744,20 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
|
||||
"MaxConnections=1%c"
|
||||
"InitialR2T=Yes%c"
|
||||
"ImmediateData=No%c"
|
||||
"MaxRecvDataSegmentLength=8192%c"
|
||||
"MaxBurstLength=262144%c"
|
||||
"FirstBurstLength=65536%c"
|
||||
"MaxRecvDataSegmentLength=%d%c"
|
||||
"MaxBurstLength=%d%c"
|
||||
"FirstBurstLength=%d%c"
|
||||
"DefaultTime2Wait=0%c"
|
||||
"DefaultTime2Retain=0%c"
|
||||
"MaxOutstandingR2T=1%c"
|
||||
"DataPDUInOrder=Yes%c"
|
||||
"DataSequenceInOrder=Yes%c"
|
||||
"ErrorRecoveryLevel=0%c",
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
|
||||
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 );
|
||||
}
|
||||
|
||||
return used;
|
||||
@ -908,6 +920,31 @@ 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
|
||||
*
|
||||
@ -1148,6 +1185,7 @@ 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 },
|
||||
@ -1847,6 +1885,24 @@ 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
|
||||
*
|
||||
@ -1862,6 +1918,7 @@ 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 ),
|
||||
|
||||
197
src/net/tls.c
197
src/net/tls.c
@ -388,6 +388,7 @@ static void free_tls ( struct refcnt *refcnt ) {
|
||||
list_del ( &iobuf->list );
|
||||
free_iob ( iobuf );
|
||||
}
|
||||
free_iob ( tls->rx_handshake );
|
||||
x509_chain_put ( tls->certs );
|
||||
x509_chain_put ( tls->chain );
|
||||
x509_root_put ( tls->root );
|
||||
@ -1682,9 +1683,14 @@ static int tls_send_certificate_verify ( struct tls_connection *tls ) {
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_send_change_cipher ( struct tls_connection *tls ) {
|
||||
static const uint8_t change_cipher[1] = { 1 };
|
||||
static const struct {
|
||||
uint8_t spec;
|
||||
} __attribute__ (( packed )) change_cipher = {
|
||||
.spec = TLS_CHANGE_CIPHER_SPEC,
|
||||
};
|
||||
|
||||
return tls_send_plaintext ( tls, TLS_TYPE_CHANGE_CIPHER,
|
||||
change_cipher, sizeof ( change_cipher ) );
|
||||
&change_cipher, sizeof ( change_cipher ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1731,20 +1737,27 @@ static int tls_send_finished ( struct tls_connection *tls ) {
|
||||
* Receive new Change Cipher record
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v data Plaintext record
|
||||
* @v len Length of plaintext record
|
||||
* @v iobuf I/O buffer
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_new_change_cipher ( struct tls_connection *tls,
|
||||
const void *data, size_t len ) {
|
||||
struct io_buffer *iobuf ) {
|
||||
const struct {
|
||||
uint8_t spec;
|
||||
} __attribute__ (( packed )) *change_cipher = iobuf->data;
|
||||
size_t len = iob_len ( iobuf );
|
||||
int rc;
|
||||
|
||||
if ( ( len != 1 ) || ( *( ( uint8_t * ) data ) != 1 ) ) {
|
||||
/* Sanity check */
|
||||
if ( ( sizeof ( *change_cipher ) != len ) ||
|
||||
( change_cipher->spec != TLS_CHANGE_CIPHER_SPEC ) ) {
|
||||
DBGC ( tls, "TLS %p received invalid Change Cipher\n", tls );
|
||||
DBGC_HD ( tls, data, len );
|
||||
DBGC_HD ( tls, change_cipher, len );
|
||||
return -EINVAL_CHANGE_CIPHER;
|
||||
}
|
||||
iob_pull ( iobuf, sizeof ( *change_cipher ) );
|
||||
|
||||
/* Change receive cipher spec */
|
||||
if ( ( rc = tls_change_cipher ( tls, &tls->rx_cipherspec_pending,
|
||||
&tls->rx_cipherspec ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not activate RX cipher: %s\n",
|
||||
@ -1760,25 +1773,27 @@ static int tls_new_change_cipher ( struct tls_connection *tls,
|
||||
* Receive new Alert record
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v data Plaintext record
|
||||
* @v len Length of plaintext record
|
||||
* @v iobuf I/O buffer
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_new_alert ( struct tls_connection *tls, const void *data,
|
||||
size_t len ) {
|
||||
static int tls_new_alert ( struct tls_connection *tls,
|
||||
struct io_buffer *iobuf ) {
|
||||
const struct {
|
||||
uint8_t level;
|
||||
uint8_t description;
|
||||
char next[0];
|
||||
} __attribute__ (( packed )) *alert = data;
|
||||
} __attribute__ (( packed )) *alert = iobuf->data;
|
||||
size_t len = iob_len ( iobuf );
|
||||
|
||||
/* Sanity check */
|
||||
if ( sizeof ( *alert ) != len ) {
|
||||
DBGC ( tls, "TLS %p received overlength Alert\n", tls );
|
||||
DBGC_HD ( tls, data, len );
|
||||
DBGC_HD ( tls, alert, len );
|
||||
return -EINVAL_ALERT;
|
||||
}
|
||||
iob_pull ( iobuf, sizeof ( *alert ) );
|
||||
|
||||
/* Handle alert */
|
||||
switch ( alert->level ) {
|
||||
case TLS_ALERT_WARNING:
|
||||
DBGC ( tls, "TLS %p received warning alert %d\n",
|
||||
@ -2392,38 +2407,33 @@ static int tls_new_finished ( struct tls_connection *tls,
|
||||
* Receive new Handshake record
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v data Plaintext record
|
||||
* @v len Length of plaintext record
|
||||
* @v iobuf I/O buffer
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_new_handshake ( struct tls_connection *tls,
|
||||
const void *data, size_t len ) {
|
||||
size_t remaining = len;
|
||||
struct io_buffer *iobuf ) {
|
||||
size_t remaining;
|
||||
int rc;
|
||||
|
||||
while ( remaining ) {
|
||||
while ( ( remaining = iob_len ( iobuf ) ) ) {
|
||||
const struct {
|
||||
uint8_t type;
|
||||
tls24_t length;
|
||||
uint8_t payload[0];
|
||||
} __attribute__ (( packed )) *handshake = data;
|
||||
} __attribute__ (( packed )) *handshake = iobuf->data;
|
||||
const void *payload;
|
||||
size_t payload_len;
|
||||
size_t record_len;
|
||||
|
||||
/* Parse header */
|
||||
if ( sizeof ( *handshake ) > remaining ) {
|
||||
DBGC ( tls, "TLS %p received underlength Handshake\n",
|
||||
tls );
|
||||
DBGC_HD ( tls, data, remaining );
|
||||
return -EINVAL_HANDSHAKE;
|
||||
/* Leave remaining fragment unconsumed */
|
||||
break;
|
||||
}
|
||||
payload_len = tls_uint24 ( &handshake->length );
|
||||
if ( payload_len > ( remaining - sizeof ( *handshake ) ) ) {
|
||||
DBGC ( tls, "TLS %p received overlength Handshake\n",
|
||||
tls );
|
||||
DBGC_HD ( tls, data, len );
|
||||
return -EINVAL_HANDSHAKE;
|
||||
/* Leave remaining fragment unconsumed */
|
||||
break;
|
||||
}
|
||||
payload = &handshake->payload;
|
||||
record_len = ( sizeof ( *handshake ) + payload_len );
|
||||
@ -2470,15 +2480,60 @@ static int tls_new_handshake ( struct tls_connection *tls,
|
||||
* which are explicitly excluded).
|
||||
*/
|
||||
if ( handshake->type != TLS_HELLO_REQUEST )
|
||||
tls_add_handshake ( tls, data, record_len );
|
||||
tls_add_handshake ( tls, handshake, record_len );
|
||||
|
||||
/* Abort on failure */
|
||||
if ( rc != 0 )
|
||||
return rc;
|
||||
|
||||
/* Move to next handshake record */
|
||||
data += record_len;
|
||||
remaining -= record_len;
|
||||
iob_pull ( iobuf, record_len );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive new unknown record
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v iobuf I/O buffer
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_new_unknown ( struct tls_connection *tls __unused,
|
||||
struct io_buffer *iobuf ) {
|
||||
|
||||
/* RFC4346 says that we should just ignore unknown record types */
|
||||
iob_pull ( iobuf, iob_len ( iobuf ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Receive new data record
|
||||
*
|
||||
* @v tls TLS connection
|
||||
* @v rx_data List of received data buffers
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int tls_new_data ( struct tls_connection *tls,
|
||||
struct list_head *rx_data ) {
|
||||
struct io_buffer *iobuf;
|
||||
int rc;
|
||||
|
||||
/* Fail unless we are ready to receive data */
|
||||
if ( ! tls_ready ( tls ) )
|
||||
return -ENOTCONN;
|
||||
|
||||
/* Deliver each I/O buffer in turn */
|
||||
while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
|
||||
list ) ) ) {
|
||||
list_del ( &iobuf->list );
|
||||
if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
|
||||
iobuf ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not deliver data: "
|
||||
"%s\n", tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -2494,41 +2549,18 @@ static int tls_new_handshake ( struct tls_connection *tls,
|
||||
*/
|
||||
static int tls_new_record ( struct tls_connection *tls, unsigned int type,
|
||||
struct list_head *rx_data ) {
|
||||
struct io_buffer *iobuf;
|
||||
int ( * handler ) ( struct tls_connection *tls, const void *data,
|
||||
size_t len );
|
||||
int ( * handler ) ( struct tls_connection *tls,
|
||||
struct io_buffer *iobuf );
|
||||
struct io_buffer *tmp = NULL;
|
||||
struct io_buffer **iobuf;
|
||||
int rc;
|
||||
|
||||
/* Deliver data records to the plainstream interface */
|
||||
if ( type == TLS_TYPE_DATA ) {
|
||||
/* Deliver data records as-is to the plainstream interface */
|
||||
if ( type == TLS_TYPE_DATA )
|
||||
return tls_new_data ( tls, rx_data );
|
||||
|
||||
/* Fail unless we are ready to receive data */
|
||||
if ( ! tls_ready ( tls ) )
|
||||
return -ENOTCONN;
|
||||
|
||||
/* Deliver each I/O buffer in turn */
|
||||
while ( ( iobuf = list_first_entry ( rx_data, struct io_buffer,
|
||||
list ) ) ) {
|
||||
list_del ( &iobuf->list );
|
||||
if ( ( rc = xfer_deliver_iob ( &tls->plainstream,
|
||||
iobuf ) ) != 0 ) {
|
||||
DBGC ( tls, "TLS %p could not deliver data: "
|
||||
"%s\n", tls, strerror ( rc ) );
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For all other records, merge into a single I/O buffer */
|
||||
iobuf = iob_concatenate ( rx_data );
|
||||
if ( ! iobuf ) {
|
||||
DBGC ( tls, "TLS %p could not concatenate non-data record "
|
||||
"type %d\n", tls, type );
|
||||
return -ENOMEM_RX_CONCAT;
|
||||
}
|
||||
|
||||
/* Determine handler */
|
||||
/* Determine handler and fragment buffer */
|
||||
iobuf = &tmp;
|
||||
switch ( type ) {
|
||||
case TLS_TYPE_CHANGE_CIPHER:
|
||||
handler = tls_new_change_cipher;
|
||||
@ -2538,19 +2570,44 @@ static int tls_new_record ( struct tls_connection *tls, unsigned int type,
|
||||
break;
|
||||
case TLS_TYPE_HANDSHAKE:
|
||||
handler = tls_new_handshake;
|
||||
iobuf = &tls->rx_handshake;
|
||||
break;
|
||||
default:
|
||||
/* RFC4346 says that we should just ignore unknown
|
||||
* record types.
|
||||
*/
|
||||
handler = NULL;
|
||||
DBGC ( tls, "TLS %p ignoring record type %d\n", tls, type );
|
||||
DBGC ( tls, "TLS %p unknown record type %d\n", tls, type );
|
||||
handler = tls_new_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Handle record and free I/O buffer */
|
||||
rc = ( handler ? handler ( tls, iobuf->data, iob_len ( iobuf ) ) : 0 );
|
||||
free_iob ( iobuf );
|
||||
/* Merge into a single I/O buffer */
|
||||
if ( *iobuf )
|
||||
list_add ( &(*iobuf)->list, rx_data );
|
||||
*iobuf = iob_concatenate ( rx_data );
|
||||
if ( ! *iobuf ) {
|
||||
DBGC ( tls, "TLS %p could not concatenate non-data record "
|
||||
"type %d\n", tls, type );
|
||||
rc = -ENOMEM_RX_CONCAT;
|
||||
goto err_concatenate;
|
||||
}
|
||||
|
||||
/* Handle record */
|
||||
if ( ( rc = handler ( tls, *iobuf ) ) != 0 )
|
||||
goto err_handle;
|
||||
|
||||
/* Discard I/O buffer if empty */
|
||||
if ( ! iob_len ( *iobuf ) ) {
|
||||
free_iob ( *iobuf );
|
||||
*iobuf = NULL;
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
assert ( tmp == NULL );
|
||||
|
||||
return 0;
|
||||
|
||||
err_handle:
|
||||
free_iob ( *iobuf );
|
||||
*iobuf = NULL;
|
||||
err_concatenate:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -601,6 +601,12 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unregister any existing ProxyDHCP or PXEBS settings */
|
||||
if ( ( settings = find_settings ( PROXYDHCP_SETTINGS_NAME ) ) != NULL )
|
||||
unregister_settings ( settings );
|
||||
if ( ( settings = find_settings ( PXEBS_SETTINGS_NAME ) ) != NULL )
|
||||
unregister_settings ( settings );
|
||||
|
||||
/* Perform ProxyDHCP if applicable */
|
||||
if ( dhcp->proxy_offer /* Have ProxyDHCP offer */ &&
|
||||
( ! dhcp->no_pxedhcp ) /* ProxyDHCP not disabled */ ) {
|
||||
|
||||
@ -42,8 +42,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
/**
|
||||
* Generate entropy samples for external testing
|
||||
*
|
||||
* @v source Entropy source
|
||||
*/
|
||||
static void entropy_sample_test_exec ( void ) {
|
||||
static void entropy_sample ( struct entropy_source *source ) {
|
||||
static noise_sample_t samples[SAMPLE_BLOCKSIZE];
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
@ -53,22 +54,35 @@ static void entropy_sample_test_exec ( void ) {
|
||||
for ( i = 0 ; i < ( SAMPLE_COUNT / SAMPLE_BLOCKSIZE ) ; i++ ) {
|
||||
|
||||
/* Collect one block of samples */
|
||||
rc = entropy_enable();
|
||||
rc = entropy_enable ( source );
|
||||
ok ( rc == 0 );
|
||||
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
|
||||
rc = get_noise ( &samples[j] );
|
||||
rc = get_noise ( source, &samples[j] );
|
||||
ok ( rc == 0 );
|
||||
}
|
||||
entropy_disable();
|
||||
entropy_disable ( source );
|
||||
|
||||
/* Print out sample values */
|
||||
for ( j = 0 ; j < SAMPLE_BLOCKSIZE ; j++ ) {
|
||||
printf ( "SAMPLE %d %d\n", ( i * SAMPLE_BLOCKSIZE + j ),
|
||||
samples[j] );
|
||||
printf ( "SAMPLE %s %d %d\n", source->name,
|
||||
( 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",
|
||||
|
||||
@ -92,15 +92,17 @@ struct uri_churi_test {
|
||||
const char *expected;
|
||||
};
|
||||
|
||||
/** A form parameter URI test list */
|
||||
/** A request parameter URI test list */
|
||||
struct uri_params_test_list {
|
||||
/** Key */
|
||||
const char *key;
|
||||
/** Value */
|
||||
const char *value;
|
||||
/** Flags */
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
/** A form parameter URI test */
|
||||
/** A request parameter URI test */
|
||||
struct uri_params_test {
|
||||
/** URI string */
|
||||
const char *string;
|
||||
@ -403,9 +405,9 @@ static void uri_churi_okx ( struct uri_churi_test *test, const char *file,
|
||||
#define uri_churi_ok( test ) uri_churi_okx ( test, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Report form parameter URI test list result
|
||||
* Report request parameter URI test list result
|
||||
*
|
||||
* @v test Form parameter URI test
|
||||
* @v test Request parameter URI test
|
||||
* @v uri URI
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
@ -428,6 +430,7 @@ static void uri_params_list_okx ( struct uri_params_test *test,
|
||||
file, line );
|
||||
okx ( strcmp ( param->value, list->value ) == 0,
|
||||
file, line );
|
||||
okx ( param->flags == list->flags, file, line );
|
||||
list++;
|
||||
}
|
||||
okx ( list->key == NULL, file, line );
|
||||
@ -437,9 +440,9 @@ static void uri_params_list_okx ( struct uri_params_test *test,
|
||||
uri_params_list_okx ( test, __FILE__, __LINE__ )
|
||||
|
||||
/**
|
||||
* Report form parameter URI test result
|
||||
* Report request parameter URI test result
|
||||
*
|
||||
* @v test Form parameter URI test
|
||||
* @v test Request parameter URI test
|
||||
* @v file Test code file
|
||||
* @v line Test code line
|
||||
*/
|
||||
@ -456,7 +459,8 @@ static void uri_params_okx ( struct uri_params_test *test, const char *file,
|
||||
okx ( params != NULL, file, line );
|
||||
if ( params ) {
|
||||
for ( list = test->list ; list->key ; list++ ) {
|
||||
param = add_parameter ( params, list->key, list->value);
|
||||
param = add_parameter ( params, list->key, list->value,
|
||||
list->flags );
|
||||
okx ( param != NULL, file, line );
|
||||
}
|
||||
}
|
||||
@ -879,27 +883,31 @@ static struct uri_churi_test uri_churi[] = {
|
||||
}
|
||||
};
|
||||
|
||||
/** Form parameter URI test list */
|
||||
/** Request parameter URI test list */
|
||||
static struct uri_params_test_list uri_params_list[] = {
|
||||
{
|
||||
"vendor",
|
||||
"10ec",
|
||||
PARAMETER_FORM,
|
||||
},
|
||||
{
|
||||
"device",
|
||||
"8139",
|
||||
PARAMETER_FORM,
|
||||
},
|
||||
{
|
||||
"uuid",
|
||||
"f59fac00-758f-498f-9fe5-87d790045d94",
|
||||
PARAMETER_HEADER,
|
||||
},
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
}
|
||||
};
|
||||
|
||||
/** Form parameter URI test */
|
||||
/** Request parameter URI test */
|
||||
static struct uri_params_test uri_params = {
|
||||
"http://boot.ipxe.org/demo/boot.php##params",
|
||||
{
|
||||
@ -912,23 +920,26 @@ static struct uri_params_test uri_params = {
|
||||
uri_params_list,
|
||||
};
|
||||
|
||||
/** Named form parameter URI test list */
|
||||
/** Named request parameter URI test list */
|
||||
static struct uri_params_test_list uri_named_params_list[] = {
|
||||
{
|
||||
"mac",
|
||||
"00:1e:65:80:d3:b6",
|
||||
PARAMETER_FORM,
|
||||
},
|
||||
{
|
||||
"serial",
|
||||
"LXTQ20Z1139322762F2000",
|
||||
PARAMETER_FORM,
|
||||
},
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
}
|
||||
};
|
||||
|
||||
/** Named form parameter URI test */
|
||||
/** Named request parameter URI test */
|
||||
static struct uri_params_test uri_named_params = {
|
||||
"http://192.168.100.4:3001/register##params=foo",
|
||||
{
|
||||
@ -996,7 +1007,7 @@ static void uri_test_exec ( void ) {
|
||||
/* Current working URI tests */
|
||||
uri_churi_ok ( uri_churi );
|
||||
|
||||
/* Form parameter URI tests */
|
||||
/* Request parameter URI tests */
|
||||
uri_params_ok ( &uri_params );
|
||||
uri_params_ok ( &uri_named_params );
|
||||
}
|
||||
|
||||
@ -156,15 +156,21 @@ int imgacquire ( const char *name_uri, unsigned long timeout,
|
||||
* @v image Executable/loadable image
|
||||
*/
|
||||
void imgstat ( struct image *image ) {
|
||||
struct image_tag *tag;
|
||||
|
||||
printf ( "%s : %zd bytes", image->name, image->len );
|
||||
if ( image->type )
|
||||
printf ( " [%s]", image->type->name );
|
||||
for_each_table_entry ( tag, IMAGE_TAGS ) {
|
||||
if ( tag->image == image )
|
||||
printf ( " [%s]", tag->name );
|
||||
}
|
||||
if ( image->flags & IMAGE_TRUSTED )
|
||||
printf ( " [TRUSTED]" );
|
||||
if ( image->flags & IMAGE_SELECTED )
|
||||
printf ( " [SELECTED]" );
|
||||
if ( image->flags & IMAGE_AUTO_UNREGISTER )
|
||||
printf ( " [AUTOFREE]" );
|
||||
if ( image->flags & IMAGE_HIDDEN )
|
||||
printf ( " [HIDDEN]" );
|
||||
if ( image->cmdline )
|
||||
printf ( " \"%s\"", image->cmdline );
|
||||
printf ( "\n" );
|
||||
|
||||
61
src/usr/shimmgmt.c
Normal file
61
src/usr/shimmgmt.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 );
|
||||
|
||||
#include <ipxe/efi/efi.h>
|
||||
#include <ipxe/efi/efi_shim.h>
|
||||
#include <usr/shimmgmt.h>
|
||||
|
||||
/** @file
|
||||
*
|
||||
* EFI shim management
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Set shim image
|
||||
*
|
||||
* @v image Shim image, or NULL to clear shim
|
||||
* @v require_loader Require use of a third party loader
|
||||
* @v allow_pxe Allow use of PXE base code
|
||||
* @v allow_sbat Allow SBAT variable access
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
int shim ( struct image *image, int require_loader, int allow_pxe,
|
||||
int allow_sbat ) {
|
||||
|
||||
/* Record (or clear) shim image */
|
||||
image_tag ( image, &efi_shim );
|
||||
|
||||
/* Avoid including image in constructed initrd */
|
||||
if ( image )
|
||||
image_hide ( image );
|
||||
|
||||
/* Record configuration */
|
||||
efi_shim_require_loader = require_loader;
|
||||
efi_shim_allow_pxe = allow_pxe;
|
||||
efi_shim_allow_sbat = allow_sbat;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -168,6 +168,9 @@
|
||||
*/
|
||||
#define EFI_IMAGE_ALIGN 0x1000
|
||||
|
||||
/** Number of data directory entries */
|
||||
#define NUMBER_OF_DIRECTORY_ENTRIES 8
|
||||
|
||||
struct elf_file {
|
||||
void *data;
|
||||
size_t len;
|
||||
@ -178,6 +181,7 @@ struct pe_section {
|
||||
struct pe_section *next;
|
||||
EFI_IMAGE_SECTION_HEADER hdr;
|
||||
void ( * fixup ) ( struct pe_section *section );
|
||||
int hidden;
|
||||
uint8_t contents[0];
|
||||
};
|
||||
|
||||
@ -191,7 +195,6 @@ struct pe_relocs {
|
||||
|
||||
struct pe_header {
|
||||
EFI_IMAGE_DOS_HEADER dos;
|
||||
uint8_t padding[128];
|
||||
EFI_IMAGE_NT_HEADERS nt;
|
||||
};
|
||||
|
||||
@ -205,7 +208,11 @@ static struct pe_header efi_pe_header = {
|
||||
.FileHeader = {
|
||||
.TimeDateStamp = 0x10d1a884,
|
||||
.SizeOfOptionalHeader =
|
||||
sizeof ( efi_pe_header.nt.OptionalHeader ),
|
||||
( sizeof ( efi_pe_header.nt.OptionalHeader ) -
|
||||
( ( EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES -
|
||||
NUMBER_OF_DIRECTORY_ENTRIES ) *
|
||||
sizeof ( efi_pe_header.nt.OptionalHeader.
|
||||
DataDirectory[0] ) ) ),
|
||||
.Characteristics = ( EFI_IMAGE_FILE_DLL |
|
||||
EFI_IMAGE_FILE_MACHINE |
|
||||
EFI_IMAGE_FILE_EXECUTABLE_IMAGE ),
|
||||
@ -218,15 +225,17 @@ static struct pe_header efi_pe_header = {
|
||||
.FileAlignment = EFI_FILE_ALIGN,
|
||||
.SizeOfImage = EFI_IMAGE_ALIGN,
|
||||
.SizeOfHeaders = sizeof ( efi_pe_header ),
|
||||
.NumberOfRvaAndSizes =
|
||||
EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES,
|
||||
.NumberOfRvaAndSizes = NUMBER_OF_DIRECTORY_ENTRIES,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/** Command-line options */
|
||||
struct options {
|
||||
/** PE32+ subsystem type */
|
||||
unsigned int subsystem;
|
||||
/** Create hybrid BIOS/UEFI binary */
|
||||
int hybrid;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -633,10 +642,12 @@ static struct pe_section * process_section ( struct elf_file *elf,
|
||||
/* Update RVA limits */
|
||||
start = new->hdr.VirtualAddress;
|
||||
end = ( start + new->hdr.Misc.VirtualSize );
|
||||
if ( ( ! *applicable_start ) || ( *applicable_start >= start ) )
|
||||
*applicable_start = start;
|
||||
if ( *applicable_end < end )
|
||||
*applicable_end = end;
|
||||
if ( ! new->hidden ) {
|
||||
if ( ( ! *applicable_start ) || ( *applicable_start >= start ) )
|
||||
*applicable_start = start;
|
||||
if ( *applicable_end < end )
|
||||
*applicable_end = end;
|
||||
}
|
||||
if ( data_start < code_end )
|
||||
data_start = code_end;
|
||||
if ( data_mid < data_start )
|
||||
@ -656,8 +667,11 @@ static struct pe_section * process_section ( struct elf_file *elf,
|
||||
( data_end - data_mid );
|
||||
|
||||
/* Update remaining file header fields */
|
||||
pe_header->nt.FileHeader.NumberOfSections++;
|
||||
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( new->hdr );
|
||||
if ( ! new->hidden ) {
|
||||
pe_header->nt.FileHeader.NumberOfSections++;
|
||||
pe_header->nt.OptionalHeader.SizeOfHeaders +=
|
||||
sizeof ( new->hdr );
|
||||
}
|
||||
pe_header->nt.OptionalHeader.SizeOfImage =
|
||||
efi_image_align ( data_end );
|
||||
|
||||
@ -673,10 +687,12 @@ static struct pe_section * process_section ( struct elf_file *elf,
|
||||
* @v nsyms Number of symbol table entries
|
||||
* @v rel Relocation record
|
||||
* @v pe_reltab PE relocation table to fill in
|
||||
* @v opts Options
|
||||
*/
|
||||
static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
|
||||
const Elf_Sym *syms, unsigned int nsyms,
|
||||
const Elf_Rel *rel, struct pe_relocs **pe_reltab ) {
|
||||
const Elf_Rel *rel, struct pe_relocs **pe_reltab,
|
||||
struct options *opts ) {
|
||||
unsigned int type = ELF_R_TYPE ( rel->r_info );
|
||||
unsigned int sym = ELF_R_SYM ( rel->r_info );
|
||||
unsigned int mrel = ELF_MREL ( elf->ehdr->e_machine, type );
|
||||
@ -739,6 +755,15 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
|
||||
* loaded.
|
||||
*/
|
||||
break;
|
||||
case ELF_MREL ( EM_X86_64, R_X86_64_32 ) :
|
||||
/* Ignore 32-bit relocations in a hybrid
|
||||
* 32-bit BIOS and 64-bit UEFI binary,
|
||||
* otherwise fall through to treat as an
|
||||
* unknown type.
|
||||
*/
|
||||
if ( opts->hybrid )
|
||||
break;
|
||||
/* fallthrough */
|
||||
default:
|
||||
eprintf ( "Unrecognised relocation type %d\n", type );
|
||||
exit ( 1 );
|
||||
@ -753,9 +778,11 @@ static void process_reloc ( struct elf_file *elf, const Elf_Shdr *shdr,
|
||||
* @v shdr ELF section header
|
||||
* @v stride Relocation record size
|
||||
* @v pe_reltab PE relocation table to fill in
|
||||
* @v opts Options
|
||||
*/
|
||||
static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
|
||||
size_t stride, struct pe_relocs **pe_reltab ) {
|
||||
size_t stride, struct pe_relocs **pe_reltab,
|
||||
struct options *opts ) {
|
||||
const Elf_Shdr *symtab;
|
||||
const Elf_Sym *syms;
|
||||
const Elf_Rel *rel;
|
||||
@ -773,7 +800,7 @@ static void process_relocs ( struct elf_file *elf, const Elf_Shdr *shdr,
|
||||
rel = ( elf->data + shdr->sh_offset );
|
||||
nrels = ( shdr->sh_size / stride );
|
||||
for ( i = 0 ; i < nrels ; i++ ) {
|
||||
process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab );
|
||||
process_reloc ( elf, shdr, syms, nsyms, rel, pe_reltab, opts );
|
||||
rel = ( ( ( const void * ) rel ) + stride );
|
||||
}
|
||||
}
|
||||
@ -914,6 +941,7 @@ static void write_pe_file ( struct pe_header *pe_header,
|
||||
FILE *pe ) {
|
||||
struct pe_section *section;
|
||||
unsigned long fpos = 0;
|
||||
unsigned int count = 0;
|
||||
|
||||
/* Align length of headers */
|
||||
fpos = pe_header->nt.OptionalHeader.SizeOfHeaders =
|
||||
@ -931,19 +959,26 @@ static void write_pe_file ( struct pe_header *pe_header,
|
||||
}
|
||||
|
||||
/* Write file header */
|
||||
if ( fwrite ( pe_header, sizeof ( *pe_header ), 1, pe ) != 1 ) {
|
||||
if ( fwrite ( pe_header,
|
||||
( offsetof ( typeof ( *pe_header ), nt.OptionalHeader ) +
|
||||
pe_header->nt.FileHeader.SizeOfOptionalHeader ),
|
||||
1, pe ) != 1 ) {
|
||||
perror ( "Could not write PE header" );
|
||||
exit ( 1 );
|
||||
}
|
||||
|
||||
/* Write section headers */
|
||||
for ( section = pe_sections ; section ; section = section->next ) {
|
||||
if ( section->hidden )
|
||||
continue;
|
||||
if ( fwrite ( §ion->hdr, sizeof ( section->hdr ),
|
||||
1, pe ) != 1 ) {
|
||||
perror ( "Could not write section header" );
|
||||
exit ( 1 );
|
||||
}
|
||||
count++;
|
||||
}
|
||||
assert ( count == pe_header->nt.FileHeader.NumberOfSections );
|
||||
|
||||
/* Write sections */
|
||||
for ( section = pe_sections ; section ; section = section->next ) {
|
||||
@ -969,6 +1004,7 @@ static void write_pe_file ( struct pe_header *pe_header,
|
||||
*
|
||||
* @v elf_name ELF file name
|
||||
* @v pe_name PE file name
|
||||
* @v opts Options
|
||||
*/
|
||||
static void elf2pe ( const char *elf_name, const char *pe_name,
|
||||
struct options *opts ) {
|
||||
@ -1012,13 +1048,13 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
|
||||
|
||||
/* Process .rel relocations */
|
||||
process_relocs ( &elf, shdr, sizeof ( Elf_Rel ),
|
||||
&pe_reltab );
|
||||
&pe_reltab, opts );
|
||||
|
||||
} else if ( shdr->sh_type == SHT_RELA ) {
|
||||
|
||||
/* Process .rela relocations */
|
||||
process_relocs ( &elf, shdr, sizeof ( Elf_Rela ),
|
||||
&pe_reltab );
|
||||
&pe_reltab, opts );
|
||||
}
|
||||
}
|
||||
|
||||
@ -1071,11 +1107,12 @@ static int parse_options ( const int argc, char **argv,
|
||||
int option_index = 0;
|
||||
static struct option long_options[] = {
|
||||
{ "subsystem", required_argument, NULL, 's' },
|
||||
{ "hybrid", no_argument, NULL, 'H' },
|
||||
{ "help", 0, NULL, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
if ( ( c = getopt_long ( argc, argv, "s:h",
|
||||
if ( ( c = getopt_long ( argc, argv, "s:Hh",
|
||||
long_options,
|
||||
&option_index ) ) == -1 ) {
|
||||
break;
|
||||
@ -1090,6 +1127,9 @@ static int parse_options ( const int argc, char **argv,
|
||||
exit ( 2 );
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
opts->hybrid = 1;
|
||||
break;
|
||||
case 'h':
|
||||
print_help ( argv[0] );
|
||||
exit ( 0 );
|
||||
|
||||
Reference in New Issue
Block a user