Compare commits

...

10 Commits
usbdisk ... rpl

Author SHA1 Message Date
22bb29eabc [prefix] Add a generic raw image prefix
Provide a generic raw image prefix, which assumes that the iPXE image
has been loaded in its entirety on a paragraph boundary.

The resulting .raw image can be loaded via RPL using an rpld.conf file
such as:

    HOST {
        ethernet = 00:00:00:00:00:00/6;
        FILE {
            path="ipxe.raw";
            load=0x2000;
        };
        execute=0x2000;
    };

Debugged-by: Johannes Heimansberg <git@jhe.dedyn.io>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-18 13:53:26 +00:00
8446a439b3 [initrd] Allow for zero-length initrd files
A zero-length initrd file will currently cause an endless loop during
reshuffling as the empty image is repeatedly swapped with itself.

Fix by terminating the inner loop before considering an image as a
candidate to be swapped with itself.

Reported-by: Pico Mitchell <pico@randomapplications.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-18 01:49:23 +00:00
4039b54ba3 [cloud] Do not enable serial console on EFI platforms
Most EFI firmware builds (including those found on ARM64 instances in
AWS EC2) will already send console output to the serial port.

Do not enable direct serial console output in EFI builds using
CONFIG=cloud.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-17 22:38:38 +00:00
cd3de55ea5 [efi] Record cached DHCPACK from loaded image's device handle, if present
Record the cached DHCPACK obtained from the EFI_PXE_BASE_CODE_PROTOCOL
instance installed on the loaded image's device handle, if present.

This allows a chainloaded UEFI iPXE to reuse the IPv4 address and DHCP
options previously obtained by the built-in PXE stack, as is already
done for a chainloaded BIOS iPXE.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-17 18:11:43 +00:00
d562339fca [efi] Defer autoboot link-layer address and autoexec script probing
The code to detect the autoboot link-layer address and to load the
autoexec script currently runs before the call to initialise() and so
has to function without a working heap.

This requirement can be relaxed by deferring this code to run via an
initialisation function.  This gives the code a normal runtime
environment, but still invokes it early enough to guarantee that the
original loaded image device handle has not yet been invalidated.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-17 17:14:19 +00:00
e39cd79a00 [efi] Split out autoexec script portions of efi_autoboot.c
The "autoboot device" and "autoexec script" functionalities in
efi_autoboot.c are unrelated except in that they both need to be
invoked by efiprefix.c before device drivers are loaded.

Split out the autoexec script portions to a separate file to avoid
potential confusion.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-17 17:14:19 +00:00
057674bb1f [pxe] Split out platform-independent portions of cachedhcp.c
Split out the portions of cachedhcp.c that can be shared between BIOS
and UEFI (both of which can provide a buffer containing a previously
obtained DHCP packet, and neither of which provide a means to
determine the length of this DHCP packet).

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-17 15:59:52 +00:00
19d0fab40f [ath5k] Add missing AR5K_EEPROM_READ in ath5k_eeprom_read_turbo_modes
The GCC11 compiler pointed out something that apparently no previous
compiler noticed: in ath5k_eeprom_pread_turbo_modes, local variable
val is used uninitialized. From what I can see, the code is just
missing an initial AR5K_EEPROM_READ. Add it right before the switch
statement.

Signed-off-by: Bruce Rogers <brogers@suse.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-16 23:35:24 +00:00
fa012dd020 [cloud] Enable IPv6 and HTTPS in cloud boot images
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-16 10:58:42 +00:00
d16535aa4f [cloud] Add utility for importing images to AWS EC2
Add a utility that can be used to upload an iPXE disk image to AWS EC2
as an Amazon Machine Image (AMI).  For example:

  make CONFIG=cloud EMBED=config/cloud/aws.ipxe bin/ipxe.usb

  ../contrib/cloud/aws-import -p -n "iPXE 1.21.1" bin/ipxe.usb

Uploads are performed in parallel across all regions, and use the EBS
direct APIs to avoid the need to store temporary files in S3 or to run
VM import tasks.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2021-02-16 00:27:40 +00:00
17 changed files with 643 additions and 234 deletions

100
contrib/cloud/aws-import Executable file
View File

@ -0,0 +1,100 @@
#!/usr/bin/env python3
import argparse
from base64 import b64encode
from concurrent.futures import ThreadPoolExecutor, as_completed
from hashlib import sha256
from itertools import count
import boto3
BLOCKSIZE = 512 * 1024
def create_snapshot(region, description, image):
"""Create an EBS snapshot"""
client = boto3.client('ebs', region_name=region)
snapshot = client.start_snapshot(VolumeSize=1,
Description=description)
snapshot_id = snapshot['SnapshotId']
with open(image, 'rb') as fh:
for block in count():
data = fh.read(BLOCKSIZE)
if not data:
break
data = data.ljust(BLOCKSIZE, b'\0')
checksum = b64encode(sha256(data).digest()).decode()
client.put_snapshot_block(SnapshotId=snapshot_id,
BlockIndex=block,
BlockData=data,
DataLength=BLOCKSIZE,
Checksum=checksum,
ChecksumAlgorithm='SHA256')
client.complete_snapshot(SnapshotId=snapshot_id,
ChangedBlocksCount=block)
return snapshot_id
def import_image(region, name, architecture, image, public):
"""Import an AMI image"""
client = boto3.client('ec2', region_name=region)
resource = boto3.resource('ec2', region_name=region)
description = '%s (%s)' % (name, architecture)
snapshot_id = create_snapshot(region=region, description=description,
image=image)
client.get_waiter('snapshot_completed').wait(SnapshotIds=[snapshot_id])
image = client.register_image(Architecture=architecture,
BlockDeviceMappings=[{
'DeviceName': '/dev/sda1',
'Ebs': {
'SnapshotId': snapshot_id,
'VolumeType': 'standard',
},
}],
EnaSupport=True,
Name=description,
RootDeviceName='/dev/sda1',
SriovNetSupport='simple',
VirtualizationType='hvm')
image_id = image['ImageId']
client.get_waiter('image_available').wait(ImageIds=[image_id])
if public:
resource.Image(image_id).modify_attribute(Attribute='launchPermission',
OperationType='add',
UserGroups=['all'])
return image_id
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Import AWS EC2 image (AMI)")
parser.add_argument('--architecture', '-a', default='x86_64',
help="CPU architecture")
parser.add_argument('--name', '-n', required=True,
help="Image name")
parser.add_argument('--public', '-p', action='store_true',
help="Make image public")
parser.add_argument('--region', '-r', action='append',
help="AWS region(s)")
parser.add_argument('image', help="iPXE disk image")
args = parser.parse_args()
# Use all regions if none specified
if not args.region:
args.region = sorted(x['RegionName'] for x in
boto3.client('ec2').describe_regions()['Regions'])
# Use one thread per region to maximise parallelism
with ThreadPoolExecutor(max_workers=len(args.region)) as executor:
futures = {executor.submit(import_image,
region=region,
name=args.name,
architecture=args.architecture,
image=args.image,
public=args.public): region
for region in args.region}
results = {futures[future]: future.result()
for future in as_completed(futures)}
# Show created images
for region in args.region:
print("%s: %s" % (region, results[region]))

View File

@ -175,18 +175,18 @@ static int initrd_swap_any ( userptr_t free, size_t free_len ) {
/* Search for adjacent image */
for_each_image ( high ) {
/* If we have found the adjacent image, swap and exit */
if ( high->data == adjacent ) {
initrd_swap ( low, high, free, free_len );
return 1;
}
/* Stop search if all remaining potential
* adjacent images are already in the correct
* order.
*/
if ( high == low )
break;
/* If we have found the adjacent image, swap and exit */
if ( high->data == adjacent ) {
initrd_swap ( low, high, free, free_len );
return 1;
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2013 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 <stdint.h>
#include <ipxe/init.h>
#include <ipxe/cachedhcp.h>
#include <realmode.h>
#include <pxe_api.h>
/** @file
*
* Cached DHCP packet
*
*/
/** Cached DHCPACK physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cached_dhcpack_phys );
#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
/** Colour for debug messages */
#define colour &cached_dhcpack_phys
/**
* Cached DHCPACK initialisation function
*
*/
static void cachedhcp_init ( void ) {
int rc;
/* Do nothing if no cached DHCPACK is present */
if ( ! cached_dhcpack_phys ) {
DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
return;
}
/* Record cached DHCPACK */
if ( ( rc = cachedhcp_record ( phys_to_user ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",
strerror ( rc ) );
return;
}
/* Mark as consumed */
cached_dhcpack_phys = 0;
}
/** Cached DHCPACK initialisation function */
struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = cachedhcp_init,
};

View File

@ -0,0 +1,53 @@
/*
* Raw binary prefix
*
* Assumes that entire image is already loaded as a contiguous block
* on a paragraph boundary and entered in real mode.
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL )
.text
.arch i386
.org 0
.code16
#include <librm.h>
.section ".prefix", "ax", @progbits
.globl _raw_start
_raw_start:
/* Adjust %cs so that %cs:0000 is the start of the image */
movw %cs, %ax
call 1f
1: popw %bx
subw $1b, %bx
shrw $4, %bx
addw %bx, %ax
pushw %ax
pushw $2f
lret
2:
/* Install iPXE */
call install
/* Set up real-mode stack */
movw %bx, %ss
movw $_estack16, %sp
/* Jump to .text16 segment */
pushw %ax
pushw $1f
lret
.section ".text16", "awx", @progbits
1:
/* Run iPXE */
virtcall main
/* Uninstall iPXE */
call uninstall
/* Boot next device */
int $0x18

View File

@ -18,8 +18,13 @@
* Note that the serial port output from an AWS EC2 virtual machine is
* generally available (as the "System Log") only after the instance
* has been stopped.
*
* Enable only for non-EFI builds, on the assumption that the standard
* EFI firmware is likely to already be logging to the serial port.
*/
#ifndef PLATFORM_efi
#define CONSOLE_SERIAL
#endif
/* Log to partition on local disk
*

View File

@ -1,3 +1,7 @@
/* Enable IPv6 and HTTPS */
#define NET_PROTO_IPV6
#define DOWNLOAD_PROTO_HTTPS
/* Allow retrieval of metadata (such as an iPXE boot script) from
* Google Compute Engine metadata server.
*/

View File

@ -25,11 +25,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <realmode.h>
#include <pxe_api.h>
#include <ipxe/cachedhcp.h>
/** @file
*
@ -37,50 +37,33 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
/** Cached DHCPACK physical address
*
* This can be set by the prefix.
*/
uint32_t __bss16 ( cached_dhcpack_phys );
#define cached_dhcpack_phys __use_data16 ( cached_dhcpack_phys )
/** Colour for debug messages */
#define colour &cached_dhcpack_phys
/** Cached DHCPACK */
static struct dhcp_packet *cached_dhcpack;
/** Colour for debug messages */
#define colour &cached_dhcpack
/**
* Cached DHCPACK startup function
* Record cached DHCPACK
*
* @v data DHCPACK packet buffer
* @v max_len Maximum possible length
* @ret rc Return status code
*/
static void cachedhcp_init ( void ) {
int cachedhcp_record ( userptr_t data, size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
size_t max_len;
size_t len;
/* Do nothing if no cached DHCPACK is present */
if ( ! cached_dhcpack_phys ) {
DBGC ( colour, "CACHEDHCP found no cached DHCPACK\n" );
return;
}
/* No reliable way to determine length before parsing packet;
* start by assuming maximum length permitted by PXE.
*/
max_len = sizeof ( BOOTPLAYER_t );
/* Allocate and populate DHCP packet */
dhcppkt = zalloc ( sizeof ( *dhcppkt ) + max_len );
if ( ! dhcppkt ) {
DBGC ( colour, "CACHEDHCP could not allocate copy\n" );
return;
return -ENOMEM;
}
dhcphdr = ( ( ( void * ) dhcppkt ) + sizeof ( *dhcppkt ) );
copy_from_user ( dhcphdr, phys_to_user ( cached_dhcpack_phys ), 0,
max_len );
copy_from_user ( dhcphdr, data, 0, max_len );
dhcppkt_init ( dhcppkt, dhcphdr, max_len );
/* Shrink packet to required length. If reallocation fails,
@ -98,10 +81,11 @@ static void cachedhcp_init ( void ) {
dhcppkt_init ( dhcppkt, dhcphdr, len );
/* Store as cached DHCPACK, and mark original copy as consumed */
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %08x+%zx\n",
cached_dhcpack_phys, len );
DBGC ( colour, "CACHEDHCP found cached DHCPACK at %#08lx+%#zx/%#zx\n",
user_to_phys ( data, 0 ), len, max_len );
cached_dhcpack = dhcppkt;
cached_dhcpack_phys = 0;
return 0;
}
/**
@ -120,11 +104,6 @@ static void cachedhcp_startup ( void ) {
}
}
/** Cached DHCPACK initialisation function */
struct init_fn cachedhcp_init_fn __init_fn ( INIT_NORMAL ) = {
.initialise = cachedhcp_init,
};
/** Cached DHCPACK startup function */
struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
.name = "cachedhcp",

View File

@ -416,6 +416,7 @@ ath5k_eeprom_read_turbo_modes(struct ath5k_hw *ah,
if (ee->ee_version < AR5K_EEPROM_VERSION_5_0)
return 0;
AR5K_EEPROM_READ(o++, val);
switch (mode){
case AR5K_EEPROM_MODE_11A:
ee->ee_switch_settling_turbo[mode] = (val >> 6) & 0x7f;

View File

@ -0,0 +1,17 @@
#ifndef _IPXE_CACHEDHCP_H
#define _IPXE_CACHEDHCP_H
/** @file
*
* Cached DHCP packet
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stddef.h>
#include <ipxe/uaccess.h>
extern int cachedhcp_record ( userptr_t data, size_t max_len );
#endif /* _IPXE_CACHEDHCP_H */

View File

@ -9,6 +9,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
extern void efi_set_autoboot ( void );
#include <ipxe/efi/efi.h>
extern int efi_set_autoboot_ll_addr ( EFI_HANDLE device );
#endif /* _IPXE_EFI_AUTOBOOT_H */

View File

@ -0,0 +1,16 @@
#ifndef _IPXE_EFI_AUTOEXEC_H
#define _IPXE_EFI_AUTOEXEC_H
/** @file
*
* EFI autoexec script
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
extern int efi_autoexec_load ( EFI_HANDLE device );
#endif /* _IPXE_EFI_AUTOEXEC_H */

View File

@ -0,0 +1,16 @@
#ifndef _IPXE_EFI_CACHEDHCP_H
#define _IPXE_EFI_CACHEDHCP_H
/** @file
*
* EFI cached DHCP packet
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
extern int efi_cachedhcp_record ( EFI_HANDLE device );
#endif /* _IPXE_EFI_CACHEDHCP_H */

View File

@ -76,6 +76,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_dummy_sanboot ( ERRFILE_CORE | 0x00240000 )
#define ERRFILE_fdt ( ERRFILE_CORE | 0x00250000 )
#define ERRFILE_dma ( ERRFILE_CORE | 0x00260000 )
#define ERRFILE_cachedhcp ( ERRFILE_CORE | 0x00270000 )
#define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 )
#define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 )
@ -384,6 +385,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_ntlm ( ERRFILE_OTHER | 0x00510000 )
#define ERRFILE_efi_veto ( ERRFILE_OTHER | 0x00520000 )
#define ERRFILE_efi_autoboot ( ERRFILE_OTHER | 0x00530000 )
#define ERRFILE_efi_autoexec ( ERRFILE_OTHER | 0x00540000 )
#define ERRFILE_efi_cachedhcp ( ERRFILE_OTHER | 0x00550000 )
/** @} */

View File

@ -25,40 +25,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_autoboot.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Guid/FileInfo.h>
#include <usr/autoboot.h>
/** @file
*
* EFI automatic booting
* EFI autoboot device
*
*/
/** Autoexec script filename */
#define AUTOEXEC_FILENAME L"autoexec.ipxe"
/** Autoexec script image name */
#define AUTOEXEC_NAME "autoexec.ipxe"
/** Autoexec script (if any) */
static void *efi_autoexec;
/** Autoexec script length */
static size_t efi_autoexec_len;
/**
* Identify autoboot device
*
* @v device Device handle
* @ret rc Return status code
*/
static int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_SIMPLE_NETWORK_PROTOCOL *snp;
@ -93,172 +77,3 @@ static int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
return 0;
}
/**
* Load autoexec script
*
* @v device Device handle
* @ret rc Return status code
*/
static int efi_load_autoexec ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
static wchar_t name[] = AUTOEXEC_FILENAME;
union {
void *interface;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
} u;
struct {
EFI_FILE_INFO info;
CHAR16 name[ sizeof ( name ) / sizeof ( name[0] ) ];
} info;
EFI_FILE_PROTOCOL *root;
EFI_FILE_PROTOCOL *file;
UINTN size;
VOID *data;
EFI_STATUS efirc;
int rc;
/* Sanity check */
assert ( efi_autoexec == NULL );
assert ( efi_autoexec_len == 0 );
/* Open simple file system protocol */
if ( ( efirc = bs->OpenProtocol ( device,
&efi_simple_file_system_protocol_guid,
&u.interface, efi_image_handle,
device,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no filesystem instance: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_filesystem;
}
/* Open root directory */
if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not open volume: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_volume;
}
/* Open autoexec script */
if ( ( efirc = root->Open ( root, &file, name,
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_open;
}
/* Get file information */
size = sizeof ( info );
if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
&info ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not get %ls info: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_getinfo;
}
size = info.info.FileSize;
/* Ignore zero-length files */
if ( ! size ) {
rc = -EINVAL;
DBGC ( device, "EFI %s has zero-length %ls\n",
efi_handle_name ( device ), name );
goto err_empty;
}
/* Allocate temporary copy */
if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
&data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not allocate %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_alloc;
}
/* Read file */
if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not read %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_read;
}
/* Record autoexec script */
efi_autoexec = data;
efi_autoexec_len = size;
data = NULL;
DBGC ( device, "EFI %s found %ls\n",
efi_handle_name ( device ), name );
/* Success */
rc = 0;
err_read:
if ( data )
bs->FreePool ( data );
err_alloc:
err_empty:
err_getinfo:
file->Close ( file );
err_open:
root->Close ( root );
err_volume:
bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid,
efi_image_handle, device );
err_filesystem:
return rc;
}
/**
* Configure automatic booting
*
*/
void efi_set_autoboot ( void ) {
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
/* Identify autoboot device, if any */
efi_set_autoboot_ll_addr ( device );
/* Load autoexec script, if any */
efi_load_autoexec ( device );
}
/**
* Register automatic boot image
*
*/
static void efi_autoboot_startup ( void ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
const char *name = AUTOEXEC_NAME;
struct image *image;
/* Do nothing if we have no autoexec script */
if ( ! efi_autoexec )
return;
/* Create autoexec image */
image = image_memory ( name, virt_to_user ( efi_autoexec ),
efi_autoexec_len );
if ( ! image ) {
DBGC ( device, "EFI %s could not create %s\n",
efi_handle_name ( device ), name );
return;
}
DBGC ( device, "EFI %s registered %s\n",
efi_handle_name ( device ), name );
/* Free temporary copy */
bs->FreePool ( efi_autoexec );
efi_autoexec = NULL;
}
/** Automatic boot startup function */
struct startup_fn efi_autoboot_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.name = "efi_autoboot",
.startup = efi_autoboot_startup,
};

View File

@ -0,0 +1,206 @@
/*
* Copyright (C) 2021 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 <string.h>
#include <errno.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_autoexec.h>
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Guid/FileInfo.h>
/** @file
*
* EFI autoexec script
*
*/
/** Autoexec script filename */
#define AUTOEXEC_FILENAME L"autoexec.ipxe"
/** Autoexec script image name */
#define AUTOEXEC_NAME "autoexec.ipxe"
/** Autoexec script (if any) */
static void *efi_autoexec;
/** Autoexec script length */
static size_t efi_autoexec_len;
/**
* Load autoexec script
*
* @v device Device handle
* @ret rc Return status code
*/
int efi_autoexec_load ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
static wchar_t name[] = AUTOEXEC_FILENAME;
union {
void *interface;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
} u;
struct {
EFI_FILE_INFO info;
CHAR16 name[ sizeof ( name ) / sizeof ( name[0] ) ];
} info;
EFI_FILE_PROTOCOL *root;
EFI_FILE_PROTOCOL *file;
UINTN size;
VOID *data;
EFI_STATUS efirc;
int rc;
/* Sanity check */
assert ( efi_autoexec == NULL );
assert ( efi_autoexec_len == 0 );
/* Open simple file system protocol */
if ( ( efirc = bs->OpenProtocol ( device,
&efi_simple_file_system_protocol_guid,
&u.interface, efi_image_handle,
device,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no filesystem instance: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_filesystem;
}
/* Open root directory */
if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not open volume: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_volume;
}
/* Open autoexec script */
if ( ( efirc = root->Open ( root, &file, name,
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_open;
}
/* Get file information */
size = sizeof ( info );
if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size,
&info ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not get %ls info: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_getinfo;
}
size = info.info.FileSize;
/* Ignore zero-length files */
if ( ! size ) {
rc = -EINVAL;
DBGC ( device, "EFI %s has zero-length %ls\n",
efi_handle_name ( device ), name );
goto err_empty;
}
/* Allocate temporary copy */
if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
&data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not allocate %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_alloc;
}
/* Read file */
if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not read %ls: %s\n",
efi_handle_name ( device ), name, strerror ( rc ) );
goto err_read;
}
/* Record autoexec script */
efi_autoexec = data;
efi_autoexec_len = size;
data = NULL;
DBGC ( device, "EFI %s found %ls\n",
efi_handle_name ( device ), name );
/* Success */
rc = 0;
err_read:
if ( data )
bs->FreePool ( data );
err_alloc:
err_empty:
err_getinfo:
file->Close ( file );
err_open:
root->Close ( root );
err_volume:
bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid,
efi_image_handle, device );
err_filesystem:
return rc;
}
/**
* Register autoexec script
*
*/
static void efi_autoexec_startup ( void ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
const char *name = AUTOEXEC_NAME;
struct image *image;
/* Do nothing if we have no autoexec script */
if ( ! efi_autoexec )
return;
/* Create autoexec image */
image = image_memory ( name, virt_to_user ( efi_autoexec ),
efi_autoexec_len );
if ( ! image ) {
DBGC ( device, "EFI %s could not create %s\n",
efi_handle_name ( device ), name );
return;
}
DBGC ( device, "EFI %s registered %s\n",
efi_handle_name ( device ), name );
/* Free temporary copy */
bs->FreePool ( efi_autoexec );
efi_autoexec = NULL;
}
/** Autoexec script startup function */
struct startup_fn efi_autoexec_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
.name = "efi_autoexec",
.startup = efi_autoexec_startup,
};

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2021 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 <string.h>
#include <errno.h>
#include <ipxe/cachedhcp.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_cachedhcp.h>
#include <ipxe/efi/Protocol/PxeBaseCode.h>
/** @file
*
* EFI cached DHCP packet
*
*/
/**
* Record cached DHCP packet
*
* @v device Device handle
* @ret rc Return status code
*/
int efi_cachedhcp_record ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_PXE_BASE_CODE_PROTOCOL *pxe;
void *interface;
} pxe;
EFI_PXE_BASE_CODE_MODE *mode;
EFI_STATUS efirc;
int rc;
/* Look for a PXE base code instance on the image's device handle */
if ( ( efirc = bs->OpenProtocol ( device,
&efi_pxe_base_code_protocol_guid,
&pxe.interface, efi_image_handle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no PXE base code instance: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_open;
}
/* Do not attempt to cache IPv6 packets */
mode = pxe.pxe->Mode;
if ( mode->UsingIpv6 ) {
rc = -ENOTSUP;
DBGC ( device, "EFI %s has IPv6 PXE base code\n",
efi_handle_name ( device ) );
goto err_ipv6;
}
/* Record DHCPACK, if present */
if ( mode->DhcpAckReceived &&
( ( rc = cachedhcp_record ( virt_to_user ( &mode->DhcpAck ),
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_record;
}
/* Success */
rc = 0;
err_record:
err_ipv6:
bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
efi_image_handle, NULL );
err_open:
return rc;
}

View File

@ -22,10 +22,13 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdlib.h>
#include <errno.h>
#include <ipxe/device.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_snp.h>
#include <ipxe/efi/efi_autoboot.h>
#include <ipxe/efi/efi_autoexec.h>
#include <ipxe/efi/efi_cachedhcp.h>
#include <ipxe/efi/efi_watchdog.h>
#include <ipxe/efi/efi_veto.h>
@ -48,9 +51,6 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
goto err_init;
/* Record autoboot device (if any) */
efi_set_autoboot();
/* Claim SNP devices for use by iPXE */
efi_snp_claim();
@ -72,6 +72,28 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
return efirc;
}
/**
* Initialise EFI application
*
*/
static void efi_init_application ( void ) {
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
/* Identify autoboot device, if any */
efi_set_autoboot_ll_addr ( device );
/* Store cached DHCP packet, if any */
efi_cachedhcp_record ( device );
/* Load autoexec script, if any */
efi_autoexec_load ( device );
}
/** EFI application initialisation function */
struct init_fn efi_init_application_fn __init_fn ( INIT_NORMAL ) = {
.initialise = efi_init_application,
};
/**
* Probe EFI root bus
*