From 614d99eba149d0fafc64dfdddc7ef04970e0d86c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Apr 2021 13:28:57 +0100 Subject: [xen] Avoid infinite loop on allocation failure in xenstore_response() Signed-off-by: Michael Brown --- src/interface/xen/xenstore.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c index a14881fcd..caeb4e934 100644 --- a/src/interface/xen/xenstore.c +++ b/src/interface/xen/xenstore.c @@ -68,14 +68,14 @@ static void xenstore_send ( struct xen_hypervisor *xen, const void *data, XENSTORE_RING_IDX cons; XENSTORE_RING_IDX idx; const char *bytes = data; - size_t offset = 0; + size_t offset; size_t fill; DBGCP ( intf, "XENSTORE raw request:\n" ); DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( prod ), data, len ); /* Write one byte at a time */ - while ( offset < len ) { + for ( offset = 0 ; offset < len ; offset++ ) { /* Wait for space to become available */ while ( 1 ) { @@ -90,7 +90,7 @@ static void xenstore_send ( struct xen_hypervisor *xen, const void *data, /* Write byte */ idx = MASK_XENSTORE_IDX ( prod++ ); - writeb ( bytes[offset++], &intf->req[idx] ); + writeb ( bytes[offset], &intf->req[idx] ); } /* Update producer counter */ @@ -125,13 +125,13 @@ static void xenstore_recv ( struct xen_hypervisor *xen, void *data, XENSTORE_RING_IDX prod; XENSTORE_RING_IDX idx; char *bytes = data; - size_t offset = 0; + size_t offset; size_t fill; DBGCP ( intf, "XENSTORE raw response:\n" ); /* Read one byte at a time */ - while ( offset < len ) { + for ( offset = 0 ; offset < len ; offset++ ) { /* Wait for data to be ready */ while ( 1 ) { @@ -147,7 +147,7 @@ static void xenstore_recv ( struct xen_hypervisor *xen, void *data, /* Read byte */ idx = MASK_XENSTORE_IDX ( cons++ ); if ( data ) - bytes[offset++] = readb ( &intf->rsp[idx] ); + bytes[offset] = readb ( &intf->rsp[idx] ); } if ( data ) DBGCP_HDA ( intf, MASK_XENSTORE_IDX ( cons - len ), data, len ); -- cgit v1.2.3-55-g7522 From e4afaa2246fb373f0aa0620869bb2d4deaa39b5f Mon Sep 17 00:00:00 2001 From: Matya Date: Tue, 20 Apr 2021 13:15:03 +0200 Subject: [build] Fix genfsimg to build ISO with long filenames Commit 79c0173 ("[build] Create util/genfsimg for building filesystem-based images") introduced the new genfsimg, which lacks the -l option when building ISO files. This option is required to build level 2 (long plain) ISO9660 filenames, which are required when using the .lkrn extensions on older versions of ISOLINUX. Signed-off-by: Michael Brown --- src/util/genfsimg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/genfsimg b/src/util/genfsimg index c3123f3ed..1a0517cc9 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -230,7 +230,7 @@ done # since isohybrid will refuse to work without them. # if [ -n "${ISOIMG}" ] ; then - ISOARGS="-J -R" + ISOARGS="-J -R -l" copy_syslinux_file "isolinux.bin" "${ISODIR}" copy_syslinux_file "ldlinux.c32" "${ISODIR}" 2>/dev/null || true ISOARGS="${ISOARGS} -no-emul-boot -eltorito-boot isolinux.bin" -- cgit v1.2.3-55-g7522 From 3efdbef2f0dd956ce7d131ca4bdfd366f8cdc9d4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Apr 2021 14:37:08 +0100 Subject: [efi] Always map full length of coherent DMA buffer allocation The EFI PCI API takes a page count as the input to AllocateBuffer() but a byte count as the input to Map(). There is nothing in the UEFI specification that requires us to map exactly the allocated length, and no systems have yet been observed that will fail if the map length does not exactly match the allocated length. However, it is plausible that some implementations may fail if asked to map a length that does not match the length of the corresponding allocation. Avoid potential future problems by always mapping the full allocated length. Signed-off-by: Michael Brown --- src/interface/efi/efi_pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 4adee0fd8..ef02c9c0e 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -523,7 +523,8 @@ static void * efipci_dma_alloc ( struct dma_device *dma, /* Map buffer */ if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ), - len, DMA_BI ) ) != 0 ) + ( pages * EFI_PAGE_SIZE ), + DMA_BI ) ) != 0 ) goto err_map; /* Increment allocation count (for debugging) */ -- cgit v1.2.3-55-g7522 From 56f7d44fde1d6ac196d115cc7dddd58e7ec098fa Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 21 Apr 2021 16:13:02 +0100 Subject: [efi] Show ACPI address space descriptor ranges in debug messages Signed-off-by: Michael Brown --- src/interface/efi/efi_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index ef02c9c0e..fda4aba0e 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -337,6 +337,9 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, offset = le64_to_cpu ( u.res->qword.offset ); start = ( offset + le64_to_cpu ( u.res->qword.min ) ); end = ( start + le64_to_cpu ( u.res->qword.len ) ); + DBGC2 ( pci, "EFIPCI " PCI_FMT " found range [%08llx,%08llx) " + "-> [%08llx,%08llx)\n", PCI_ARGS ( pci ), start, end, + ( start - offset ), ( end - offset ) ); if ( ( bus_addr < start ) || ( ( bus_addr + len ) > end ) ) continue; -- cgit v1.2.3-55-g7522 From b2501dd1221d7910e7aa896ae8b927bb63b77d3a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 23 Apr 2021 12:27:57 +0100 Subject: [readline] Add an optional timeout to readline_history() Signed-off-by: Michael Brown --- src/hci/commands/nvo_cmd.c | 2 +- src/hci/readline.c | 17 ++++++++++++++--- src/hci/shell.c | 2 +- src/include/readline/readline.h | 3 ++- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index ac0d60651..88e9d4714 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -241,7 +241,7 @@ static int read_value ( struct named_setting *setting, char **args __unused, NULL, &setting->setting, &existing ); /* Read new value */ - if ( ( rc = readline_history ( NULL, existing, NULL, value ) ) != 0 ) + if ( ( rc = readline_history ( NULL, existing, NULL, 0, value ) ) != 0 ) goto err_readline; err_readline: diff --git a/src/hci/readline.c b/src/hci/readline.c index 83a2e0b90..852c4503a 100644 --- a/src/hci/readline.c +++ b/src/hci/readline.c @@ -248,6 +248,7 @@ void history_free ( struct readline_history *history ) { * @v prompt Prompt string * @v prefill Prefill string, or NULL for no prefill * @v history History buffer, or NULL for no history + * @v timeout Timeout period, in ticks (0=indefinite) * @ret line Line read from console (excluding terminating newline) * @ret rc Return status code * @@ -255,7 +256,8 @@ void history_free ( struct readline_history *history ) { * eventually call free() to release the storage. */ int readline_history ( const char *prompt, const char *prefill, - struct readline_history *history, char **line ) { + struct readline_history *history, unsigned long timeout, + char **line ) { char buf[READLINE_MAX]; struct edit_string string; int key; @@ -285,8 +287,17 @@ int readline_history ( const char *prompt, const char *prefill, } while ( 1 ) { + + /* Get keypress */ + key = getkey ( timeout ); + if ( key < 0 ) { + rc = -ETIMEDOUT; + goto done; + } + timeout = 0; + /* Handle keypress */ - key = edit_string ( &string, getkey ( 0 ) ); + key = edit_string ( &string, key ); sync_console ( &string ); move_by = 0; switch ( key ) { @@ -342,6 +353,6 @@ int readline_history ( const char *prompt, const char *prefill, char * readline ( const char *prompt ) { char *line; - readline_history ( prompt, NULL, NULL, &line ); + readline_history ( prompt, NULL, NULL, 0, &line ); return line; } diff --git a/src/hci/shell.c b/src/hci/shell.c index 276eb3527..8ecf73a6f 100644 --- a/src/hci/shell.c +++ b/src/hci/shell.c @@ -91,7 +91,7 @@ int shell ( void ) { /* Read and execute commands */ do { - readline_history ( shell_prompt, NULL, &history, &line ); + readline_history ( shell_prompt, NULL, &history, 0, &line ); if ( line ) { rc = system ( line ); free ( line ); diff --git a/src/include/readline/readline.h b/src/include/readline/readline.h index afafbbdf5..3caf28b47 100644 --- a/src/include/readline/readline.h +++ b/src/include/readline/readline.h @@ -51,7 +51,8 @@ struct readline_history { extern void history_free ( struct readline_history *history ); extern int readline_history ( const char *prompt, const char *prefill, - struct readline_history *history, char **line ); + struct readline_history *history, + unsigned long timeout, char **line ); extern char * __malloc readline ( const char *prompt ); #endif /* _READLINE_H */ -- cgit v1.2.3-55-g7522 From 323af9ee84ae767f1699657d885bb4ca801c00ce Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 23 Apr 2021 12:30:30 +0100 Subject: [settings] Add --timeout option to "read" command Requested-by: Matteo Guglielmi Signed-off-by: Michael Brown --- src/hci/commands/nvo_cmd.c | 57 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/src/hci/commands/nvo_cmd.c b/src/hci/commands/nvo_cmd.c index 88e9d4714..6ad7e7428 100644 --- a/src/hci/commands/nvo_cmd.c +++ b/src/hci/commands/nvo_cmd.c @@ -100,20 +100,40 @@ static int show_exec ( int argc, char **argv ) { } /** "set", "clear", and "read" options */ -struct set_core_options {}; +struct set_core_options { + /** Timeout */ + unsigned long timeout; +}; /** "set", "clear", and "read" option list */ -static struct option_descriptor set_core_opts[] = {}; +static union { + /* "set" takes no options */ + struct option_descriptor set[0]; + /* "clear" takes no options */ + struct option_descriptor clear[0]; + /* "read" takes --timeout option */ + struct option_descriptor read[1]; +} set_core_opts = { + .read = { + OPTION_DESC ( "timeout", 't', required_argument, + struct set_core_options, timeout, parse_timeout ), + }, +}; /** "set" command descriptor */ static struct command_descriptor set_cmd = - COMMAND_DESC ( struct set_core_options, set_core_opts, 1, MAX_ARGUMENTS, - " " ); + COMMAND_DESC ( struct set_core_options, set_core_opts.set, + 1, MAX_ARGUMENTS, " " ); + +/** "clear" command descriptor */ +static struct command_descriptor clear_cmd = + COMMAND_DESC ( struct set_core_options, set_core_opts.clear, + 1, 1, "" ); -/** "clear" and "read" command descriptor */ -static struct command_descriptor clear_read_cmd = - COMMAND_DESC ( struct set_core_options, set_core_opts, 1, 1, - "" ); +/** "read" command descriptor */ +static struct command_descriptor read_cmd = + COMMAND_DESC ( struct set_core_options, set_core_opts.read, + 1, 1, "" ); /** * "set", "clear", and "read" command @@ -127,6 +147,7 @@ static struct command_descriptor clear_read_cmd = static int set_core_exec ( int argc, char **argv, struct command_descriptor *cmd, int ( * get_value ) ( struct named_setting *setting, + struct set_core_options *opts, char **args, char **value ) ) { struct set_core_options opts; struct named_setting setting; @@ -143,7 +164,8 @@ static int set_core_exec ( int argc, char **argv, goto err_parse_setting; /* Parse setting value */ - if ( ( rc = get_value ( &setting, &argv[ optind + 1 ], &value ) ) != 0 ) + if ( ( rc = get_value ( &setting, &opts, &argv[ optind + 1 ], + &value ) ) != 0 ) goto err_get_value; /* Apply default type if necessary */ @@ -170,11 +192,13 @@ static int set_core_exec ( int argc, char **argv, * Get setting value for "set" command * * @v setting Named setting + * @v opts Options list * @v args Remaining arguments * @ret value Setting value * @ret rc Return status code */ static int set_value ( struct named_setting *setting __unused, + struct set_core_options *opts __unused, char **args, char **value ) { *value = concat_args ( args ); @@ -200,10 +224,12 @@ static int set_exec ( int argc, char **argv ) { * * @v setting Named setting * @v args Remaining arguments + * @v opts Options list * @ret value Setting value * @ret rc Return status code */ static int clear_value ( struct named_setting *setting __unused, + struct set_core_options *opts __unused, char **args __unused, char **value ) { *value = NULL; @@ -218,7 +244,7 @@ static int clear_value ( struct named_setting *setting __unused, * @ret rc Return status code */ static int clear_exec ( int argc, char **argv ) { - return set_core_exec ( argc, argv, &clear_read_cmd, clear_value ); + return set_core_exec ( argc, argv, &clear_cmd, clear_value ); } /** @@ -226,11 +252,13 @@ static int clear_exec ( int argc, char **argv ) { * * @v setting Named setting * @v args Remaining arguments + * @v opts Options list * @ret value Setting value * @ret rc Return status code */ -static int read_value ( struct named_setting *setting, char **args __unused, - char **value ) { +static int read_value ( struct named_setting *setting, + struct set_core_options *opts, + char **args __unused, char **value ) { char *existing; int rc; @@ -241,7 +269,8 @@ static int read_value ( struct named_setting *setting, char **args __unused, NULL, &setting->setting, &existing ); /* Read new value */ - if ( ( rc = readline_history ( NULL, existing, NULL, 0, value ) ) != 0 ) + if ( ( rc = readline_history ( NULL, existing, NULL, opts->timeout, + value ) ) != 0 ) goto err_readline; err_readline: @@ -257,7 +286,7 @@ static int read_value ( struct named_setting *setting, char **args __unused, * @ret rc Return status code */ static int read_exec ( int argc, char **argv ) { - return set_core_exec ( argc, argv, &clear_read_cmd, read_value ); + return set_core_exec ( argc, argv, &read_cmd, read_value ); } /** "inc" options */ -- cgit v1.2.3-55-g7522 From e994237c0b7de6e4f17cc1a1c740043c553d2bdf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 1 May 2021 21:33:38 +0100 Subject: [cloud] Add ability to generate Dokuwiki table of AMI images Add an option to generate the amilist.txt list of current AMI images as included in the EC2 documentation at https://ipxe.org/howto/ec2 Signed-off-by: Michael Brown --- contrib/cloud/aws-import | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/contrib/cloud/aws-import b/contrib/cloud/aws-import index 9ee53e704..a93a00843 100755 --- a/contrib/cloud/aws-import +++ b/contrib/cloud/aws-import @@ -65,6 +65,12 @@ def import_image(region, name, architecture, image, public): return image_id +def launch_link(region, image_id): + """Construct a web console launch link""" + return ("https://console.aws.amazon.com/ec2/v2/home?" + "region=%s#LaunchInstanceWizard:ami=%s" % (region, image_id)) + + # Parse command-line arguments parser = argparse.ArgumentParser(description="Import AWS EC2 image (AMI)") parser.add_argument('--architecture', '-a', default='x86_64', @@ -75,6 +81,8 @@ 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('--wiki', '-w', metavar='FILE', + help="Generate Dokuwiki table") parser.add_argument('image', help="iPXE disk image") args = parser.parse_args() @@ -95,6 +103,18 @@ with ThreadPoolExecutor(max_workers=len(args.region)) as executor: results = {futures[future]: future.result() for future in as_completed(futures)} +# Construct Dokuwiki table +wikitab = ["^ AWS region ^ CPU architecture ^ AMI ID ^\n"] + list( + "| ''%s'' | ''%s'' | ''[[%s|%s]]'' |\n" % ( + region, + args.architecture, + launch_link(region, results[region]), + results[region], + ) for region in args.region) +if args.wiki: + with open(args.wiki, 'wt') as fh: + fh.writelines(wikitab) + # Show created images for region in args.region: print("%s: %s" % (region, results[region])) -- cgit v1.2.3-55-g7522 From 6dad316e665c359cc880e4d3e90121ea4eb0ee3a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sat, 1 May 2021 22:08:17 +0100 Subject: [cloud] Use a sortable default AMI name The AWS console user interface provides no convenient way to sort AMIs by creation date. Provide a default AMI name constructed from the current date and CPU architecture, to simplify the task of finding the most recent iPXE AMI in a given AWS region. Signed-off-by: Michael Brown --- contrib/cloud/aws-import | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contrib/cloud/aws-import b/contrib/cloud/aws-import index a93a00843..65b77b1f8 100755 --- a/contrib/cloud/aws-import +++ b/contrib/cloud/aws-import @@ -3,6 +3,7 @@ import argparse from base64 import b64encode from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import date from hashlib import sha256 from itertools import count @@ -75,7 +76,7 @@ def launch_link(region, image_id): 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, +parser.add_argument('--name', '-n', help="Image name") parser.add_argument('--public', '-p', action='store_true', help="Make image public") @@ -86,6 +87,13 @@ parser.add_argument('--wiki', '-w', metavar='FILE', parser.add_argument('image', help="iPXE disk image") args = parser.parse_args() +# Use default name if none specified +if not args.name: + args.name = 'iPXE (%s %s)' % ( + date.today().strftime('%Y-%m-%d'), + args.architecture, + ) + # Use all regions if none specified if not args.region: args.region = sorted(x['RegionName'] for x in -- cgit v1.2.3-55-g7522 From 438513f6f6103f674f65dc91b2d59b81d3b25791 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 2 May 2021 09:39:10 +0100 Subject: [cloud] Autodetect CPU architecture from AMI disk image Signed-off-by: Michael Brown --- contrib/cloud/aws-import | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/contrib/cloud/aws-import b/contrib/cloud/aws-import index 65b77b1f8..4e0fafd66 100755 --- a/contrib/cloud/aws-import +++ b/contrib/cloud/aws-import @@ -6,12 +6,22 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from datetime import date from hashlib import sha256 from itertools import count +import subprocess import boto3 BLOCKSIZE = 512 * 1024 +def detect_architecture(image): + """Detect CPU architecture""" + mdir = subprocess.run(['mdir', '-b', '-i', image, '::/EFI/BOOT'], + capture_output=True) + if any(b'BOOTAA64.EFI' in x for x in mdir.stdout.splitlines()): + return 'arm64' + return 'x86_64' + + def create_snapshot(region, description, image): """Create an EBS snapshot""" client = boto3.client('ebs', region_name=region) @@ -74,8 +84,6 @@ def launch_link(region, 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', help="Image name") parser.add_argument('--public', '-p', action='store_true', @@ -87,11 +95,14 @@ parser.add_argument('--wiki', '-w', metavar='FILE', parser.add_argument('image', help="iPXE disk image") args = parser.parse_args() +# Detect CPU architecture +architecture = detect_architecture(args.image) + # Use default name if none specified if not args.name: args.name = 'iPXE (%s %s)' % ( date.today().strftime('%Y-%m-%d'), - args.architecture, + architecture, ) # Use all regions if none specified @@ -104,7 +115,7 @@ with ThreadPoolExecutor(max_workers=len(args.region)) as executor: futures = {executor.submit(import_image, region=region, name=args.name, - architecture=args.architecture, + architecture=architecture, image=args.image, public=args.public): region for region in args.region} @@ -115,7 +126,7 @@ with ThreadPoolExecutor(max_workers=len(args.region)) as executor: wikitab = ["^ AWS region ^ CPU architecture ^ AMI ID ^\n"] + list( "| ''%s'' | ''%s'' | ''[[%s|%s]]'' |\n" % ( region, - args.architecture, + architecture, launch_link(region, results[region]), results[region], ) for region in args.region) @@ -125,4 +136,4 @@ if args.wiki: # Show created images for region in args.region: - print("%s: %s" % (region, results[region])) + print("%s %s %s" % (region, architecture, results[region])) -- cgit v1.2.3-55-g7522 From 1dfc05622d183213c55b49c3ea4f4fd5c7bd6253 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 2 May 2021 12:35:37 +0100 Subject: [cloud] Attempt to include CPUID_SETTINGS only for x86 builds Signed-off-by: Michael Brown --- src/config/cloud/settings.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/cloud/settings.h b/src/config/cloud/settings.h index 34deeb070..c9d6bdc93 100644 --- a/src/config/cloud/settings.h +++ b/src/config/cloud/settings.h @@ -1,4 +1,6 @@ /* It can often be useful to know the CPU on which a cloud instance is * running (e.g. to isolate problems with Azure AMD instances). */ +#if defined ( __i386__ ) || defined ( __x86_64__ ) #define CPUID_SETTINGS +#endif -- cgit v1.2.3-55-g7522 From 106f4c539182b4a92b8a427b637497e6da19bd85 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 2 May 2021 12:23:00 +0100 Subject: [cloud] Allow multiple images to be imported simultaneously Allow both x86_64 and arm64 images to be imported in a single import command, thereby allowing for e.g. make CONFIG=cloud EMBED=config/cloud/aws.ipxe bin/ipxe.usb make CONFIG=cloud EMBED=config/cloud/aws.ipxe \ CROSS=aarch64-linux-gnu- bin-arm64-efi/ipxe.usb ../contrib/cloud/aws-import -w amilist.txt -p \ bin/ipxe.usb bin-arm64-efi/ipxe.usb This simplifies the process of generating a single amilist.txt file for inclusion in the documentation at https://ipxe.org/howto/ec2 Signed-off-by: Michael Brown --- contrib/cloud/aws-import | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/contrib/cloud/aws-import b/contrib/cloud/aws-import index 4e0fafd66..eef4302d5 100755 --- a/contrib/cloud/aws-import +++ b/contrib/cloud/aws-import @@ -92,33 +92,31 @@ parser.add_argument('--region', '-r', action='append', help="AWS region(s)") parser.add_argument('--wiki', '-w', metavar='FILE', help="Generate Dokuwiki table") -parser.add_argument('image', help="iPXE disk image") +parser.add_argument('image', nargs='+', help="iPXE disk image") args = parser.parse_args() -# Detect CPU architecture -architecture = detect_architecture(args.image) +# Detect CPU architectures +architectures = {image: detect_architecture(image) for image in args.image} # Use default name if none specified if not args.name: - args.name = 'iPXE (%s %s)' % ( - date.today().strftime('%Y-%m-%d'), - architecture, - ) + args.name = 'iPXE (%s)' % date.today().strftime('%Y-%m-%d') # 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: +# Use one thread per import to maximise parallelism +imports = [(region, image) for region in args.region for image in args.image] +with ThreadPoolExecutor(max_workers=len(imports)) as executor: futures = {executor.submit(import_image, region=region, name=args.name, - architecture=architecture, - image=args.image, - public=args.public): region - for region in args.region} + architecture=architectures[image], + image=image, + public=args.public): (region, image) + for region, image in imports} results = {futures[future]: future.result() for future in as_completed(futures)} @@ -126,14 +124,16 @@ with ThreadPoolExecutor(max_workers=len(args.region)) as executor: wikitab = ["^ AWS region ^ CPU architecture ^ AMI ID ^\n"] + list( "| ''%s'' | ''%s'' | ''[[%s|%s]]'' |\n" % ( region, - architecture, - launch_link(region, results[region]), - results[region], - ) for region in args.region) + architectures[image], + launch_link(region, results[(region, image)]), + results[(region, image)], + ) for region, image in imports) if args.wiki: with open(args.wiki, 'wt') as fh: fh.writelines(wikitab) # Show created images -for region in args.region: - print("%s %s %s" % (region, architecture, results[region])) +for region, image in imports: + print("%s %s %s %s" % ( + region, image, architectures[image], results[(region, image)] + )) -- cgit v1.2.3-55-g7522 From de4f31cdcad7c4734c68da828351eeb7afd0360e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 May 2021 13:09:30 +0100 Subject: [image] Provide image_set_len() utility function Signed-off-by: Michael Brown --- src/core/image.c | 25 +++++++++++++++++++++---- src/include/ipxe/image.h | 1 + 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/core/image.c b/src/core/image.c index 9fe77c54c..ce8cf868b 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -176,14 +176,13 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) { } /** - * Set image data + * Set image length * * @v image Image - * @v data Image data * @v len Length of image data * @ret rc Return status code */ -int image_set_data ( struct image *image, userptr_t data, size_t len ) { +int image_set_len ( struct image *image, size_t len ) { userptr_t new; /* (Re)allocate image data */ @@ -191,10 +190,28 @@ int image_set_data ( struct image *image, userptr_t data, size_t len ) { if ( ! new ) return -ENOMEM; image->data = new; + image->len = len; + + return 0; +} + +/** + * Set image data + * + * @v image Image + * @v data Image data + * @v len Length of image data + * @ret rc Return status code + */ +int image_set_data ( struct image *image, userptr_t data, size_t len ) { + int rc; + + /* Set image length */ + if ( ( rc = image_set_len ( image, len ) ) != 0 ) + return rc; /* Copy in new image data */ memcpy_user ( image->data, 0, data, 0, len ); - image->len = len; return 0; } diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 4fd270081..046edf9a5 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -175,6 +175,7 @@ extern struct image * alloc_image ( struct uri *uri ); extern int image_set_uri ( struct image *image, struct uri *uri ); extern int image_set_name ( struct image *image, const char *name ); extern int image_set_cmdline ( struct image *image, const char *cmdline ); +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 ); -- cgit v1.2.3-55-g7522 From 5c9c8d2b9b78cf4e1f256fe6874855c1aee458f2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 May 2021 13:11:31 +0100 Subject: [image] Add "imgextract" command for extracting archive images Add the concept of extracting an image from an archive (which could be a single-file archive such as a gzip-compressed file), along with an "imgextract" command to expose this functionality to scripts. Signed-off-by: Michael Brown --- src/config/config.c | 3 + src/config/config_archive.c | 32 +++++++++++ src/config/general.h | 1 + src/core/archive.c | 104 ++++++++++++++++++++++++++++++++++ src/hci/commands/image_archive_cmd.c | 105 +++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/image.h | 10 ++++ src/include/usr/imgarchive.h | 16 ++++++ src/usr/imgarchive.c | 54 ++++++++++++++++++ 9 files changed, 326 insertions(+) create mode 100644 src/config/config_archive.c create mode 100644 src/core/archive.c create mode 100644 src/hci/commands/image_archive_cmd.c create mode 100644 src/include/usr/imgarchive.h create mode 100644 src/usr/imgarchive.c diff --git a/src/config/config.c b/src/config/config.c index 5e7a3ecfd..dd1fceb92 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -284,6 +284,9 @@ REQUIRE_OBJECT ( cert_cmd ); #ifdef IMAGE_MEM_CMD REQUIRE_OBJECT ( image_mem_cmd ); #endif +#ifdef IMAGE_ARCHIVE_CMD +REQUIRE_OBJECT ( image_archive_cmd ); +#endif /* * Drag in miscellaneous objects diff --git a/src/config/config_archive.c b/src/config/config_archive.c new file mode 100644 index 000000000..3644b5de8 --- /dev/null +++ b/src/config/config_archive.c @@ -0,0 +1,32 @@ +/* + * 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 + +/** @file + * + * Archive image configuration + * + */ + +PROVIDE_REQUIRING_SYMBOL(); diff --git a/src/config/general.h b/src/config/general.h index 9b21f1271..fd317be96 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -156,6 +156,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define NTP_CMD /* NTP commands */ //#define CERT_CMD /* Certificate management commands */ //#define IMAGE_MEM_CMD /* Read memory command */ +//#define IMAGE_ARCHIVE_CMD /* Archive image management commands */ /* * ROM-specific options diff --git a/src/core/archive.c b/src/core/archive.c new file mode 100644 index 000000000..cd024bde3 --- /dev/null +++ b/src/core/archive.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Archive images + * + */ + +/** + * Extract archive image + * + * @v image Image + * @v name Extracted image name + * @v extracted Extracted image to fill in + * @ret rc Return status code + */ +int image_extract ( struct image *image, const char *name, + struct image **extracted ) { + char *dot; + int rc; + + /* Check that this image can be used to extract an archive image */ + if ( ! ( image->type && image->type->extract ) ) { + rc = -ENOTSUP; + goto err_unsupported; + } + + /* Allocate new image */ + *extracted = alloc_image ( image->uri ); + if ( ! *extracted ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Set image name */ + if ( ( rc = image_set_name ( *extracted, + ( name ? name : image->name ) ) ) != 0 ) { + goto err_set_name; + } + + /* Strip any archive or compression suffix from implicit name */ + if ( ( ! name ) && ( (*extracted)->name ) && + ( ( dot = strrchr ( (*extracted)->name, '.' ) ) != NULL ) ) { + *dot = '\0'; + } + + /* Try extracting archive image */ + if ( ( rc = image->type->extract ( image, *extracted ) ) != 0 ) { + DBGC ( image, "IMAGE %s could not extract image: %s\n", + image->name, strerror ( rc ) ); + goto err_extract; + } + + /* Register image */ + if ( ( rc = register_image ( *extracted ) ) != 0 ) + goto err_register; + + /* Drop local reference to image */ + image_put ( *extracted ); + + return 0; + + unregister_image ( *extracted ); + err_register: + err_extract: + err_set_name: + image_put ( *extracted ); + err_alloc: + err_unsupported: + return rc; +} + +/* Drag in objects via image_extract() */ +REQUIRING_SYMBOL ( image_extract ); + +/* Drag in archive image formats */ +REQUIRE_OBJECT ( config_archive ); diff --git a/src/hci/commands/image_archive_cmd.c b/src/hci/commands/image_archive_cmd.c new file mode 100644 index 000000000..a2212aecf --- /dev/null +++ b/src/hci/commands/image_archive_cmd.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include + +/** @file + * + * Archive image commands + * + */ + +/** "imgextract" options */ +struct imgextract_options { + /** Image name */ + char *name; + /** Keep original image */ + int keep; + /** Download timeout */ + unsigned long timeout; +}; + +/** "imgextract" option list */ +static struct option_descriptor imgextract_opts[] = { + OPTION_DESC ( "name", 'n', required_argument, + struct imgextract_options, name, parse_string ), + OPTION_DESC ( "keep", 'k', no_argument, + struct imgextract_options, keep, parse_flag ), + OPTION_DESC ( "timeout", 't', required_argument, + struct imgextract_options, timeout, parse_timeout ), +}; + +/** "imgextract" command descriptor */ +static struct command_descriptor imgextract_cmd = + COMMAND_DESC ( struct imgextract_options, imgextract_opts, 1, 1, NULL ); + +/** + * The "imgextract" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int imgextract_exec ( int argc, char **argv ) { + struct imgextract_options opts; + struct image *image; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &imgextract_cmd, + &opts ) ) != 0 ) + goto err_parse; + + /* Acquire image */ + if ( ( rc = imgacquire ( argv[optind], opts.timeout, &image ) ) != 0 ) + goto err_acquire; + + /* Extract archive image */ + if ( ( rc = imgextract ( image, opts.name ) ) != 0 ) + goto err_extract; + + /* Success */ + rc = 0; + + err_extract: + /* Discard original image unless --keep was specified */ + if ( ! opts.keep ) + unregister_image ( image ); + err_acquire: + err_parse: + return rc; +} + +/** Archive image commands */ +struct command image_archive_commands[] __command = { + { + .name = "imgextract", + .exec = imgextract_exec, + }, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e3bf9f565..162e6b7b0 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -301,6 +301,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_png ( ERRFILE_IMAGE | 0x00070000 ) #define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) #define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) +#define ERRFILE_archive ( ERRFILE_IMAGE | 0x000a0000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index 046edf9a5..c6e723dc6 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -113,6 +113,14 @@ struct image_type { */ int ( * asn1 ) ( struct image *image, size_t offset, struct asn1_cursor **cursor ); + /** + * Extract archive image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ + int ( * extract ) ( struct image *image, struct image *extracted ); }; /** @@ -190,6 +198,8 @@ extern struct image * image_memory ( const char *name, userptr_t data, 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 ); +extern int image_extract ( struct image *image, const char *name, + struct image **extracted ); /** * Increment reference count on an image diff --git a/src/include/usr/imgarchive.h b/src/include/usr/imgarchive.h new file mode 100644 index 000000000..bf0c18f55 --- /dev/null +++ b/src/include/usr/imgarchive.h @@ -0,0 +1,16 @@ +#ifndef _USR_IMGARCHIVE_H +#define _USR_IMGARCHIVE_H + +/** @file + * + * Archive image management + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +extern int imgextract ( struct image *image, const char *name ); + +#endif /* _USR_IMGARCHIVE_H */ diff --git a/src/usr/imgarchive.c b/src/usr/imgarchive.c new file mode 100644 index 000000000..6849dd510 --- /dev/null +++ b/src/usr/imgarchive.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Archive image management + * + */ + +/** + * Extract archive image + * + * @v image Image + * @v name Extracted image name (or NULL to use default) + * @ret rc Return status code + */ +int imgextract ( struct image *image, const char *name ) { + struct image *extracted; + int rc; + + /* Extract archive image */ + if ( ( rc = image_extract ( image, name, &extracted ) ) != 0 ) { + printf ( "Could not extract image: %s\n", strerror ( rc ) ); + return rc; + } + + return 0; +} -- cgit v1.2.3-55-g7522 From d093683d93ccfac4c76e72264ec3b0d8f0017b92 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 May 2021 13:17:35 +0100 Subject: [zlib] Add support for zlib archive images Signed-off-by: Michael Brown --- src/config/config_archive.c | 4 ++ src/config/general.h | 1 + src/image/zlib.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/zlib.h | 43 ++++++++++++ src/tests/tests.c | 1 + src/tests/zlib_test.c | 133 ++++++++++++++++++++++++++++++++++++ 7 files changed, 345 insertions(+) create mode 100644 src/image/zlib.c create mode 100644 src/include/ipxe/zlib.h create mode 100644 src/tests/zlib_test.c diff --git a/src/config/config_archive.c b/src/config/config_archive.c index 3644b5de8..eceebaecb 100644 --- a/src/config/config_archive.c +++ b/src/config/config_archive.c @@ -30,3 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ PROVIDE_REQUIRING_SYMBOL(); + +#ifdef IMAGE_ZLIB +REQUIRE_OBJECT ( zlib ); +#endif diff --git a/src/config/general.h b/src/config/general.h index fd317be96..d6653795e 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -117,6 +117,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_PNG /* PNG image support */ #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ +#define IMAGE_ZLIB /* ZLIB image support */ /* * Command-line commands to include diff --git a/src/image/zlib.c b/src/image/zlib.c new file mode 100644 index 000000000..bc27142d7 --- /dev/null +++ b/src/image/zlib.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * zlib compressed images + * + */ + +/** + * Extract compressed data to image + * + * @v format Compression format code + * @v in Compressed input chunk + * @v extracted Extracted image + * @ret rc Return status code + */ +int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in, + struct image *extracted ) { + struct deflate *deflate; + struct deflate_chunk out; + int rc; + + /* Allocate and initialise decompressor */ + deflate = zalloc ( sizeof ( *deflate ) ); + if ( ! deflate ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Decompress data, (re)allocating if necessary */ + while ( 1 ) { + + /* (Re)initialise decompressor */ + deflate_init ( deflate, format ); + + /* (Re)initialise input chunk */ + in->offset = 0; + + /* Initialise output chunk */ + deflate_chunk_init ( &out, extracted->data, 0, extracted->len ); + + /* Decompress data */ + if ( ( rc = deflate_inflate ( deflate, in, &out ) ) != 0 ) { + DBGC ( extracted, "ZLIB %p could not decompress: %s\n", + extracted, strerror ( rc ) ); + goto err_inflate; + } + + /* Check that decompression is valid */ + if ( ! deflate_finished ( deflate ) ) { + DBGC ( extracted, "ZLIB %p decompression incomplete\n", + extracted ); + rc = -EINVAL; + goto err_unfinished; + } + + /* Finish if output image size was correct */ + if ( out.offset == extracted->len ) + break; + + /* Otherwise, resize output image and retry */ + if ( ( rc = image_set_len ( extracted, out.offset ) ) != 0 ) { + DBGC ( extracted, "ZLIB %p could not resize: %s\n", + extracted, strerror ( rc ) ); + goto err_set_size; + } + } + + /* Success */ + rc = 0; + + err_set_size: + err_unfinished: + err_inflate: + free ( deflate ); + err_alloc: + return rc; +} + +/** + * Extract zlib image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ +static int zlib_extract ( struct image *image, struct image *extracted ) { + struct deflate_chunk in; + int rc; + + /* Initialise input chunk */ + deflate_chunk_init ( &in, image->data, 0, image->len ); + + /* Decompress image */ + if ( ( rc = zlib_deflate ( DEFLATE_ZLIB, &in, extracted ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Probe zlib image + * + * @v image zlib image + * @ret rc Return status code + */ +static int zlib_probe ( struct image *image ) { + union zlib_magic magic; + + /* Sanity check */ + if ( image->len < sizeof ( magic ) ) { + DBGC ( image, "ZLIB %p image too short\n", image ); + return -ENOEXEC; + } + + /* Check magic header */ + copy_from_user ( &magic, image->data, 0, sizeof ( magic ) ); + if ( ! zlib_magic_is_valid ( &magic ) ) { + DBGC ( image, "ZLIB %p invalid magic data\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** zlib image type */ +struct image_type zlib_image_type __image_type ( PROBE_NORMAL ) = { + .name = "zlib", + .probe = zlib_probe, + .extract = zlib_extract, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 162e6b7b0..35b03fe9d 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -302,6 +302,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_der ( ERRFILE_IMAGE | 0x00080000 ) #define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_archive ( ERRFILE_IMAGE | 0x000a0000 ) +#define ERRFILE_zlib ( ERRFILE_IMAGE | 0x000b0000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/include/ipxe/zlib.h b/src/include/ipxe/zlib.h new file mode 100644 index 000000000..29016c38e --- /dev/null +++ b/src/include/ipxe/zlib.h @@ -0,0 +1,43 @@ +#ifndef _IPXE_ZLIB_H +#define _IPXE_ZLIB_H + +/** @file + * + * zlib compressed images + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include + +/** zlib magic header */ +union zlib_magic { + /** Compression method and flags */ + uint8_t cmf; + /** Check value */ + uint16_t check; +} __attribute__ (( packed )); + +/** + * Check that zlib magic header is valid + * + * @v magic Magic header + * @ret is_valid Magic header is valid + */ +static inline int zlib_magic_is_valid ( union zlib_magic *magic ) { + + /* Check magic value as per RFC 6713 */ + return ( ( ( magic->cmf & 0x8f ) == 0x08 ) && + ( ( be16_to_cpu ( magic->check ) % 31 ) == 0 ) ); +} + +extern int zlib_deflate ( enum deflate_format format, struct deflate_chunk *in, + struct image *extracted ); + +extern struct image_type zlib_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_ZLIB_H */ diff --git a/src/tests/tests.c b/src/tests/tests.c index 2e812d6ff..f4cf041ac 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -73,3 +73,4 @@ REQUIRE_OBJECT ( bitops_test ); REQUIRE_OBJECT ( der_test ); REQUIRE_OBJECT ( pem_test ); REQUIRE_OBJECT ( ntlm_test ); +REQUIRE_OBJECT ( zlib_test ); diff --git a/src/tests/zlib_test.c b/src/tests/zlib_test.c new file mode 100644 index 000000000..df52d09ac --- /dev/null +++ b/src/tests/zlib_test.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * zlib image tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** A zlib test */ +struct zlib_test { + /** Compressed filename */ + const char *compressed_name; + /** Compressed data */ + const void *compressed; + /** Length of compressed data */ + size_t compressed_len; + /** Expected uncompressed name */ + const char *expected_name; + /** Expected uncompressed data */ + const void *expected; + /** Length of expected uncompressed data */ + size_t expected_len; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a zlib test */ +#define ZLIB( name, COMPRESSED, EXPECTED ) \ + static const uint8_t name ## _compressed[] = COMPRESSED; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct zlib_test name = { \ + .compressed_name = #name ".z", \ + .compressed = name ## _compressed, \ + .compressed_len = sizeof ( name ## _compressed ), \ + .expected_name = #name, \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** "Hello world" */ +ZLIB ( hello_world, + DATA ( 0x78, 0x9c, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, + 0x2f, 0xca, 0x49, 0x01, 0x00, 0x18, 0xab, 0x04, 0x3d ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64 ) ); + +/** + * Report zlib test result + * + * @v test zlib test + * @v file Test code file + * @v line Test code line + */ +static void zlib_okx ( struct zlib_test *test, const char *file, + unsigned int line ) { + struct image *image; + struct image *extracted; + + /* Construct compressed image */ + image = image_memory ( test->compressed_name, + virt_to_user ( test->compressed ), + test->compressed_len ); + okx ( image != NULL, file, line ); + okx ( image->len == test->compressed_len, file, line ); + + /* Check type detection */ + okx ( image->type == &zlib_image_type, file, line ); + + /* Extract archive image */ + okx ( image_extract ( image, NULL, &extracted ) == 0, file, line ); + + /* Verify extracted image content */ + okx ( extracted->len == test->expected_len, file, line ); + okx ( memcmp_user ( extracted->data, 0, + virt_to_user ( test->expected ), 0, + test->expected_len ) == 0, file, line ); + + /* Verify extracted image name */ + okx ( strcmp ( extracted->name, test->expected_name ) == 0, + file, line ); + + /* Unregister images */ + unregister_image ( extracted ); + unregister_image ( image ); +} +#define zlib_ok( test ) zlib_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform zlib self-test + * + */ +static void zlib_test_exec ( void ) { + + zlib_ok ( &hello_world ); +} + +/** zlib self-test */ +struct self_test zlib_test __self_test = { + .name = "zlib", + .exec = zlib_test_exec, +}; -- cgit v1.2.3-55-g7522 From 866fa1ce7639c93de8905cf16ab79b8086990728 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 6 May 2021 18:38:37 +0100 Subject: [gzip] Add support for gzip archive images Signed-off-by: Michael Brown --- src/config/config_archive.c | 3 + src/config/general.h | 1 + src/image/gzip.c | 166 ++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/errfile.h | 1 + src/include/ipxe/gzip.h | 71 +++++++++++++++++++ src/tests/gzip_test.c | 160 ++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 7 files changed, 403 insertions(+) create mode 100644 src/image/gzip.c create mode 100644 src/include/ipxe/gzip.h create mode 100644 src/tests/gzip_test.c diff --git a/src/config/config_archive.c b/src/config/config_archive.c index eceebaecb..84f21b956 100644 --- a/src/config/config_archive.c +++ b/src/config/config_archive.c @@ -34,3 +34,6 @@ PROVIDE_REQUIRING_SYMBOL(); #ifdef IMAGE_ZLIB REQUIRE_OBJECT ( zlib ); #endif +#ifdef IMAGE_GZIP +REQUIRE_OBJECT ( gzip ); +#endif diff --git a/src/config/general.h b/src/config/general.h index d6653795e..fcfbaf5e9 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -118,6 +118,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ #define IMAGE_ZLIB /* ZLIB image support */ +#define IMAGE_GZIP /* GZIP image support */ /* * Command-line commands to include diff --git a/src/image/gzip.c b/src/image/gzip.c new file mode 100644 index 000000000..40728138b --- /dev/null +++ b/src/image/gzip.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @file + * + * gzip compressed images + * + */ + +/** + * Extract gzip image + * + * @v image Image + * @v extracted Extracted image + * @ret rc Return status code + */ +static int gzip_extract ( struct image *image, struct image *extracted ) { + struct gzip_header header; + struct gzip_extra_header extra; + struct gzip_crc_header crc; + struct gzip_footer footer; + struct deflate_chunk in; + unsigned int strings; + size_t offset; + size_t len; + off_t nul; + int rc; + + /* Sanity check */ + assert ( image->len >= ( sizeof ( header ) + sizeof ( footer ) ) ); + + /* Extract footer */ + len = ( image->len - sizeof ( footer ) ); + copy_from_user ( &footer, image->data, len, sizeof ( footer ) ); + + /* Extract fixed header */ + copy_from_user ( &header, image->data, 0, sizeof ( header ) ); + offset = sizeof ( header ); + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip extra header, if present */ + if ( header.flags & GZIP_FL_EXTRA ) { + copy_from_user ( &extra, image->data, offset, + sizeof ( extra ) ); + offset += sizeof ( extra ); + offset += le16_to_cpu ( extra.len ); + if ( offset > len ) { + DBGC ( image, "GZIP %p overlength extra header\n", + image ); + return -EINVAL; + } + } + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip name and/or comment, if present */ + strings = 0; + if ( header.flags & GZIP_FL_NAME ) + strings++; + if ( header.flags & GZIP_FL_COMMENT ) + strings++; + while ( strings-- ) { + nul = memchr_user ( image->data, offset, 0, ( len - offset ) ); + if ( nul < 0 ) { + DBGC ( image, "GZIP %p overlength name/comment\n", + image ); + return -EINVAL; + } + offset = ( nul + 1 /* NUL */ ); + } + assert ( offset <= ( image->len - sizeof ( footer ) ) ); + + /* Skip CRC, if present */ + if ( header.flags & GZIP_FL_HCRC ) { + offset += sizeof ( crc ); + if ( offset > len ) { + DBGC ( image, "GZIP %p overlength CRC header\n", + image ); + return -EINVAL; + } + } + + /* Initialise input chunk */ + deflate_chunk_init ( &in, userptr_add ( image->data, offset ), 0, len ); + + /* Presize extracted image */ + if ( ( rc = image_set_len ( extracted, + le32_to_cpu ( footer.len ) ) ) != 0 ) { + DBGC ( image, "GZIP %p could not presize: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Decompress image (expanding if necessary) */ + if ( ( rc = zlib_deflate ( DEFLATE_RAW, &in, extracted ) ) != 0 ) { + DBGC ( image, "GZIP %p could not decompress: %s\n", + image, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe gzip image + * + * @v image gzip image + * @ret rc Return status code + */ +static int gzip_probe ( struct image *image ) { + struct gzip_header header; + struct gzip_footer footer; + + /* Sanity check */ + if ( image->len < ( sizeof ( header ) + sizeof ( footer ) ) ) { + DBGC ( image, "GZIP %p image too short\n", image ); + return -ENOEXEC; + } + + /* Check magic header */ + copy_from_user ( &header.magic, image->data, 0, + sizeof ( header.magic ) ); + if ( header.magic != cpu_to_be16 ( GZIP_MAGIC ) ) { + DBGC ( image, "GZIP %p invalid magic\n", image ); + return -ENOEXEC; + } + + return 0; +} + +/** gzip image type */ +struct image_type gzip_image_type __image_type ( PROBE_NORMAL ) = { + .name = "gzip", + .probe = gzip_probe, + .extract = gzip_extract, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 35b03fe9d..90c91cdb9 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -303,6 +303,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_pem ( ERRFILE_IMAGE | 0x00090000 ) #define ERRFILE_archive ( ERRFILE_IMAGE | 0x000a0000 ) #define ERRFILE_zlib ( ERRFILE_IMAGE | 0x000b0000 ) +#define ERRFILE_gzip ( ERRFILE_IMAGE | 0x000c0000 ) #define ERRFILE_asn1 ( ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_chap ( ERRFILE_OTHER | 0x00010000 ) diff --git a/src/include/ipxe/gzip.h b/src/include/ipxe/gzip.h new file mode 100644 index 000000000..c8cf64147 --- /dev/null +++ b/src/include/ipxe/gzip.h @@ -0,0 +1,71 @@ +#ifndef _IPXE_GZIP_H +#define _IPXE_GZIP_H + +/** @file + * + * gzip compressed images + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include + +/** gzip header */ +struct gzip_header { + /** Magic ID */ + uint16_t magic; + /** Compression method */ + uint8_t method; + /** Flags */ + uint8_t flags; + /** Modification time */ + uint32_t mtime; + /** Extra flags */ + uint8_t extra; + /** Operating system */ + uint8_t os; +} __attribute__ (( packed )); + +/** Magic ID */ +#define GZIP_MAGIC 0x1f8b + +/** Compression method */ +#define GZIP_METHOD_DEFLATE 0x08 + +/** CRC header is present */ +#define GZIP_FL_HCRC 0x02 + +/** Extra header is present */ +#define GZIP_FL_EXTRA 0x04 + +/** File name is present */ +#define GZIP_FL_NAME 0x08 + +/** File comment is present */ +#define GZIP_FL_COMMENT 0x10 + +/** gzip extra header */ +struct gzip_extra_header { + /** Extra header length (excluding this field) */ + uint16_t len; +} __attribute__ (( packed )); + +/** gzip CRC header */ +struct gzip_crc_header { + /** CRC-16 */ + uint16_t crc; +} __attribute__ (( packed )); + +/** gzip footer */ +struct gzip_footer { + /** CRC-32 */ + uint32_t crc; + /** Uncompressed size (modulo 2^32) */ + uint32_t len; +} __attribute__ (( packed )); + +extern struct image_type gzip_image_type __image_type ( PROBE_NORMAL ); + +#endif /* _IPXE_GZIP_H */ diff --git a/src/tests/gzip_test.c b/src/tests/gzip_test.c new file mode 100644 index 000000000..fa76edc53 --- /dev/null +++ b/src/tests/gzip_test.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * gzip image tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** A gzip test */ +struct gzip_test { + /** Compressed filename */ + const char *compressed_name; + /** Compressed data */ + const void *compressed; + /** Length of compressed data */ + size_t compressed_len; + /** Expected uncompressed name */ + const char *expected_name; + /** Expected uncompressed data */ + const void *expected; + /** Length of expected uncompressed data */ + size_t expected_len; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a gzip test */ +#define GZIP( name, COMPRESSED, EXPECTED ) \ + static const uint8_t name ## _compressed[] = COMPRESSED; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct gzip_test name = { \ + .compressed_name = #name ".gz", \ + .compressed = name ## _compressed, \ + .compressed_len = sizeof ( name ## _compressed ), \ + .expected_name = #name, \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** "Hello world" */ +GZIP ( hello_world, + DATA ( 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x28, 0xcf, 0x2f, 0xca, + 0x49, 0x01, 0x00, 0x52, 0x9e, 0xd6, 0x8b, 0x0b, 0x00, 0x00, + 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, + 0x64 ) ); + +/** "Hello filename" */ +GZIP ( hello_filename, + DATA ( 0x1f, 0x8b, 0x08, 0x08, 0xeb, 0x5b, 0x96, 0x60, 0x00, 0x03, + 0x68, 0x77, 0x2e, 0x74, 0x78, 0x74, 0x00, 0xf3, 0x48, 0xcd, + 0xc9, 0xc9, 0x57, 0x48, 0xcb, 0xcc, 0x49, 0xcd, 0x4b, 0xcc, + 0x4d, 0x05, 0x00, 0x69, 0x37, 0x25, 0x3c, 0x0e, 0x00, 0x00, + 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x66, 0x69, 0x6c, 0x65, + 0x6e, 0x61, 0x6d, 0x65 ) ); + +/** "Hello assorted headers" */ +GZIP ( hello_headers, + DATA ( 0x1f, 0x8b, 0x08, 0x1c, 0x11, 0x5c, 0x96, 0x60, 0x00, 0x03, + 0x05, 0x00, 0x41, 0x70, 0x01, 0x00, 0x0d, 0x68, 0x77, 0x2e, + 0x74, 0x78, 0x74, 0x00, 0x2f, 0x2f, 0x77, 0x68, 0x79, 0x3f, + 0x00, 0xf3, 0x48, 0xcd, 0xc9, 0xc9, 0x57, 0x48, 0x2c, 0x2e, + 0xce, 0x2f, 0x2a, 0x49, 0x4d, 0x51, 0xc8, 0x48, 0x4d, 0x4c, + 0x49, 0x2d, 0x2a, 0x06, 0x00, 0x59, 0xa4, 0x19, 0x61, 0x16, + 0x00, 0x00, 0x00 ), + DATA ( 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x61, 0x73, 0x73, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x20, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73 ) ); + +/** + * Report gzip test result + * + * @v test gzip test + * @v file Test code file + * @v line Test code line + */ +static void gzip_okx ( struct gzip_test *test, const char *file, + unsigned int line ) { + struct image *image; + struct image *extracted; + + /* Construct compressed image */ + image = image_memory ( test->compressed_name, + virt_to_user ( test->compressed ), + test->compressed_len ); + okx ( image != NULL, file, line ); + okx ( image->len == test->compressed_len, file, line ); + + /* Check type detection */ + okx ( image->type == &gzip_image_type, file, line ); + + /* Extract archive image */ + okx ( image_extract ( image, NULL, &extracted ) == 0, file, line ); + + /* Verify extracted image content */ + okx ( extracted->len == test->expected_len, file, line ); + okx ( memcmp_user ( extracted->data, 0, + virt_to_user ( test->expected ), 0, + test->expected_len ) == 0, file, line ); + + /* Verify extracted image name */ + okx ( strcmp ( extracted->name, test->expected_name ) == 0, + file, line ); + + /* Unregister images */ + unregister_image ( extracted ); + unregister_image ( image ); +} +#define gzip_ok( test ) gzip_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform gzip self-test + * + */ +static void gzip_test_exec ( void ) { + + gzip_ok ( &hello_world ); + gzip_ok ( &hello_filename ); + gzip_ok ( &hello_headers ); +} + +/** gzip self-test */ +struct self_test gzip_test __self_test = { + .name = "gzip", + .exec = gzip_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index f4cf041ac..1cc4c81e8 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -74,3 +74,4 @@ REQUIRE_OBJECT ( der_test ); REQUIRE_OBJECT ( pem_test ); REQUIRE_OBJECT ( ntlm_test ); REQUIRE_OBJECT ( zlib_test ); +REQUIRE_OBJECT ( gzip_test ); -- cgit v1.2.3-55-g7522 From 13c1abe10abd1c24307a6777b6cedfc5b46b088d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 10:09:33 +0100 Subject: [prefix] Specify i486 architecture for LZMA decompressor The decompressor uses the i486 "bswap" instruction, but does not require any instructions that exist only on i586 or above. Update the ".arch" directive to reflect the requirements of the code as implemented. Reported-by: Martin Habets Signed-off-by: Michael Brown --- src/arch/x86/prefix/unlzma.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arch/x86/prefix/unlzma.S b/src/arch/x86/prefix/unlzma.S index 956eeb24c..979f699ee 100644 --- a/src/arch/x86/prefix/unlzma.S +++ b/src/arch/x86/prefix/unlzma.S @@ -44,7 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ .text - .arch i586 + .arch i486 .section ".prefix.lib", "ax", @progbits #ifdef CODE16 -- cgit v1.2.3-55-g7522 From 05fcf1a2f0809b8d87ca5affea1f1bfe0996235b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 10:24:00 +0100 Subject: [rng] Check for TSC support before using RTC entropy source Signed-off-by: Michael Brown --- src/arch/x86/include/ipxe/cpuid.h | 3 +++ src/arch/x86/interface/pcbios/rtc_entropy.c | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/arch/x86/include/ipxe/cpuid.h b/src/arch/x86/include/ipxe/cpuid.h index b5403bd9d..3983dfb89 100644 --- a/src/arch/x86/include/ipxe/cpuid.h +++ b/src/arch/x86/include/ipxe/cpuid.h @@ -42,6 +42,9 @@ struct x86_features { /** Hypervisor is present */ #define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL +/** TSC is present */ +#define CPUID_FEATURES_INTEL_EDX_TSC 0x00000010UL + /** FXSAVE and FXRSTOR are supported */ #define CPUID_FEATURES_INTEL_EDX_FXSR 0x01000000UL diff --git a/src/arch/x86/interface/pcbios/rtc_entropy.c b/src/arch/x86/interface/pcbios/rtc_entropy.c index e9e6baa59..e0c175685 100644 --- a/src/arch/x86/interface/pcbios/rtc_entropy.c +++ b/src/arch/x86/interface/pcbios/rtc_entropy.c @@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** Maximum time to wait for an RTC interrupt, in milliseconds */ @@ -174,8 +175,17 @@ static int rtc_entropy_check ( void ) { * @ret rc Return status code */ static int rtc_entropy_enable ( void ) { + struct x86_features features; int rc; + /* Check that TSC is supported */ + x86_features ( &features ); + if ( ! ( features.intel.edx & CPUID_FEATURES_INTEL_EDX_TSC ) ) { + DBGC ( &rtc_flag, "RTC has no TSC\n" ); + rc = -ENOTSUP; + goto err_no_tsc; + } + /* Hook ISR and enable RTC interrupts */ rtc_hook_isr(); enable_irq ( RTC_IRQ ); @@ -191,6 +201,7 @@ static int rtc_entropy_enable ( void ) { rtc_disable_int(); disable_irq ( RTC_IRQ ); rtc_unhook_isr(); + err_no_tsc: return rc; } -- cgit v1.2.3-55-g7522 From a6a8bb1a9a58becb82dfacc7bc27cf645d0e216c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 10:55:17 +0100 Subject: [undi] Read TSC only when profiling Avoid using the "rdtsc" instruction unless profiling is enabled. This allows the non-debug build of the UNDI driver to be used on a CPU such as a 486 that does not support the TSC. Reported-by: Nikolai Zhubr Signed-off-by: Michael Brown --- src/arch/x86/drivers/net/undinet.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/arch/x86/drivers/net/undinet.c b/src/arch/x86/drivers/net/undinet.c index 9b7d6d849..43cb18bfe 100644 --- a/src/arch/x86/drivers/net/undinet.c +++ b/src/arch/x86/drivers/net/undinet.c @@ -104,6 +104,13 @@ static union u_PXENV_ANY __bss16 ( undinet_params ); SEGOFF16_t __bss16 ( undinet_entry_point ); #define undinet_entry_point __use_data16 ( undinet_entry_point ) +/* Read TSC in real mode only when profiling */ +#if PROFILING +#define RDTSC_IF_PROFILING "rdtsc\n\t" +#else +#define RDTSC_IF_PROFILING "" +#endif + /** IRQ profiler */ static struct profiler undinet_irq_profiler __profiler = { .name = "undinet.irq" }; @@ -288,14 +295,14 @@ static int undinet_call ( struct undi_nic *undinic, unsigned int function, */ profile_start ( &profiler->total ); __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */ - "rdtsc\n\t" + RDTSC_IF_PROFILING "pushl %%eax\n\t" "pushw %%es\n\t" "pushw %%di\n\t" "pushw %%bx\n\t" "lcall *undinet_entry_point\n\t" "movw %%ax, %%bx\n\t" - "rdtsc\n\t" + RDTSC_IF_PROFILING "addw $6, %%sp\n\t" "popl %%edx\n\t" "popl %%ebp\n\t" /* gcc bug */ ) -- cgit v1.2.3-55-g7522 From 191f8825cbd17a6819545e5633287e3d934039b6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 13:54:59 +0100 Subject: [image] Allow single-member archive images to be executed transparently Provide image_extract_exec() as a helper method to allow single-member archive images (such as gzip compressed images) to be executed without an explicit "imgextract" step. Signed-off-by: Michael Brown --- src/core/archive.c | 30 ++++++++++++++++++++++++++++++ src/image/gzip.c | 1 + src/image/zlib.c | 1 + src/include/ipxe/image.h | 1 + 4 files changed, 33 insertions(+) diff --git a/src/core/archive.c b/src/core/archive.c index cd024bde3..7ef86bd9a 100644 --- a/src/core/archive.c +++ b/src/core/archive.c @@ -97,6 +97,36 @@ int image_extract ( struct image *image, const char *name, return rc; } +/** + * Extract and execute image + * + * @v image Image + * @ret rc Return status code + */ +int image_extract_exec ( struct image *image ) { + struct image *extracted; + int rc; + + /* Extract image */ + if ( ( rc = image_extract ( image, NULL, &extracted ) ) != 0 ) + goto err_extract; + + /* Set image command line */ + if ( ( rc = image_set_cmdline ( extracted, image->cmdline ) ) != 0 ) + goto err_set_cmdline; + + /* Set auto-unregister flag */ + extracted->flags |= IMAGE_AUTO_UNREGISTER; + + /* Tail-recurse into extracted image */ + return image_exec ( extracted ); + + err_set_cmdline: + unregister_image ( extracted ); + err_extract: + return rc; +} + /* Drag in objects via image_extract() */ REQUIRING_SYMBOL ( image_extract ); diff --git a/src/image/gzip.c b/src/image/gzip.c index 40728138b..98376e113 100644 --- a/src/image/gzip.c +++ b/src/image/gzip.c @@ -163,4 +163,5 @@ struct image_type gzip_image_type __image_type ( PROBE_NORMAL ) = { .name = "gzip", .probe = gzip_probe, .extract = gzip_extract, + .exec = image_extract_exec, }; diff --git a/src/image/zlib.c b/src/image/zlib.c index bc27142d7..a42c47e1b 100644 --- a/src/image/zlib.c +++ b/src/image/zlib.c @@ -159,4 +159,5 @@ struct image_type zlib_image_type __image_type ( PROBE_NORMAL ) = { .name = "zlib", .probe = zlib_probe, .extract = zlib_extract, + .exec = image_extract_exec, }; diff --git a/src/include/ipxe/image.h b/src/include/ipxe/image.h index c6e723dc6..0a5a26034 100644 --- a/src/include/ipxe/image.h +++ b/src/include/ipxe/image.h @@ -200,6 +200,7 @@ extern int image_asn1 ( struct image *image, size_t offset, struct asn1_cursor **cursor ); extern int image_extract ( struct image *image, const char *name, struct image **extracted ); +extern int image_extract_exec ( struct image *image ); /** * Increment reference count on an image -- cgit v1.2.3-55-g7522 From 62f732207e7cbd226a11b85581c2c33e1e6be409 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 14:13:01 +0100 Subject: [image] Propagate trust flag to extracted archive images An extracted image is wholly derived from the original archive image. If the original archive image has been verified and marked as trusted, then this trust logically extends to any image extracted from it. Signed-off-by: Michael Brown --- src/core/archive.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/core/archive.c b/src/core/archive.c index 7ef86bd9a..bb62c7e47 100644 --- a/src/core/archive.c +++ b/src/core/archive.c @@ -82,6 +82,10 @@ int image_extract ( struct image *image, const char *name, if ( ( rc = register_image ( *extracted ) ) != 0 ) goto err_register; + /* Propagate trust flag */ + if ( image->flags & IMAGE_TRUSTED ) + image_trust ( *extracted ); + /* Drop local reference to image */ image_put ( *extracted ); -- cgit v1.2.3-55-g7522 From d7bc9e9d67c2e7a4d2006d2c48485b3265aea038 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 12 May 2021 14:37:57 +0100 Subject: [image] Support archive image formats independently of "imgextract" command Support for the zlib and gzip archive image formats is currently included only if the IMAGE_ARCHIVE_CMD is used to enable the "imgextract" command. The ability to transparently execute a single-member archive image without using the "imgextract" command renders this unintuitive: a user wanting to gain the ability to boot a gzip-compressed kernel image would expect to have to enable IMAGE_GZIP rather than IMAGE_ARCHIVE_CMD. Reverse the inclusion logic, so that archive image formats must now be enabled explicitly (via IMAGE_GZIP and/or IMAGE_ZLIB), with the archive image management commands dragged in as needed if any archive image formats are enabled. The archive image management commands may be explicitly disabled via IMAGE_ARCHIVE_CMD if necessary. This matches the behaviour of IBMGMT_CMD and similar options, where the relevant commands are included only when something else already drags in the underlying feature. Signed-off-by: Michael Brown --- src/config/config.c | 9 ++++++--- src/config/config_archive.c | 7 ++----- src/config/general.h | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/config/config.c b/src/config/config.c index dd1fceb92..a81866132 100644 --- a/src/config/config.c +++ b/src/config/config.c @@ -182,6 +182,12 @@ REQUIRE_OBJECT ( efi_image ); #ifdef IMAGE_SDI REQUIRE_OBJECT ( sdi ); #endif +#ifdef IMAGE_ZLIB +REQUIRE_OBJECT ( zlib ); +#endif +#ifdef IMAGE_GZIP +REQUIRE_OBJECT ( gzip ); +#endif /* * Drag in all requested commands @@ -284,9 +290,6 @@ REQUIRE_OBJECT ( cert_cmd ); #ifdef IMAGE_MEM_CMD REQUIRE_OBJECT ( image_mem_cmd ); #endif -#ifdef IMAGE_ARCHIVE_CMD -REQUIRE_OBJECT ( image_archive_cmd ); -#endif /* * Drag in miscellaneous objects diff --git a/src/config/config_archive.c b/src/config/config_archive.c index 84f21b956..746fc7e44 100644 --- a/src/config/config_archive.c +++ b/src/config/config_archive.c @@ -31,9 +31,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); PROVIDE_REQUIRING_SYMBOL(); -#ifdef IMAGE_ZLIB -REQUIRE_OBJECT ( zlib ); -#endif -#ifdef IMAGE_GZIP -REQUIRE_OBJECT ( gzip ); +#ifdef IMAGE_ARCHIVE_CMD +REQUIRE_OBJECT ( image_archive_cmd ); #endif diff --git a/src/config/general.h b/src/config/general.h index fcfbaf5e9..2d15f500a 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -117,8 +117,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define IMAGE_PNG /* PNG image support */ #define IMAGE_DER /* DER image support */ #define IMAGE_PEM /* PEM image support */ -#define IMAGE_ZLIB /* ZLIB image support */ -#define IMAGE_GZIP /* GZIP image support */ +//#define IMAGE_ZLIB /* ZLIB image support */ +//#define IMAGE_GZIP /* GZIP image support */ /* * Command-line commands to include @@ -158,7 +158,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define NTP_CMD /* NTP commands */ //#define CERT_CMD /* Certificate management commands */ //#define IMAGE_MEM_CMD /* Read memory command */ -//#define IMAGE_ARCHIVE_CMD /* Archive image management commands */ +#define IMAGE_ARCHIVE_CMD /* Archive image management commands */ /* * ROM-specific options -- cgit v1.2.3-55-g7522 From adb2ed907ea0b67a7e39a65ca1bbcf7224415e1b Mon Sep 17 00:00:00 2001 From: Christian Nilsson Date: Thu, 14 Feb 2019 21:56:11 +0100 Subject: [intel] Add PCI ID for I219-V and -LM 10 to 15 Signed-off-by: Christian Nilsson --- src/drivers/net/intel.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c index 83492961f..ea3ebf68d 100644 --- a/src/drivers/net/intel.c +++ b/src/drivers/net/intel.c @@ -1025,6 +1025,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x043a, "dh8900cc-f", "DH8900CC Fiber", 0 ), PCI_ROM ( 0x8086, 0x043c, "dh8900cc-b", "DH8900CC Backplane", 0 ), PCI_ROM ( 0x8086, 0x0440, "dh8900cc-s", "DH8900CC SFP", 0 ), + PCI_ROM ( 0x8086, 0x0d4c, "i219lm-11", "I219-LM (11)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4d, "i219v-11", "I219-V (11)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4e, "i219lm-10", "I219-LM (10)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d4f, "i219v-10", "I219-V (10)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d53, "i219lm-12", "I219-LM (12)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x0d55, "i219v-12", "I219-V (12)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1000, "82542-f", "82542 (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1001, "82543gc-f", "82543GC (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1004, "82543gc", "82543GC (Copper)", 0 ), @@ -1161,6 +1167,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f4, "i219lm-15", "I219-LM (15)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f5, "i219v-15", "I219-V (15)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15f9, "i219lm-14", "I219-LM (14)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ), + PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ), PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), -- cgit v1.2.3-55-g7522 From 059c4dc688cea6d9398d63ca10f5c00e94ae2159 Mon Sep 17 00:00:00 2001 From: Joseph Date: Thu, 22 Apr 2021 20:07:53 -0700 Subject: [bnxt] Use hexadecimal values in PCI_ROM entries Use hexadecimal values instead of macros in PCI_ROM entries so Perl script can parse them correctly. Move PCI_ROM entries from header file to C file. Integrate bnxt_vf_nics array into PCI_ROM entries by introducing BNXT_FLAG_PCI_VF flag into driver_data field. Add whitespaces in PCI_ROM entries for style consistency. Signed-off-by: Michael Brown --- src/drivers/net/bnxt/bnxt.c | 68 ++++++++++++++++++++-- src/drivers/net/bnxt/bnxt.h | 137 +------------------------------------------- 2 files changed, 64 insertions(+), 141 deletions(-) diff --git a/src/drivers/net/bnxt/bnxt.c b/src/drivers/net/bnxt/bnxt.c index b8663c00a..e3876503f 100644 --- a/src/drivers/net/bnxt/bnxt.c +++ b/src/drivers/net/bnxt/bnxt.c @@ -23,16 +23,74 @@ static void bnxt_adv_cq_index ( struct bnxt *bp, u16 cnt ); static int bnxt_rx_complete ( struct net_device *dev, struct rx_pkt_cmpl *rx ); void bnxt_link_evt ( struct bnxt *bp, struct hwrm_async_event_cmpl *evt ); +static struct pci_device_id bnxt_nics[] = { + PCI_ROM( 0x14e4, 0x16c0, "14e4-16C0", "14e4-16C0", 0 ), + PCI_ROM( 0x14e4, 0x16c1, "14e4-16C1", "14e4-16C1", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x16c8, "14e4-16C8", "14e4-16C8", 0 ), + PCI_ROM( 0x14e4, 0x16c9, "14e4-16C9", "14e4-16C9", 0 ), + PCI_ROM( 0x14e4, 0x16ca, "14e4-16CA", "14e4-16CA", 0 ), + PCI_ROM( 0x14e4, 0x16cc, "14e4-16CC", "14e4-16CC", 0 ), + PCI_ROM( 0x14e4, 0x16cd, "14e4-16CD", "14e4-16CD", 0 ), + PCI_ROM( 0x14e4, 0x16ce, "14e4-16CE", "14e4-16CE", 0 ), + PCI_ROM( 0x14e4, 0x16cf, "14e4-16CF", "14e4-16CF", 0 ), + PCI_ROM( 0x14e4, 0x16d0, "14e4-16D0", "14e4-16D0", 0 ), + PCI_ROM( 0x14e4, 0x16d1, "14e4-16D1", "14e4-16D1", 0 ), + PCI_ROM( 0x14e4, 0x16d2, "14e4-16D2", "14e4-16D2", 0 ), + PCI_ROM( 0x14e4, 0x16d4, "14e4-16D4", "14e4-16D4", 0 ), + PCI_ROM( 0x14e4, 0x16d5, "14e4-16D5", "14e4-16D5", 0 ), + PCI_ROM( 0x14e4, 0x16d6, "14e4-16D6", "14e4-16D6", 0 ), + PCI_ROM( 0x14e4, 0x16d7, "14e4-16D7", "14e4-16D7", 0 ), + PCI_ROM( 0x14e4, 0x16d8, "14e4-16D8", "14e4-16D8", 0 ), + PCI_ROM( 0x14e4, 0x16d9, "14e4-16D9", "14e4-16D9", 0 ), + PCI_ROM( 0x14e4, 0x16da, "14e4-16DA", "14e4-16DA", 0 ), + PCI_ROM( 0x14e4, 0x16db, "14e4-16DB", "14e4-16DB", 0 ), + PCI_ROM( 0x14e4, 0x16dc, "14e4-16DC", "14e4-16DC", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x16de, "14e4-16DE", "14e4-16DE", 0 ), + PCI_ROM( 0x14e4, 0x16df, "14e4-16DF", "14e4-16DF", 0 ), + PCI_ROM( 0x14e4, 0x16e0, "14e4-16E0", "14e4-16E0", 0 ), + PCI_ROM( 0x14e4, 0x16e2, "14e4-16E2", "14e4-16E2", 0 ), + PCI_ROM( 0x14e4, 0x16e3, "14e4-16E3", "14e4-16E3", 0 ), + PCI_ROM( 0x14e4, 0x16e4, "14e4-16E4", "14e4-16E4", 0 ), + PCI_ROM( 0x14e4, 0x16e7, "14e4-16E7", "14e4-16E7", 0 ), + PCI_ROM( 0x14e4, 0x16e8, "14e4-16E8", "14e4-16E8", 0 ), + PCI_ROM( 0x14e4, 0x16e9, "14e4-16E9", "14e4-16E9", 0 ), + PCI_ROM( 0x14e4, 0x16ea, "14e4-16EA", "14e4-16EA", 0 ), + PCI_ROM( 0x14e4, 0x16eb, "14e4-16EB", "14e4-16EB", 0 ), + PCI_ROM( 0x14e4, 0x16ec, "14e4-16EC", "14e4-16EC", 0 ), + PCI_ROM( 0x14e4, 0x16ed, "14e4-16ED", "14e4-16ED", 0 ), + PCI_ROM( 0x14e4, 0x16ee, "14e4-16EE", "14e4-16EE", 0 ), + PCI_ROM( 0x14e4, 0x16ef, "14e4-16EF", "14e4-16EF", 0 ), + PCI_ROM( 0x14e4, 0x16f0, "14e4-16F0", "14e4-16F0", 0 ), + PCI_ROM( 0x14e4, 0x16f1, "14e4-16F1", "14e4-16F1", 0 ), + PCI_ROM( 0x14e4, 0x1604, "14e4-1604", "14e4-1604", 0 ), + PCI_ROM( 0x14e4, 0x1605, "14e4-1605", "14e4-1605", 0 ), + PCI_ROM( 0x14e4, 0x1606, "14e4-1606", "14e4-1606", 0 ), + PCI_ROM( 0x14e4, 0x1609, "14e4-1609", "14e4-1609", 0 ), + PCI_ROM( 0x14e4, 0x1614, "14e4-1614", "14e4-1614", 0 ), + PCI_ROM( 0x14e4, 0xd802, "14e4-D802", "14e4-D802", 0 ), + PCI_ROM( 0x14e4, 0xd804, "14e4-D804", "14e4-D804", 0 ), + PCI_ROM( 0x14e4, 0x1750, "14e4-1750", "14e4-1750", 0 ), + PCI_ROM( 0x14e4, 0x1802, "14e4-1802", "14e4-1802", 0 ), + PCI_ROM( 0x14e4, 0x1805, "14e4-1805", "14e4-1805", 0 ), + PCI_ROM( 0x14e4, 0x1751, "14e4-1751", "14e4-1751", 0 ), + PCI_ROM( 0x14e4, 0x1801, "14e4-1801", "14e4-1801", 0 ), + PCI_ROM( 0x14e4, 0x1804, "14e4-1804", "14e4-1804", 0 ), + PCI_ROM( 0x14e4, 0x1752, "14e4-1752", "14e4-1752", 0 ), + PCI_ROM( 0x14e4, 0x1800, "14e4-1800", "14e4-1800", 0 ), + PCI_ROM( 0x14e4, 0x1803, "14e4-1803", "14e4-1803", 0 ), + PCI_ROM( 0x14e4, 0x1806, "14e4-1806", "14e4-1806", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1807, "14e4-1807", "14e4-1807", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1808, "14e4-1808", "14e4-1808", BNXT_FLAG_PCI_VF ), + PCI_ROM( 0x14e4, 0x1809, "14e4-1809", "14e4-1809", BNXT_FLAG_PCI_VF ), +}; + /** * Check if Virtual Function */ u8 bnxt_is_pci_vf ( struct pci_device *pdev ) { - u16 i; - - for ( i = 0; i < ARRAY_SIZE ( bnxt_vf_nics ); i++ ) { - if ( pdev->device == bnxt_vf_nics[i] ) - return 1; + if ( FLAG_TEST ( pdev->id->driver_data, BNXT_FLAG_PCI_VF ) ) { + return 1; } return 0; } diff --git a/src/drivers/net/bnxt/bnxt.h b/src/drivers/net/bnxt/bnxt.h index 4cca07bd3..2cbaec5e5 100644 --- a/src/drivers/net/bnxt/bnxt.h +++ b/src/drivers/net/bnxt/bnxt.h @@ -51,6 +51,7 @@ union dma_addr64_t { #define BNXT_FLAG_MULTI_HOST 0x0008 #define BNXT_FLAG_NPAR_MODE 0x0010 #define BNXT_FLAG_ATOMICS_ENABLE 0x0020 +#define BNXT_FLAG_PCI_VF 0x0040 /******************************************************************************* * Status codes. ******************************************************************************/ @@ -867,140 +868,4 @@ struct bnxt { FUNC_VF_CFG_REQ_ENABLES_ASYNC_EVENT_CR | \ FUNC_VF_CFG_REQ_ENABLES_DFLT_MAC_ADDR) -/* Device ID's */ -#define PCI_VID_BCOM 0x14e4 #define CHIP_NUM_57500 0x1750 - -#define DID_57508 0x1750 -#define DID_57508_MF 0x1802 -#define DID_57508_MF_RDMA 0x1805 -#define DID_57504 0x1751 -#define DID_57504_MF 0x1801 -#define DID_57504_MF_RDMA 0x1804 -#define DID_57502 0x1752 -#define DID_57502_MF 0x1800 -#define DID_57502_MF_RDMA 0x1803 -#define DID_57508_VF 0x1806 -#define DID_57508_VF_RDMA 0x1807 -#define DID_57508_VF_HV 0x1806 -#define DID_57508_VF_RDMA_HV 0x1807 -/* Stratus Device IDs */ -#define DID_57320_1 0x16F0 -#define DID_57320_2 0x16F1 -#define DID_57454_MHB 0x1604 -#define DID_57454_MHB_RDMA 0x1605 -#define DID_57454_VF_RDMA 0x1606 -#define DID_57454_VF 0x1609 -#define DID_57454 0x1614 -#define DID_58802 0xD802 -#define DID_58804 0xD804 - -#define DID_57417_RDMA_MF 0x16C0 -#define DID_57417_VF_RDMA 0x16c1 -#define DID_57301 0x16C8 -#define DID_57302 0x16C9 -#define DID_57304 0x16CA -#define DID_57417_MF 0x16CC -#define DID_58700 0x16CD -#define DID_57311 0x16CE -#define DID_57312 0x16CF -#define DID_57402 0x16D0 -#define DID_57404 0x16D1 -#define DID_57406 0x16D2 -#define DID_57402_MF 0x16D4 -#define DID_57407C 0x16D5 -#define DID_57412 0x16D6 -#define DID_57414 0x16D7 -#define DID_57416C 0x16D8 -#define DID_57417C 0x16D9 -#define DID_57402L 0x16DA -#define DID_57404L 0x16DB -#define DID_57417_VF 0x16dc -#define DID_57412_MF 0x16DE -#define DID_57314 0x16DF -#define DID_57317C 0x16E0 -#define DID_57417F 0x16E2 -#define DID_57416F 0x16E3 -#define DID_57317F 0x16E4 -#define DID_57404_MF 0x16E7 -#define DID_57406_MF 0x16E8 -#define DID_57407F 0x16E9 -#define DID_57407_MF 0x16EA -#define DID_57412_RDMA_MF 0x16EB -#define DID_57414_MF 0x16EC -#define DID_57414_RDMA_MF 0x16ED -#define DID_57416_MF 0x16EE -#define DID_57416_RDMA_MF 0x16EF - -static struct pci_device_id bnxt_nics[] = { - PCI_ROM(PCI_VID_BCOM, DID_57417_RDMA_MF, "14e4-16C0", "14e4-16C0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_VF_RDMA, "14e4-16C1", "14e4-16C1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57301, "14e4-16C8", "14e4-16C8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57302, "14e4-16C9", "14e4-16C9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57304, "14e4-16CA", "14e4-16CA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_MF, "14e4-16CC", "14e4-16CC", 0), - PCI_ROM(PCI_VID_BCOM, DID_58700, "14e4-16CD", "14e4-16CD", 0), - PCI_ROM(PCI_VID_BCOM, DID_57311, "14e4-16CE", "14e4-16CE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57312, "14e4-16CF", "14e4-16CF", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402, "14e4-16D0", "14e4-16D0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404, "14e4-16D1", "14e4-16D1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57406, "14e4-16D2", "14e4-16D2", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402_MF, "14e4-16D4", "14e4-16D4", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407C, "14e4-16D5", "14e4-16D5", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412, "14e4-16D6", "14e4-16D6", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414, "14e4-16D7", "14e4-16D7", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416C, "14e4-16D8", "14e4-16D8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417C, "14e4-16D9", "14e4-16D9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57402L, "14e4-16DA", "14e4-16DA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404L, "14e4-16DB", "14e4-16DB", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417_VF, "14e4-16DC", "14e4-16DC", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412_MF, "14e4-16DE", "14e4-16DE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57314, "14e4-16DF", "14e4-16DF", 0), - PCI_ROM(PCI_VID_BCOM, DID_57317C, "14e4-16E0", "14e4-16E0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57417F, "14e4-16E2", "14e4-16E2", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416F, "14e4-16E3", "14e4-16E3", 0), - PCI_ROM(PCI_VID_BCOM, DID_57317F, "14e4-16E4", "14e4-16E4", 0), - PCI_ROM(PCI_VID_BCOM, DID_57404_MF, "14e4-16E7", "14e4-16E7", 0), - PCI_ROM(PCI_VID_BCOM, DID_57406_MF, "14e4-16E8", "14e4-16E8", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407F, "14e4-16E9", "14e4-16E9", 0), - PCI_ROM(PCI_VID_BCOM, DID_57407_MF, "14e4-16EA", "14e4-16EA", 0), - PCI_ROM(PCI_VID_BCOM, DID_57412_RDMA_MF, "14e4-16EB", "14e4-16EB", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414_MF, "14e4-16EC", "14e4-16EC", 0), - PCI_ROM(PCI_VID_BCOM, DID_57414_RDMA_MF, "14e4-16ED", "14e4-16ED", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416_MF, "14e4-16EE", "14e4-16EE", 0), - PCI_ROM(PCI_VID_BCOM, DID_57416_RDMA_MF, "14e4-16EF", "14e4-16EF", 0), - - PCI_ROM(PCI_VID_BCOM, DID_57320_1, "14e4-16F0", "14e4-16F0", 0), - PCI_ROM(PCI_VID_BCOM, DID_57320_2, "14e4-16F1", "14e4-16F1", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_MHB, "14e4-1604", "14e4-1604", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_MHB_RDMA, "14e4-1605", "14e4-1605", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_VF_RDMA, "14e4-1606", "14e4-1606", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454_VF, "14e4-1609", "14e4-1609", 0), - PCI_ROM(PCI_VID_BCOM, DID_57454, "14e4-1614", "14e4-1614", 0), - PCI_ROM(PCI_VID_BCOM, DID_58802, "14e4-D802", "14e4-D802", 0), - PCI_ROM(PCI_VID_BCOM, DID_58804, "14e4-D804", "14e4-D804", 0), - - PCI_ROM(PCI_VID_BCOM, DID_57508, "14e4-1750", "14e4-1750", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_MF, "14e4-1802", "14e4-1802", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_MF_RDMA, "14e4-1805", "14e4-1805", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504, "14e4-1751", "14e4-1751", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504_MF, "14e4-1801", "14e4-1801", 0), - PCI_ROM(PCI_VID_BCOM, DID_57504_MF_RDMA, "14e4-1804", "14e4-1804", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502, "14e4-1752", "14e4-1752", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502_MF, "14e4-1800", "14e4-1800", 0), - PCI_ROM(PCI_VID_BCOM, DID_57502_MF_RDMA, "14e4-1803", "14e4-1803", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF, "14e4-1806", "14e4-1806", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF_RDMA, "14e4-1807", "14e4-1807", 0), - PCI_ROM(PCI_VID_BCOM, DID_57508_VF_HV, "14e4-1808", "14e4-1808", 0), - PCI_ROM(PCI_VID_BCOM, - DID_57508_VF_RDMA_HV, "14e4-1809", "14e4-1809", 0), -}; - -static u16 bnxt_vf_nics[] = { - DID_57508_VF, - DID_57508_VF_RDMA, - DID_57508_VF_HV, - DID_57508_VF_RDMA_HV, - DID_57417_VF, - DID_57417_VF_RDMA, -}; -- cgit v1.2.3-55-g7522 From 661093054bcfae16d79404304d6f8318baf1231e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 May 2021 11:45:24 +0100 Subject: [libc] Add strncasecmp() Signed-off-by: Michael Brown --- src/core/string.c | 17 ++++++++++++++++- src/include/strings.h | 2 ++ src/tests/string_test.c | 8 ++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/core/string.c b/src/core/string.c index 188fe0864..9a1b9b72a 100644 --- a/src/core/string.c +++ b/src/core/string.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -205,11 +206,24 @@ int strncmp ( const char *first, const char *second, size_t max ) { * @ret diff Difference */ int strcasecmp ( const char *first, const char *second ) { + + return strncasecmp ( first, second, ~( ( size_t ) 0 ) ); +} + +/** + * Compare case-insensitive strings + * + * @v first First string + * @v second Second string + * @v max Maximum length to compare + * @ret diff Difference + */ +int strncasecmp ( const char *first, const char *second, size_t max ) { const uint8_t *first_bytes = ( ( const uint8_t * ) first ); const uint8_t *second_bytes = ( ( const uint8_t * ) second ); int diff; - for ( ; ; first_bytes++, second_bytes++ ) { + for ( ; max-- ; first_bytes++, second_bytes++ ) { diff = ( toupper ( *first_bytes ) - toupper ( *second_bytes ) ); if ( diff ) @@ -217,6 +231,7 @@ int strcasecmp ( const char *first, const char *second ) { if ( ! *first_bytes ) return 0; } + return 0; } /** diff --git a/src/include/strings.h b/src/include/strings.h index fab26dc28..d7e9d6971 100644 --- a/src/include/strings.h +++ b/src/include/strings.h @@ -189,5 +189,7 @@ bzero ( void *dest, size_t len ) { } int __pure strcasecmp ( const char *first, const char *second ) __nonnull; +int __pure strncasecmp ( const char *first, const char *second, + size_t max ) __nonnull; #endif /* _STRINGS_H */ diff --git a/src/tests/string_test.c b/src/tests/string_test.c index 88a730aec..3afb8deb2 100644 --- a/src/tests/string_test.c +++ b/src/tests/string_test.c @@ -105,6 +105,14 @@ static void string_test_exec ( void ) { ok ( strcasecmp ( "Uncle", "Uncle Jack" ) != 0 ); ok ( strcasecmp ( "not", "equal" ) != 0 ); + /* Test strncasecmp() */ + ok ( strncasecmp ( "", "", 0 ) == 0 ); + ok ( strncasecmp ( "", "", 73 ) == 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jack", 47 ) == 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 47 ) != 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 9 ) != 0 ); + ok ( strncasecmp ( "Uncle Jack", "Uncle jake", 8 ) == 0 ); + /* Test memcmp() */ ok ( memcmp ( "", "", 0 ) == 0 ); ok ( memcmp ( "Foo", "Foo", 3 ) == 0 ); -- cgit v1.2.3-55-g7522 From fc8bd4ba1a65db9d9091705f30fec19ded75530c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 May 2021 11:46:28 +0100 Subject: [x509] Use case-insensitive comparison for certificate names DNS names are case-insensitive, and RFC 5280 (unlike RFC 3280) mandates support for case-insensitive name comparison in X.509 certificates. Signed-off-by: Michael Brown --- src/crypto/x509.c | 3 ++- src/tests/x509_test.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/crypto/x509.c b/src/crypto/x509.c index 17d8c7a43..1f017eb03 100644 --- a/src/crypto/x509.c +++ b/src/crypto/x509.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -1464,7 +1465,7 @@ static int x509_check_dnsname ( struct x509_certificate *cert, /* Compare names */ if ( ! ( ( strlen ( name ) == len ) && - ( memcmp ( name, dnsname, len ) == 0 ) ) ) + ( strncasecmp ( name, dnsname, len ) == 0 ) ) ) return -ENOENT; if ( name != fullname ) { diff --git a/src/tests/x509_test.c b/src/tests/x509_test.c index 256c3e85e..b6cba575c 100644 --- a/src/tests/x509_test.c +++ b/src/tests/x509_test.c @@ -1037,6 +1037,7 @@ static void x509_test_exec ( void ) { /* Check certificate names */ x509_check_name_ok ( &server_crt, "boot.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "demo.test.ipxe.org" ); + x509_check_name_ok ( &server_crt, "demo.test.iPXE.org" ); x509_check_name_fail_ok ( &server_crt, "incorrect.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "anything.alt.test.ipxe.org" ); x509_check_name_ok ( &server_crt, "wildcard.alt.test.ipxe.org" ); -- cgit v1.2.3-55-g7522 From bfca3db41e9af78e56e7a9b7f30121df83b6af0a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 17 May 2021 14:57:48 +0100 Subject: [cpio] Split out bzImage initrd CPIO header construction iPXE will construct CPIO headers for images that have a non-empty command line, thereby allowing raw images (without CPIO headers) to be injected into a dynamically constructed initrd. This feature is currently implemented within the BIOS-only bzImage format support. Split out the CPIO header construction logic to allow for reuse in other contexts such as in a UEFI build. Signed-off-by: Michael Brown --- src/arch/x86/image/bzimage.c | 52 ++------------------------ src/arch/x86/image/initrd.c | 1 + src/arch/x86/include/initrd.h | 7 ---- src/core/cpio.c | 85 +++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/cpio.h | 21 +++++++++++ 5 files changed, 110 insertions(+), 56 deletions(-) diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index 51498bf95..a782127a1 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -326,32 +326,6 @@ static void bzimage_set_cmdline ( struct image *image, DBGC ( image, "bzImage %p command line \"%s\"\n", image, cmdline ); } -/** - * Parse standalone image command line for cpio parameters - * - * @v image bzImage file - * @v cpio CPIO header - * @v cmdline Command line - */ -static void bzimage_parse_cpio_cmdline ( struct image *image, - struct cpio_header *cpio, - const char *cmdline ) { - char *arg; - char *end; - unsigned int mode; - - /* Look for "mode=" */ - if ( ( arg = strstr ( cmdline, "mode=" ) ) ) { - arg += 5; - mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); - if ( *end && ( *end != ' ' ) ) { - DBGC ( image, "bzImage %p strange \"mode=\"" - "terminator '%c'\n", image, *end ); - } - cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); - } -} - /** * Align initrd length * @@ -374,11 +348,9 @@ static inline size_t bzimage_align ( size_t len ) { static size_t bzimage_load_initrd ( struct image *image, struct image *initrd, userptr_t address ) { - char *filename = initrd->cmdline; - char *cmdline; + const char *filename = cpio_name ( initrd ); struct cpio_header cpio; size_t offset; - size_t name_len; size_t pad_len; /* Do not include kernel image itself as an initrd */ @@ -386,25 +358,7 @@ static size_t bzimage_load_initrd ( struct image *image, return 0; /* Create cpio header for non-prebuilt images */ - if ( filename && filename[0] ) { - cmdline = strchr ( filename, ' ' ); - name_len = ( ( cmdline ? ( ( size_t ) ( cmdline - filename ) ) - : strlen ( filename ) ) + 1 /* NUL */ ); - memset ( &cpio, '0', sizeof ( cpio ) ); - memcpy ( cpio.c_magic, CPIO_MAGIC, sizeof ( cpio.c_magic ) ); - cpio_set_field ( cpio.c_mode, 0100644 ); - cpio_set_field ( cpio.c_nlink, 1 ); - cpio_set_field ( cpio.c_filesize, initrd->len ); - cpio_set_field ( cpio.c_namesize, name_len ); - if ( cmdline ) { - bzimage_parse_cpio_cmdline ( image, &cpio, - ( cmdline + 1 /* ' ' */ )); - } - offset = ( ( sizeof ( cpio ) + name_len + 0x03 ) & ~0x03 ); - } else { - offset = 0; - name_len = 0; - } + offset = cpio_header ( initrd, &cpio ); /* Copy in initrd image body (and cpio header if applicable) */ if ( address ) { @@ -413,7 +367,7 @@ static size_t bzimage_load_initrd ( struct image *image, memset_user ( address, 0, 0, offset ); copy_to_user ( address, 0, &cpio, sizeof ( cpio ) ); copy_to_user ( address, sizeof ( cpio ), filename, - ( name_len - 1 /* NUL (or space) */ ) ); + cpio_name_len ( initrd ) ); } DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)" "%s%s\n", image, initrd, user_to_phys ( address, 0 ), diff --git a/src/arch/x86/image/initrd.c b/src/arch/x86/image/initrd.c index 49b959a91..d7b1f5773 100644 --- a/src/arch/x86/image/initrd.c +++ b/src/arch/x86/image/initrd.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** @file * diff --git a/src/arch/x86/include/initrd.h b/src/arch/x86/include/initrd.h index ddb3e5a45..2fb9d3d3a 100644 --- a/src/arch/x86/include/initrd.h +++ b/src/arch/x86/include/initrd.h @@ -11,13 +11,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -/** Minimum alignment for initrds - * - * Some versions of Linux complain about initrds that are not - * page-aligned. - */ -#define INITRD_ALIGN 4096 - /** Minimum free space required to reshuffle initrds * * Chosen to avoid absurdly long reshuffling times diff --git a/src/core/cpio.c b/src/core/cpio.c index 080c72daf..27aee7581 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include +#include #include #include @@ -45,3 +46,87 @@ void cpio_set_field ( char *field, unsigned long value ) { snprintf ( buf, sizeof ( buf ), "%08lx", value ); memcpy ( field, buf, 8 ); } + +/** + * Get CPIO image filename + * + * @v image Image + * @ret len CPIO filename length (0 for no filename) + */ +size_t cpio_name_len ( struct image *image ) { + const char *name = cpio_name ( image ); + char *sep; + size_t len; + + /* Check for existence of CPIO filename */ + if ( ! name ) + return 0; + + /* Locate separator (if any) */ + sep = strchr ( name, ' ' ); + len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) ); + + return len; +} + +/** + * Parse CPIO image parameters + * + * @v image Image + * @v cpio CPIO header to fill in + */ +static void cpio_parse_cmdline ( struct image *image, + struct cpio_header *cpio ) { + const char *cmdline; + 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; + mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); + if ( *end && ( *end != ' ' ) ) { + DBGC ( image, "CPIO %p strange \"mode=\" " + "terminator '%c'\n", image, *end ); + } + cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); + } +} + +/** + * Construct CPIO header for image, if applicable + * + * @v image Image + * @v cpio CPIO header to fill in + * @ret len Length of magic CPIO header (including filename) + */ +size_t cpio_header ( struct image *image, struct cpio_header *cpio ) { + size_t name_len; + size_t len; + + /* Get filename length */ + name_len = cpio_name_len ( image ); + + /* Images with no filename are assumed to already be CPIO archives */ + if ( ! name_len ) + return 0; + + /* Construct CPIO header */ + memset ( cpio, '0', sizeof ( *cpio ) ); + memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) ); + cpio_set_field ( cpio->c_mode, 0100644 ); + cpio_set_field ( cpio->c_nlink, 1 ); + cpio_set_field ( cpio->c_filesize, image->len ); + cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) ); + cpio_parse_cmdline ( image, cpio ); + + /* Calculate total length */ + len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 ) + & ~( CPIO_ALIGN - 1 ) ); + + return len; +} diff --git a/src/include/ipxe/cpio.h b/src/include/ipxe/cpio.h index 0637c531d..9c5e22d5a 100644 --- a/src/include/ipxe/cpio.h +++ b/src/include/ipxe/cpio.h @@ -9,6 +9,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include + /** A CPIO archive header * * All field are hexadecimal ASCII numbers padded with '0' on the @@ -48,6 +50,25 @@ struct cpio_header { /** CPIO magic */ #define CPIO_MAGIC "070701" +/** CPIO header length alignment */ +#define CPIO_ALIGN 4 + +/** Alignment for CPIO archives within an initrd */ +#define INITRD_ALIGN 4096 + +/** + * Get CPIO image name + * + * @v image Image + * @ret name Image name (not NUL terminated) + */ +static inline __attribute__ (( always_inline )) const char * +cpio_name ( struct image *image ) { + return image->cmdline; +} + extern void cpio_set_field ( char *field, unsigned long value ); +extern size_t cpio_name_len ( struct image *image ); +extern size_t cpio_header ( struct image *image, struct cpio_header *cpio ); #endif /* _IPXE_CPIO_H */ -- cgit v1.2.3-55-g7522 From ef9953b7122937dc12762e9f5ce797ba6859a24e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 18 May 2021 14:03:15 +0100 Subject: [efi] Allow for non-image-backed virtual files Restructure the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL implementation to allow for the existence of virtual files that are not simply backed by a single underlying image. Signed-off-by: Michael Brown --- src/interface/efi/efi_file.c | 290 +++++++++++++++++++++++++++++++++---------- 1 file changed, 221 insertions(+), 69 deletions(-) diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 52de0987c..c67fdea54 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -50,18 +50,54 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 -/** An image exposed as an EFI file */ +/** An EFI virtual file reader */ +struct efi_file_reader { + /** EFI file */ + struct efi_file *file; + /** Position within virtual file */ + size_t pos; + /** Output data buffer */ + void *data; + /** Length of output data buffer */ + size_t len; +}; + +/** An EFI file */ struct efi_file { + /** Reference count */ + struct refcnt refcnt; /** EFI file protocol */ EFI_FILE_PROTOCOL file; - /** Image */ + /** Image (if any) */ struct image *image; + /** Filename */ + const char *name; /** Current file position */ size_t pos; + /** + * Read from file + * + * @v reader File reader + * @ret len Length read + */ + size_t ( * read ) ( struct efi_file_reader *reader ); }; static struct efi_file efi_file_root; +/** + * Free EFI file + * + * @v refcnt Reference count + */ +static void efi_file_free ( struct refcnt *refcnt ) { + struct efi_file *file = + container_of ( refcnt, struct efi_file, refcnt ); + + image_put ( file->image ); + free ( file ); +} + /** * Get EFI file name (for debugging) * @@ -70,28 +106,140 @@ static struct efi_file efi_file_root; */ static const char * efi_file_name ( struct efi_file *file ) { - return ( file->image ? file->image->name : "" ); + return ( file == &efi_file_root ? "" : file->name ); } /** * Find EFI file image * - * @v wname Filename + * @v name Filename * @ret image Image, or NULL */ -static struct image * efi_file_find ( const CHAR16 *wname ) { - char name[ wcslen ( wname ) + 1 /* NUL */ ]; +static struct image * efi_file_find ( const char *name ) { struct image *image; /* Find image */ - snprintf ( name, sizeof ( name ), "%ls", wname ); list_for_each_entry ( image, &images, list ) { if ( strcasecmp ( image->name, name ) == 0 ) return image; } return NULL; +} + +/** + * Get length of EFI file + * + * @v file EFI file + * @ret len Length of file + */ +static size_t efi_file_len ( struct efi_file *file ) { + struct efi_file_reader reader; + + /* If this is the root directory, then treat as length zero */ + if ( ! file->read ) + return 0; + + /* Initialise reader */ + reader.file = file; + reader.pos = 0; + reader.data = NULL; + reader.len = 0; + + /* Perform dummy read to determine file length */ + file->read ( &reader ); + + return reader.pos; +} + +/** + * Read chunk of EFI file + * + * @v reader EFI file reader + * @v data Input data, or UNULL to zero-fill + * @v len Length of input data + * @ret len Length of output data + */ +static size_t efi_file_read_chunk ( struct efi_file_reader *reader, + userptr_t data, size_t len ) { + struct efi_file *file = reader->file; + size_t offset; + + /* Calculate offset into input data */ + offset = ( file->pos - reader->pos ); + + /* Consume input data range */ + reader->pos += len; + + /* Calculate output length */ + if ( offset < len ) { + len -= offset; + } else { + len = 0; + } + if ( len > reader->len ) + len = reader->len; + + /* Copy or zero output data */ + if ( data ) { + copy_from_user ( reader->data, data, offset, len ); + } else { + memset ( reader->data, 0, len ); + } + + /* Consume output buffer */ + file->pos += len; + reader->data += len; + reader->len -= len; + + return len; +} + +/** + * Read from image-backed file + * + * @v reader EFI file reader + * @ret len Length read + */ +static size_t efi_file_read_image ( struct efi_file_reader *reader ) { + struct efi_file *file = reader->file; + struct image *image = file->image; + + /* Read from file */ + return efi_file_read_chunk ( reader, image->data, image->len ); +} + +/** + * Open fixed file + * + * @v file EFI file + * @v new New EFI file + * @ret efirc EFI status code + */ +static EFI_STATUS efi_file_open_fixed ( struct efi_file *file, + EFI_FILE_PROTOCOL **new ) { + + /* Increment reference count */ + ref_get ( &file->refcnt ); + + /* Return opened file */ + *new = &file->file; + + DBGC ( file, "EFIFILE %s opened\n", efi_file_name ( file ) ); + return 0; +} +/** + * Associate file with image + * + * @v file EFI file + * @v image Image + */ +static void efi_file_image ( struct efi_file *file, struct image *image ) { + + file->image = image; + file->name = image->name; + file->read = efi_file_read_image; } /** @@ -106,50 +254,56 @@ static struct image * efi_file_find ( const CHAR16 *wname ) { */ static EFI_STATUS EFIAPI efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, - CHAR16 *wname, UINT64 mode __unused, - UINT64 attributes __unused ) { + 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; + + /* Convert name to ASCII */ + snprintf ( buf, sizeof ( buf ), "%ls", wname ); + name = buf; /* Initial '\' indicates opening from the root directory */ - while ( *wname == L'\\' ) { + while ( *name == '\\' ) { file = &efi_file_root; - wname++; + name++; } /* Allow root directory itself to be opened */ - if ( ( wname[0] == L'\0' ) || ( wname[0] == L'.' ) ) { - *new = &efi_file_root.file; - return 0; - } + if ( ( name[0] == '\0' ) || ( name[0] == '.' ) ) + return efi_file_open_fixed ( &efi_file_root, new ); /* Fail unless opening from the root */ - if ( file->image ) { + if ( file != &efi_file_root ) { DBGC ( file, "EFIFILE %s is not a directory\n", efi_file_name ( file ) ); return EFI_NOT_FOUND; } - /* Identify image */ - image = efi_file_find ( wname ); - if ( ! image ) { - DBGC ( file, "EFIFILE \"%ls\" does not exist\n", wname ); - return EFI_NOT_FOUND; - } - /* Fail unless opening read-only */ if ( mode != EFI_FILE_MODE_READ ) { DBGC ( file, "EFIFILE %s cannot be opened in mode %#08llx\n", - image->name, mode ); + name, mode ); return EFI_WRITE_PROTECTED; } + /* Identify image */ + image = efi_file_find ( name ); + if ( ! image ) { + DBGC ( file, "EFIFILE %s does not exist\n", name ); + return EFI_NOT_FOUND; + } + /* 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 ) ); - new_file->image = image_get ( image ); + efi_file_image ( new_file, image_get ( image ) ); *new = &new_file->file; DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) ); @@ -165,14 +319,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, static EFI_STATUS EFIAPI efi_file_close ( EFI_FILE_PROTOCOL *this ) { struct efi_file *file = container_of ( this, struct efi_file, file ); - /* Do nothing if this is the root */ - if ( ! file->image ) - return 0; - /* Close file */ DBGC ( file, "EFIFILE %s closed\n", efi_file_name ( file ) ); - image_put ( file->image ); - free ( file ); + ref_put ( &file->refcnt ); return 0; } @@ -229,30 +378,29 @@ static EFI_STATUS efi_file_varlen ( UINT64 *base, size_t base_len, /** * Return file information structure * - * @v image Image, or NULL for the root directory + * @v file EFI file * @v len Length of data buffer * @v data Data buffer * @ret efirc EFI status code */ -static EFI_STATUS efi_file_info ( struct image *image, UINTN *len, +static EFI_STATUS efi_file_info ( struct efi_file *file, UINTN *len, VOID *data ) { EFI_FILE_INFO info; - const char *name; + size_t file_len; + + /* Get file length */ + file_len = efi_file_len ( file ); /* Populate file information */ memset ( &info, 0, sizeof ( info ) ); - if ( image ) { - info.FileSize = image->len; - info.PhysicalSize = image->len; - info.Attribute = EFI_FILE_READ_ONLY; - name = image->name; - } else { - info.Attribute = ( EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY ); - name = ""; - } - - return efi_file_varlen ( &info.Size, SIZE_OF_EFI_FILE_INFO, name, - len, data ); + info.FileSize = file_len; + info.PhysicalSize = file_len; + info.Attribute = EFI_FILE_READ_ONLY; + if ( file == &efi_file_root ) + info.Attribute |= EFI_FILE_DIRECTORY; + + return efi_file_varlen ( &info.Size, SIZE_OF_EFI_FILE_INFO, + file->name, len, data ); } /** @@ -266,14 +414,16 @@ static EFI_STATUS efi_file_info ( struct image *image, UINTN *len, static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len, VOID *data ) { EFI_STATUS efirc; + struct efi_file entry; struct image *image; unsigned int index; - /* Construct directory entry at current position */ + /* Construct directory entries for image-backed files */ index = file->pos; for_each_image ( image ) { if ( index-- == 0 ) { - efirc = efi_file_info ( image, len, data ); + efi_file_image ( &entry, image ); + efirc = efi_file_info ( &entry, len, data ); if ( efirc == 0 ) file->pos++; return efirc; @@ -296,21 +446,25 @@ static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len, static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this, UINTN *len, VOID *data ) { struct efi_file *file = container_of ( this, struct efi_file, file ); - size_t remaining; + struct efi_file_reader reader; + size_t pos = file->pos; /* If this is the root directory, then construct a directory entry */ - if ( ! file->image ) + if ( ! file->read ) return efi_file_read_dir ( file, len, data ); + /* Initialise reader */ + reader.file = file; + reader.pos = 0; + reader.data = data; + reader.len = *len; + /* Read from the file */ - remaining = ( file->image->len - file->pos ); - if ( *len > remaining ) - *len = remaining; DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n", - efi_file_name ( file ), file->pos, - ( ( size_t ) ( file->pos + *len ) ) ); - copy_from_user ( data, file->image->data, file->pos, *len ); - file->pos += *len; + efi_file_name ( file ), pos, file->pos ); + *len = file->read ( &reader ); + assert ( ( pos + *len ) == file->pos ); + return 0; } @@ -342,24 +496,21 @@ static EFI_STATUS EFIAPI efi_file_write ( EFI_FILE_PROTOCOL *this, static EFI_STATUS EFIAPI efi_file_set_position ( EFI_FILE_PROTOCOL *this, UINT64 position ) { struct efi_file *file = container_of ( this, struct efi_file, file ); + size_t len; - /* If this is the root directory, reset to the start */ - if ( ! file->image ) { - DBGC ( file, "EFIFILE root directory rewound\n" ); - file->pos = 0; - return 0; - } + /* Get file length */ + len = efi_file_len ( file ); /* Check for the magic end-of-file value */ if ( position == 0xffffffffffffffffULL ) - position = file->image->len; + position = len; /* Fail if we attempt to seek past the end of the file (since * we do not support writes). */ - if ( position > file->image->len ) { + if ( position > len ) { DBGC ( file, "EFIFILE %s cannot seek to %#08llx of %#08zx\n", - efi_file_name ( file ), position, file->image->len ); + efi_file_name ( file ), position, len ); return EFI_UNSUPPORTED; } @@ -408,7 +559,7 @@ static EFI_STATUS EFIAPI efi_file_get_info ( EFI_FILE_PROTOCOL *this, /* Get file information */ DBGC ( file, "EFIFILE %s get file information\n", efi_file_name ( file ) ); - return efi_file_info ( file->image, len, data ); + return efi_file_info ( file, len, data ); } else if ( memcmp ( type, &efi_file_system_info_id, sizeof ( *type ) ) == 0 ) { @@ -468,6 +619,7 @@ static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) { /** Root directory */ static struct efi_file efi_file_root = { + .refcnt = REF_INIT ( ref_no_free ), .file = { .Revision = EFI_FILE_PROTOCOL_REVISION, .Open = efi_file_open, @@ -482,6 +634,7 @@ static struct efi_file efi_file_root = { .Flush = efi_file_flush, }, .image = NULL, + .name = "", }; /** @@ -496,8 +649,7 @@ efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused, EFI_FILE_PROTOCOL **file ) { DBGC ( &efi_file_root, "EFIFILE open volume\n" ); - *file = &efi_file_root.file; - return 0; + return efi_file_open_fixed ( &efi_file_root, file ); } /** EFI simple file system protocol */ -- cgit v1.2.3-55-g7522 From e5f02551735922eb235388bff08249a6f31ded3d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 21 May 2021 14:27:27 +0100 Subject: [efi] Provide an "initrd.magic" file for use by UEFI kernels Provide a file "initrd.magic" via the EFI_SIMPLE_FILE_SYSTEM_PROTOCOL that contains the initrd file as constructed for BIOS bzImage kernels (including injected files with CPIO headers constructed by iPXE). This allows BIOS and UEFI kernels to obtain the exact same initramfs image, by adding "initrd=initrd.magic" to the kernel command line. For example: #!ipxe kernel boot/vmlinuz initrd=initrd.magic initrd boot/initrd.img initrd boot/modules/e1000.ko /lib/modules/e1000.ko initrd boot/modules/af_packet.ko /lib/modules/af_packet.ko boot Do not include the "initrd.magic" file within the root directory listing, since doing so would break software such as wimboot that processes all files within the root directory. Signed-off-by: Michael Brown --- src/interface/efi/efi_file.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index c67fdea54..fc64b369c 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -38,6 +38,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ struct efi_file { }; static struct efi_file efi_file_root; +static struct efi_file efi_file_initrd; /** * Free EFI file @@ -209,6 +211,67 @@ static size_t efi_file_read_image ( struct efi_file_reader *reader ) { return efi_file_read_chunk ( reader, image->data, image->len ); } +/** + * Read from magic initrd file + * + * @v reader EFI file reader + * @ret len Length read + */ +static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { + struct efi_file *file = reader->file; + struct cpio_header cpio; + struct image *image; + const char *name; + size_t pad_len; + size_t cpio_len; + size_t name_len; + size_t len; + + /* Read from file */ + len = 0; + for_each_image ( image ) { + + /* Ignore currently executing image */ + if ( image == current_image ) + continue; + + /* Pad to alignment boundary */ + pad_len = ( ( -reader->pos ) & ( INITRD_ALIGN - 1 ) ); + if ( pad_len ) { + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) pad\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + pad_len ) ); + } + len += efi_file_read_chunk ( reader, UNULL, pad_len ); + + /* Read CPIO header, if applicable */ + cpio_len = cpio_header ( image, &cpio ); + if ( cpio_len ) { + name = cpio_name ( image ); + name_len = cpio_name_len ( image ); + pad_len = ( cpio_len - sizeof ( cpio ) - name_len ); + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + cpio_len ), image->name ); + len += efi_file_read_chunk ( reader, + virt_to_user ( &cpio ), + sizeof ( cpio ) ); + len += efi_file_read_chunk ( reader, + virt_to_user ( name ), + name_len ); + len += efi_file_read_chunk ( reader, UNULL, pad_len ); + } + + /* Read file data */ + DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s\n", + efi_file_name ( file ), reader->pos, + ( reader->pos + image->len ), image->name ); + len += efi_file_read_chunk ( reader, image->data, image->len ); + } + + return len; +} + /** * Open fixed file * @@ -289,6 +352,10 @@ 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 ); + /* Identify image */ image = efi_file_find ( name ); if ( ! image ) { @@ -637,6 +704,27 @@ static struct efi_file efi_file_root = { .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, + }, + .image = NULL, + .name = "initrd.magic", + .read = efi_file_read_initrd, +}; + /** * Open root directory * -- cgit v1.2.3-55-g7522 From 0d68d71519153407670c72bc7520d2de2d6841e3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 May 2021 15:26:07 +0100 Subject: [build] Use SOURCE_DATE_EPOCH for .iso timestamps if it exists Originally-implemented-by: Bernhard M. Wiedemann Signed-off-by: Michael Brown --- src/util/genfsimg | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/util/genfsimg b/src/util/genfsimg index 1a0517cc9..3c8b0c898 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -240,6 +240,16 @@ if [ -n "${ISOIMG}" ] ; then else FATIMG= fi + if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then + DATE_FMT="+%Y%m%d%H%M%S00" + BUILD_DATE=$(date -u -d "@${SOURCE_DATE_EPOCH}" "${DATE_FMT}" \ + 2>/dev/null || \ + date -u -r "${SOURCE_DATE_EPOCH}" "${DATE_FMT}" \ + 2>/dev/null || \ + date -u "${DATE_FMT}") + ISOARGS="${ISOARGS} --set_all_file_dates ${BUILD_DATE}" + ISOARGS="${ISOARGS} --modification-date=${BUILD_DATE}" + fi fi # Create FAT filesystem image, if applicable -- cgit v1.2.3-55-g7522 From 74c54461cbb67e5ba20fc8119982a09121221853 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 24 May 2021 15:38:54 +0100 Subject: [build] Use SOURCE_DATE_EPOCH for isohybrid MBR ID if it exists Signed-off-by: Michael Brown --- src/util/genfsimg | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/util/genfsimg b/src/util/genfsimg index 3c8b0c898..1635a11ac 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -307,7 +307,11 @@ if [ -n "${ISOIMG}" ] ; then -publisher "ipxe.org" -sysid "iPXE" -o "${ISOIMG}" \ ${ISOARGS} "${ISODIR}" if isohybrid --version >/dev/null 2>&1 ; then - isohybrid "${ISOIMG}" + ISOHYBRIDARGS= + if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then + ISOHYBRIDARGS="${ISOHYBRIDARGS} --id ${SOURCE_DATE_EPOCH}" + fi + isohybrid ${ISOHYBRIDARGS} "${ISOIMG}" fi fi -- cgit v1.2.3-55-g7522 From f3f568e382a5f19824b3bfc6081cde39eee661e8 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 3 Jun 2021 13:34:14 +0100 Subject: [crypto] Add memory output constraints for big-integer inline assembly The ARM versions of the big-integer inline assembly functions include constraints to indicate that the output value is modified by the assembly code. These constraints are not present in the equivalent code for the x86 versions. As of GCC 11, this results in the compiler reporting that the output values may be uninitialized. Fix by including the relevant memory output constraints. Reported-by: Christian Hesse Signed-off-by: Michael Brown --- src/arch/x86/core/x86_bigint.c | 11 ++++---- src/arch/x86/include/bits/bigint.h | 54 ++++++++++++++++++++++++++------------ 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/arch/x86/core/x86_bigint.c b/src/arch/x86/core/x86_bigint.c index 6413b2fa8..9a25bdad5 100644 --- a/src/arch/x86/core/x86_bigint.c +++ b/src/arch/x86/core/x86_bigint.c @@ -75,17 +75,18 @@ void bigint_multiply_raw ( const uint32_t *multiplicand0, * * a < 2^{n}, b < 2^{n} => ab < 2^{2n} */ - __asm__ __volatile__ ( "mull %4\n\t" - "addl %%eax, (%5,%2,4)\n\t" - "adcl %%edx, 4(%5,%2,4)\n\t" + __asm__ __volatile__ ( "mull %5\n\t" + "addl %%eax, (%6,%2,4)\n\t" + "adcl %%edx, 4(%6,%2,4)\n\t" "\n1:\n\t" - "adcl $0, 8(%5,%2,4)\n\t" + "adcl $0, 8(%6,%2,4)\n\t" "inc %2\n\t" /* Does not affect CF */ "jc 1b\n\t" : "=&a" ( discard_a ), "=&d" ( discard_d ), - "=&r" ( index ) + "=&r" ( index ), + "+m" ( *result ) : "0" ( multiplicand_element ), "g" ( multiplier_element ), "r" ( result_elements ), diff --git a/src/arch/x86/include/bits/bigint.h b/src/arch/x86/include/bits/bigint.h index 4f1bc87ff..7443d6fdc 100644 --- a/src/arch/x86/include/bits/bigint.h +++ b/src/arch/x86/include/bits/bigint.h @@ -25,19 +25,22 @@ typedef uint32_t bigint_element_t; static inline __attribute__ (( always_inline )) void bigint_init_raw ( uint32_t *value0, unsigned int size, const void *data, size_t len ) { - long pad_len = ( sizeof ( bigint_t ( size ) ) - len ); + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); + long pad_len = ( sizeof ( *value ) - len ); void *discard_D; long discard_c; /* Copy raw data in reverse order, padding with zeros */ __asm__ __volatile__ ( "\n1:\n\t" - "movb -1(%2,%1), %%al\n\t" + "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" - "mov %3, %1\n\t" + "mov %4, %1\n\t" "rep stosb\n\t" - : "=&D" ( discard_D ), "=&c" ( discard_c ) + : "=&D" ( discard_D ), "=&c" ( discard_c ), + "+m" ( *value ) : "r" ( data ), "g" ( pad_len ), "0" ( value0 ), "1" ( len ) : "eax" ); @@ -53,6 +56,8 @@ bigint_init_raw ( uint32_t *value0, unsigned int size, static inline __attribute__ (( always_inline )) void bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; void *discard_S; long discard_c; @@ -60,11 +65,11 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" "lodsl\n\t" - "adcl %%eax, (%3,%0,4)\n\t" + "adcl %%eax, (%4,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" : "=&r" ( index ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "1" ( addend0 ), "2" ( size ) : "eax" ); } @@ -79,6 +84,8 @@ bigint_add_raw ( const uint32_t *addend0, uint32_t *value0, static inline __attribute__ (( always_inline )) void bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; void *discard_S; long discard_c; @@ -86,11 +93,11 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" "lodsl\n\t" - "sbbl %%eax, (%3,%0,4)\n\t" + "sbbl %%eax, (%4,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" : "=&r" ( index ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "1" ( subtrahend0 ), "2" ( size ) : "eax" ); @@ -104,15 +111,18 @@ bigint_subtract_raw ( const uint32_t *subtrahend0, uint32_t *value0, */ static inline __attribute__ (( always_inline )) void bigint_rol_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long index; long discard_c; __asm__ __volatile__ ( "xor %0, %0\n\t" /* Zero %0 and clear CF */ "\n1:\n\t" - "rcll $1, (%2,%0,4)\n\t" + "rcll $1, (%3,%0,4)\n\t" "inc %0\n\t" /* Does not affect CF */ "loop 1b\n\t" - : "=&r" ( index ), "=&c" ( discard_c ) + : "=&r" ( index ), "=&c" ( discard_c ), + "+m" ( *value ) : "r" ( value0 ), "1" ( size ) ); } @@ -124,13 +134,15 @@ bigint_rol_raw ( uint32_t *value0, unsigned int size ) { */ static inline __attribute__ (( always_inline )) void bigint_ror_raw ( uint32_t *value0, unsigned int size ) { + bigint_t ( size ) __attribute__ (( may_alias )) *value = + ( ( void * ) value0 ); long discard_c; __asm__ __volatile__ ( "clc\n\t" "\n1:\n\t" - "rcrl $1, -4(%1,%0,4)\n\t" + "rcrl $1, -4(%2,%0,4)\n\t" "loop 1b\n\t" - : "=&c" ( discard_c ) + : "=&c" ( discard_c ), "+m" ( *value ) : "r" ( value0 ), "0" ( size ) ); } @@ -239,6 +251,8 @@ bigint_max_set_bit_raw ( const uint32_t *value0, unsigned int size ) { static inline __attribute__ (( always_inline )) void bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, uint32_t *dest0, unsigned int dest_size ) { + bigint_t ( dest_size ) __attribute__ (( may_alias )) *dest = + ( ( void * ) dest0 ); long pad_size = ( dest_size - source_size ); void *discard_D; void *discard_S; @@ -246,10 +260,10 @@ bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, __asm__ __volatile__ ( "rep movsl\n\t" "xorl %%eax, %%eax\n\t" - "mov %3, %2\n\t" + "mov %4, %2\n\t" "rep stosl\n\t" : "=&D" ( discard_D ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *dest ) : "g" ( pad_size ), "0" ( dest0 ), "1" ( source0 ), "2" ( source_size ) : "eax" ); @@ -266,13 +280,15 @@ bigint_grow_raw ( const uint32_t *source0, unsigned int source_size, static inline __attribute__ (( always_inline )) void bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, uint32_t *dest0, unsigned int dest_size ) { + bigint_t ( dest_size ) __attribute__ (( may_alias )) *dest = + ( ( void * ) dest0 ); void *discard_D; void *discard_S; long discard_c; __asm__ __volatile__ ( "rep movsl\n\t" : "=&D" ( discard_D ), "=&S" ( discard_S ), - "=&c" ( discard_c ) + "=&c" ( discard_c ), "+m" ( *dest ) : "0" ( dest0 ), "1" ( source0 ), "2" ( dest_size ) : "eax" ); @@ -289,15 +305,19 @@ bigint_shrink_raw ( const uint32_t *source0, unsigned int source_size __unused, static inline __attribute__ (( always_inline )) void bigint_done_raw ( const uint32_t *value0, unsigned int size __unused, void *out, size_t len ) { + struct { + uint8_t bytes[len]; + } __attribute__ (( may_alias )) *out_bytes = out; void *discard_D; long discard_c; /* Copy raw data in reverse order */ __asm__ __volatile__ ( "\n1:\n\t" - "movb -1(%2,%1), %%al\n\t" + "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" - : "=&D" ( discard_D ), "=&c" ( discard_c ) + : "=&D" ( discard_D ), "=&c" ( discard_c ), + "+m" ( *out_bytes ) : "r" ( value0 ), "0" ( out ), "1" ( len ) : "eax" ); } -- cgit v1.2.3-55-g7522 From 065dce8d5950938a7250a93c2630c4b53d2e8293 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 4 Jun 2021 14:16:44 +0100 Subject: [ath5k] Avoid returning uninitialised data on EEPROM read errors Originally-implemented-by: Bernhard M. Wiedemann Signed-off-by: Michael Brown --- src/drivers/net/ath/ath5k/ath5k_eeprom.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/drivers/net/ath/ath5k/ath5k_eeprom.c b/src/drivers/net/ath/ath5k/ath5k_eeprom.c index 12519bc59..46f33d1e8 100644 --- a/src/drivers/net/ath/ath5k/ath5k_eeprom.c +++ b/src/drivers/net/ath/ath5k/ath5k_eeprom.c @@ -39,6 +39,9 @@ static int ath5k_hw_eeprom_read(struct ath5k_hw *ah, u32 offset, u16 *data) { u32 status, timeout; + /* Avoid returning uninitialised data on error */ + *data = 0xffff; + /* * Initialize EEPROM access */ -- cgit v1.2.3-55-g7522 From 92807f5759e0207aaa909ee0e1429478f210aff1 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Jun 2021 12:01:10 +0100 Subject: [rndis] Fix size of reserved fields Most RNDIS data structures include a trailing 4-byte reserved field. For the REMOTE_NDIS_PACKET_MSG and REMOTE_NDIS_INITIALIZE_CMPLT structures, this is an 8-byte field instead. iPXE currently uses incorrect structure definitions with a 4-byte reserved field in all data structures, resulting in data payloads that overlap the last 4 bytes of the 8-byte reserved field. RNDIS uses explicit offsets to locate any data payloads beyond the message header, and so liberal RNDIS parsers (such as those used in Hyper-V and in the Linux USB Ethernet gadget driver) are still able to parse the malformed structures. A stricter RNDIS parser (such as that found in some older Android builds that seem to use an out-of-tree USB Ethernet gadget driver) may reject the malformed structures since the data payload offset is less than the header length, causing iPXE to be unable to transmit packets. Fix by correcting the length of the reserved fields. Debugged-by: Martin Nield Signed-off-by: Michael Brown --- src/include/ipxe/rndis.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/ipxe/rndis.h b/src/include/ipxe/rndis.h index bcb6d8e6a..e8ece1e85 100644 --- a/src/include/ipxe/rndis.h +++ b/src/include/ipxe/rndis.h @@ -84,7 +84,7 @@ struct rndis_initialise_completion { /** Packet alignment factor */ uint32_t align; /** Reserved */ - uint32_t reserved; + uint32_t reserved[2]; } __attribute__ (( packed )); /** RNDIS halt message */ @@ -237,7 +237,7 @@ struct rndis_packet_message { /** Per-packet information record */ struct rndis_packet_field ppi; /** Reserved */ - uint32_t reserved; + uint32_t reserved[2]; } __attribute__ (( packed )); /** RNDIS packet record */ -- cgit v1.2.3-55-g7522 From 52300ccf98f94d48dbde84626097c7c22cfdf867 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Jun 2021 13:18:19 +0100 Subject: [base64] Include terminating NUL within base64 character array Reported-by: Bernhard M. Wiedemann Signed-off-by: Michael Brown --- src/core/base64.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/base64.c b/src/core/base64.c index e452f7d41..ec11be261 100644 --- a/src/core/base64.c +++ b/src/core/base64.c @@ -36,7 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -static const char base64[64] = +static const char base64[ 64 + 1 /* NUL */ ] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; /** -- cgit v1.2.3-55-g7522 From 3dd1989ac0c0b699adcfe5c1c0efa2b229fd1e86 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 7 Jun 2021 13:26:01 +0100 Subject: [libc] Match standard prototype for putchar() Reported-by: Bernhard M. Wiedemann Signed-off-by: Michael Brown --- src/core/console.c | 5 ++++- src/include/stdio.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/console.c b/src/core/console.c index 7fd00036f..2b90809bf 100644 --- a/src/core/console.c +++ b/src/core/console.c @@ -20,11 +20,12 @@ unsigned int console_height = CONSOLE_DEFAULT_HEIGHT; * Write a single character to each console device * * @v character Character to be written + * @ret character Character written * * The character is written out to all enabled console devices, using * each device's console_driver::putchar() method. */ -void putchar ( int character ) { +int putchar ( int character ) { struct console_driver *console; /* Automatic LF -> CR,LF translation */ @@ -37,6 +38,8 @@ void putchar ( int character ) { console->putchar ) console->putchar ( character ); } + + return character; } /** diff --git a/src/include/stdio.h b/src/include/stdio.h index a618482ce..ac17da83d 100644 --- a/src/include/stdio.h +++ b/src/include/stdio.h @@ -6,7 +6,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -extern void putchar ( int character ); +extern int putchar ( int character ); extern int getchar ( void ); -- cgit v1.2.3-55-g7522 From 3c040ad387099483102708bb1839110bc788cefb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 11 Jun 2021 14:46:54 +0100 Subject: [efi] Veto the Itautec Ip4ConfigDxe driver The Ip4ConfigDxe driver bug that was observed on Dell systems in commit 64b4452 ("[efi] Blacklist the Dell Ip4ConfigDxe driver") has also been observed on systems with a manufacturer name of "Itautec S.A.". The symptoms of the bug are identical: an attempt to call DisconnectController() on the LOM device handle will lock up the system. Fix by extending the veto to cover the Ip4ConfigDxe driver for this manufacturer. Debugged-by: Celso Viana Signed-off-by: Michael Brown --- src/interface/efi/efi_veto.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index 6ff7898e9..b616539d3 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -362,7 +362,7 @@ static int efi_veto_driver ( EFI_HANDLE driver ) { } /** - * Veto Dell Ip4ConfigDxe driver + * Veto Ip4ConfigDxe driver on some platforms * * @v binding Driver binding protocol * @v loaded Loaded image protocol @@ -372,19 +372,21 @@ static int efi_veto_driver ( EFI_HANDLE driver ) { * @ret vetoed Driver is to be vetoed */ static int -efi_veto_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, - EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, - const char *manufacturer, const CHAR16 *name ) { +efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, + const char *manufacturer, const CHAR16 *name ) { static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; static const char *dell = "Dell Inc."; + static const char *itautec = "Itautec S.A."; /* Check manufacturer and driver name */ if ( ! manufacturer ) return 0; if ( ! name ) return 0; - if ( strcmp ( manufacturer, dell ) != 0 ) + if ( ( strcmp ( manufacturer, dell ) != 0 ) && + ( strcmp ( manufacturer, itautec ) != 0 ) ) return 0; if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) return 0; @@ -436,8 +438,8 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, /** Driver vetoes */ static struct efi_veto efi_vetoes[] = { { - .name = "Dell Ip4Config", - .veto = efi_veto_dell_ip4config, + .name = "Ip4Config", + .veto = efi_veto_ip4config, }, { .name = "HP Xhci", -- cgit v1.2.3-55-g7522 From bf4ccd4265ac614fbfa38bf168b6eeaf4c17d51e Mon Sep 17 00:00:00 2001 From: Bernhard M. Wiedemann Date: Sat, 12 Jun 2021 21:55:43 +0200 Subject: [build] Ensure build ID is deterministic Commit 040cdd0 ("[linux] Add a prefix to all symbols to avoid future name collisions") unintentionally reintroduced an element of non-determinism into the build ID, by omitting the -D option when manipulating the blib.a archive. Fix by adding the -D option to restore determinism. Reworded-by: Michael Brown Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index e017c656b..9be7e7e13 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1167,7 +1167,7 @@ $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) $(Q)$(RM) $(BLIB) $(QM)$(ECHO) " [AR] $@" $(Q)$(AR) rD $@ $(sort $(BLIB_OBJS)) - $(Q)$(OBJCOPY) --prefix-symbols=$(SYMBOL_PREFIX) $@ + $(Q)$(OBJCOPY) -D --prefix-symbols=$(SYMBOL_PREFIX) $@ $(Q)$(RANLIB) -D $@ blib : $(BLIB) -- cgit v1.2.3-55-g7522 From 51c88a4a62116d77a854b55100c6955d6f1eb6dc Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Jun 2021 14:32:23 +0100 Subject: [build] Fix building on broken versions of GNU binutils Some versions of GNU objcopy (observed with binutils 2.23.52.0.1 on CentOS 7.0.1406) document the -D/--enable-deterministic-archives option but fail to recognise the short form of the option. Work around this problem by using the long form of the option. Reported-by: Olaf Hering Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 9be7e7e13..0deb15c10 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1167,7 +1167,8 @@ $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) $(Q)$(RM) $(BLIB) $(QM)$(ECHO) " [AR] $@" $(Q)$(AR) rD $@ $(sort $(BLIB_OBJS)) - $(Q)$(OBJCOPY) -D --prefix-symbols=$(SYMBOL_PREFIX) $@ + $(Q)$(OBJCOPY) --enable-deterministic-archives \ + --prefix-symbols=$(SYMBOL_PREFIX) $@ $(Q)$(RANLIB) -D $@ blib : $(BLIB) -- cgit v1.2.3-55-g7522 From 9b6ad2d888c8550af1edfb324853f8fa2e596640 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 17 Jun 2021 15:29:22 +0100 Subject: [peerdist] Assume that most recently discovered peer can be reused The peer discovery time has a significant impact on the overall PeerDist download speed, since each block requires an individual discovery attempt. In most cases, a peer that responds for block N will turn out to also respond for block N+1. Assume that the most recently discovered peer (for any block) probably has a copy of the next block to be discovered, thereby allowing the peer download attempt to begin immediately. In the case that this assumption is incorrect, the existing error recovery path will allow for fallback to newly discovered peers (or to the origin server). Suggested-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/net/peerdisc.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/net/peerdisc.c b/src/net/peerdisc.c index 55e3f7fa7..d7e0d2989 100644 --- a/src/net/peerdisc.c +++ b/src/net/peerdisc.c @@ -73,6 +73,9 @@ static LIST_HEAD ( peerdisc_segments ); */ unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; +/** Most recently discovered peer (for any block) */ +static char *peerdisc_recent; + /** Hosted cache server */ static char *peerhost; @@ -383,6 +386,7 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment, struct peerdisc_peer *peer; struct peerdisc_client *peerdisc; struct peerdisc_client *tmp; + char *recent; /* Ignore duplicate peers */ list_for_each_entry ( peer, &segment->peers, list ) { @@ -403,6 +407,15 @@ static int peerdisc_discovered ( struct peerdisc_segment *segment, /* Add to end of list of peers */ list_add_tail ( &peer->list, &segment->peers ); + /* Record as most recently discovered peer */ + if ( location != peerdisc_recent ) { + recent = strdup ( location ); + if ( recent ) { + free ( peerdisc_recent ); + peerdisc_recent = recent; + } + } + /* Notify all clients */ list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list ) peerdisc->op->discovered ( peerdisc ); @@ -484,6 +497,16 @@ static struct peerdisc_segment * peerdisc_create ( const char *id ) { } else { + /* Add most recently discovered peer to list of peers + * + * This is a performance optimisation: we assume that + * the most recently discovered peer for any block has + * a high probability of also having a copy of the + * next block that we attempt to discover. + */ + if ( peerdisc_recent ) + peerdisc_discovered ( segment, peerdisc_recent ); + /* Start discovery timer */ start_timer_nodelay ( &segment->timer ); DBGC2 ( segment, "PEERDISC %p discovering %s\n", -- cgit v1.2.3-55-g7522 From 0688114ea63fee21c39d94cc8df9e2a5003ac283 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Jun 2021 10:22:38 +0100 Subject: [cloud] Show ifstat output after a failed boot attempt Signed-off-by: Michael Brown --- src/config/cloud/aws.ipxe | 3 ++- src/config/cloud/gce.ipxe | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe index 2c96e3888..66ad77d98 100644 --- a/src/config/cloud/aws.ipxe +++ b/src/config/cloud/aws.ipxe @@ -5,4 +5,5 @@ echo CPU: ${cpuvendor} ${cpumodel} ifstat || dhcp || route || -chain -ar http://169.254.169.254/latest/user-data +chain -ar http://169.254.169.254/latest/user-data || +ifstat || diff --git a/src/config/cloud/gce.ipxe b/src/config/cloud/gce.ipxe index 88e12b56b..65e2e57dc 100644 --- a/src/config/cloud/gce.ipxe +++ b/src/config/cloud/gce.ipxe @@ -5,4 +5,5 @@ echo CPU: ${cpuvendor} ${cpumodel} ifstat || dhcp || route || -chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot +chain -ar http://metadata.google.internal/computeMetadata/v1/instance/attributes/ipxeboot || +ifstat || -- cgit v1.2.3-55-g7522 From 5622575c5e4bffb07307f39903bb95caad7d172d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 22 Jun 2021 14:48:10 +0100 Subject: [realtek] Work around hardware bug on RTL8211B The RTL8211B seems to have a bug that prevents the link from coming up unless the MII_MMD_DATA register is cleared. The Linux kernel driver applies this workaround (in rtl8211b_resume()) only to the specific RTL8211B PHY model, along with a matching workaround to set bit 9 of MII_MMD_DATA when suspending the PHY. Since we have no need to ever suspend the PHY, and since writing a zero ought to be harmless, we just clear the register unconditionally. Debugged-by: Nikolay Pertsev Tested-by: Nikolay Pertsev Signed-off-by: Michael Brown --- src/drivers/net/realtek.c | 10 ++++++++++ src/include/mii.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c index 0af3416d5..a43efb68b 100644 --- a/src/drivers/net/realtek.c +++ b/src/drivers/net/realtek.c @@ -420,6 +420,16 @@ static int realtek_phy_reset ( struct realtek_nic *rtl ) { */ } + /* Some cards (e.g. RTL8211B) have a hardware errata that + * requires the MII_MMD_DATA register to be cleared before the + * link will come up. + */ + if ( ( rc = mii_write ( &rtl->mii, MII_MMD_DATA, 0 ) ) != 0 ) { + /* Ignore failures, since the register may not be + * present on all PHYs. + */ + } + /* Restart autonegotiation */ if ( ( rc = mii_restart ( &rtl->mii ) ) != 0 ) { DBGC ( rtl, "REALTEK %p could not restart MII: %s\n", diff --git a/src/include/mii.h b/src/include/mii.h index e2afef854..515ba224d 100644 --- a/src/include/mii.h +++ b/src/include/mii.h @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_ONLY ); #define MII_EXPANSION 0x06 /* Expansion register */ #define MII_CTRL1000 0x09 /* 1000BASE-T control */ #define MII_STAT1000 0x0a /* 1000BASE-T status */ +#define MII_MMD_CTRL 0x0d /* MMD Access Control Register */ +#define MII_MMD_DATA 0x0e /* MMD Access Data Register */ #define MII_ESTATUS 0x0f /* Extended Status */ #define MII_DCOUNTER 0x12 /* Disconnect counter */ #define MII_FCSCOUNTER 0x13 /* False carrier counter */ -- cgit v1.2.3-55-g7522 From 4aa03758210d861dacb0531062576287ac76e353 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 Jun 2021 12:32:19 +0100 Subject: [rdc] Add driver for RDC R6040 embedded NIC Signed-off-by: Michael Brown --- src/drivers/net/rdc.c | 694 +++++++++++++++++++++++++++++++++++++++++++++ src/drivers/net/rdc.h | 194 +++++++++++++ src/include/ipxe/errfile.h | 1 + 3 files changed, 889 insertions(+) create mode 100644 src/drivers/net/rdc.c create mode 100644 src/drivers/net/rdc.h diff --git a/src/drivers/net/rdc.c b/src/drivers/net/rdc.c new file mode 100644 index 000000000..c3239c002 --- /dev/null +++ b/src/drivers/net/rdc.c @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rdc.h" + +/** @file + * + * RDC R6040 network driver + * + */ + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v rdc RDC device + * @ret rc Return status code + */ +static int rdc_reset ( struct rdc_nic *rdc ) { + unsigned int i; + + /* Reset NIC */ + writew ( RDC_MCR1_RST, rdc->regs + RDC_MCR1 ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < RDC_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check for reset completion */ + if ( readw ( rdc->regs + RDC_MCR1 ) & RDC_MCR1_RST ) { + mdelay ( 1 ); + continue; + } + + /* Reset internal state machine */ + writew ( RDC_MACSM_RST, rdc->regs + RDC_MACSM ); + writew ( 0, rdc->regs + RDC_MACSM ); + mdelay ( RDC_MACSM_RESET_DELAY_MS ); + + return 0; + } + + DBGC ( rdc, "RDC %p timed out waiting for reset\n", rdc ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * MII interface + * + ****************************************************************************** + */ + +/** + * Read from MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @ret value Data read, or negative error + */ +static int rdc_mii_read ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg ) { + struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio ); + uint16_t mmdio; + unsigned int i; + + /* Initiate read */ + mmdio = ( RDC_MMDIO_MIIRD | RDC_MMDIO_PHYAD ( phy ) | + RDC_MMDIO_REGAD ( reg ) ); + writew ( mmdio, rdc->regs + RDC_MMDIO ); + + /* Wait for read to complete */ + for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) { + + /* Check for read completion */ + if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIRD ) { + udelay ( 1 ); + continue; + } + + /* Return register value */ + return ( readw ( rdc->regs + RDC_MMRD ) ); + } + + DBGC ( rdc, "RDC %p timed out waiting for MII read\n", rdc ); + return -ETIMEDOUT; +} + +/** + * Write to MII register + * + * @v mdio MII interface + * @v phy PHY address + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int rdc_mii_write ( struct mii_interface *mdio, unsigned int phy, + unsigned int reg, unsigned int data ) { + struct rdc_nic *rdc = container_of ( mdio, struct rdc_nic, mdio ); + uint16_t mmdio; + unsigned int i; + + /* Initiate write */ + mmdio = ( RDC_MMDIO_MIIWR | RDC_MMDIO_PHYAD ( phy ) | + RDC_MMDIO_REGAD ( reg ) ); + writew ( data, rdc->regs + RDC_MMWD ); + writew ( mmdio, rdc->regs + RDC_MMDIO ); + + /* Wait for write to complete */ + for ( i = 0 ; i < RDC_MII_MAX_WAIT_US ; i++ ) { + + /* Check for write completion */ + if ( readw ( rdc->regs + RDC_MMDIO ) & RDC_MMDIO_MIIWR ) { + udelay ( 1 ); + continue; + } + + return 0; + } + + DBGC ( rdc, "RDC %p timed out waiting for MII write\n", rdc ); + return -ETIMEDOUT; +} + +/** RDC MII operations */ +static struct mii_operations rdc_mii_operations = { + .read = rdc_mii_read, + .write = rdc_mii_write, +}; + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Initialise PHY + * + * @v rdc RDC device + * @ret rc Return status code + */ +static int rdc_init_phy ( struct rdc_nic *rdc ) { + int rc; + + /* Find PHY address */ + if ( ( rc = mii_find ( &rdc->mii ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not find PHY address: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + /* Reset PHY */ + if ( ( rc = mii_reset ( &rdc->mii ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not reset PHY: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Check link state + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rdc_check_link ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + int rc; + + /* Check link state */ + if ( ( rc = mii_check_link ( &rdc->mii, netdev ) ) != 0 ) { + DBGC ( rdc, "RDC %p could not check link: %s\n", + rdc, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Create descriptor ring + * + * @v rdc RDC device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int rdc_create_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + struct rdc_descriptor *desc; + struct rdc_descriptor *next; + physaddr_t start; + unsigned int i; + + /* Allocate descriptor ring */ + ring->desc = dma_alloc ( rdc->dma, &ring->map, len, len ); + if ( ! ring->desc ) + return -ENOMEM; + + /* Initialise descriptor ring */ + memset ( ring->desc, 0, len ); + for ( i = 0 ; i < ring->count ; i++ ) { + desc = &ring->desc[i]; + next = &ring->desc[ ( i + 1 ) & ( ring->count - 1 ) ]; + desc->next = cpu_to_le32 ( dma ( &ring->map, next ) ); + } + + /* Program ring address */ + start = dma ( &ring->map, ring->desc ); + writew ( ( start >> 0 ), ( rdc->regs + ring->reg + RDC_MxDSA_LO ) ); + writew ( ( start >> 16 ), ( rdc->regs + ring->reg + RDC_MxDSA_HI ) ); + + DBGC ( rdc, "RDC %p ring %#02x is at [%08lx,%08lx)\n", + rdc, ring->reg, virt_to_phys ( ring->desc ), + ( virt_to_phys ( ring->desc ) + len ) ); + return 0; +} + +/** + * Destroy descriptor ring + * + * @v rdc RDC device + * @v ring Descriptor ring + */ +static void rdc_destroy_ring ( struct rdc_nic *rdc, struct rdc_ring *ring ) { + size_t len = ( ring->count * sizeof ( ring->desc[0] ) ); + + /* Clear ring address */ + writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_LO ) ); + writew ( 0, ( rdc->regs + ring->reg + RDC_MxDSA_HI ) ); + + /* Free descriptors */ + dma_free ( &ring->map, ring->desc, len ); + ring->desc = NULL; + + /* Reset ring */ + ring->prod = 0; + ring->cons = 0; +} + +/** + * Refill receive descriptor ring + * + * @v rdc RDC device + */ +static void rdc_refill_rx ( struct rdc_nic *rdc ) { + struct rdc_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + + /* Refill ring */ + while ( ( rdc->rx.prod - rdc->rx.cons ) < RDC_NUM_RX_DESC ) { + + /* Allocate I/O buffer */ + iobuf = alloc_rx_iob ( RDC_RX_MAX_LEN, rdc->dma ); + if ( ! iobuf ) { + /* Wait for next refill */ + break; + } + + /* Get next receive descriptor */ + rx_idx = ( rdc->rx.prod++ % RDC_NUM_RX_DESC ); + rx = &rdc->rx.desc[rx_idx]; + + /* Populate receive descriptor */ + rx->len = cpu_to_le16 ( RDC_RX_MAX_LEN ); + rx->addr = cpu_to_le32 ( iob_dma ( iobuf ) ); + wmb(); + rx->flags = cpu_to_le16 ( RDC_FL_OWNED ); + + /* Record I/O buffer */ + assert ( rdc->rx_iobuf[rx_idx] == NULL ); + rdc->rx_iobuf[rx_idx] = iobuf; + + DBGC2 ( rdc, "RDC %p RX %d is [%lx,%lx)\n", + rdc, rx_idx, virt_to_phys ( iobuf->data ), + ( virt_to_phys ( iobuf->data ) + RDC_RX_MAX_LEN ) ); + } +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rdc_open ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + int rc; + + /* Create transmit descriptor ring */ + if ( ( rc = rdc_create_ring ( rdc, &rdc->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = rdc_create_ring ( rdc, &rdc->rx ) ) != 0 ) + goto err_create_rx; + + /* Program receive buffer length */ + writew ( RDC_RX_MAX_LEN, rdc->regs + RDC_MRBSR ); + + /* Enable transmit and receive */ + writew ( ( RDC_MCR0_FD | RDC_MCR0_TXEN | RDC_MCR0_PROMISC | + RDC_MCR0_RXEN ), + rdc->regs + RDC_MCR0 ); + + /* Enable PHY status polling */ + writew ( ( RDC_MPSCCR_EN | RDC_MPSCCR_PHYAD ( rdc->mii.address ) | + RDC_MPSCCR_SLOW ), + rdc->regs + RDC_MPSCCR ); + + /* Fill receive ring */ + rdc_refill_rx ( rdc ); + + /* Update link state */ + rdc_check_link ( netdev ); + + return 0; + + rdc_destroy_ring ( rdc, &rdc->rx ); + err_create_rx: + rdc_destroy_ring ( rdc, &rdc->tx ); + err_create_tx: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rdc_close ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + unsigned int i; + + /* Disable NIC */ + writew ( 0, rdc->regs + RDC_MCR0 ); + + /* Destroy receive descriptor ring */ + rdc_destroy_ring ( rdc, &rdc->rx ); + + /* Discard any unused receive buffers */ + for ( i = 0 ; i < RDC_NUM_RX_DESC ; i++ ) { + if ( rdc->rx_iobuf[i] ) + free_rx_iob ( rdc->rx_iobuf[i] ); + rdc->rx_iobuf[i] = NULL; + } + + /* Destroy transmit descriptor ring */ + rdc_destroy_ring ( rdc, &rdc->tx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rdc_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *tx; + unsigned int tx_idx; + int rc; + + /* Get next transmit descriptor */ + if ( ( rdc->tx.prod - rdc->tx.cons ) >= RDC_NUM_TX_DESC ) { + DBGC ( rdc, "RDC %p out of transmit descriptors\n", rdc ); + return -ENOBUFS; + } + tx_idx = ( rdc->tx.prod % RDC_NUM_TX_DESC ); + tx = &rdc->tx.desc[tx_idx]; + + /* Pad to minimum length */ + iob_pad ( iobuf, ETH_ZLEN ); + + /* Map I/O buffer */ + if ( ( rc = iob_map_tx ( iobuf, rdc->dma ) ) != 0 ) + return rc; + + /* Update producer index */ + rdc->tx.prod++; + + /* Populate transmit descriptor */ + tx->len = cpu_to_le16 ( iob_len ( iobuf ) ); + tx->addr = cpu_to_le32 ( iob_dma ( iobuf ) ); + wmb(); + tx->flags = cpu_to_le16 ( RDC_FL_OWNED ); + wmb(); + + /* Notify card that there are packets ready to transmit */ + writew ( RDC_MTPR_TM2TX, rdc->regs + RDC_MTPR ); + + return 0; +} + +/** + * Poll for completed packets + * + * @v netdev Network device + */ +static void rdc_poll_tx ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *tx; + unsigned int tx_idx; + + /* Check for completed packets */ + while ( rdc->tx.cons != rdc->tx.prod ) { + + /* Get next transmit descriptor */ + tx_idx = ( rdc->tx.cons % RDC_NUM_TX_DESC ); + tx = &rdc->tx.desc[tx_idx]; + + /* Stop if descriptor is still in use */ + if ( tx->flags & cpu_to_le16 ( RDC_FL_OWNED ) ) + return; + DBGC2 ( rdc, "RDC %p TX %d complete\n", rdc, tx_idx ); + + /* Complete transmit descriptor */ + rdc->tx.cons++; + netdev_tx_complete_next ( netdev ); + } +} + +/** + * Poll for received packets + * + * @v netdev Network device + */ +static void rdc_poll_rx ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + struct rdc_descriptor *rx; + struct io_buffer *iobuf; + unsigned int rx_idx; + size_t len; + + /* Check for received packets */ + while ( rdc->rx.cons != rdc->rx.prod ) { + + /* Get next receive descriptor */ + rx_idx = ( rdc->rx.cons % RDC_NUM_RX_DESC ); + rx = &rdc->rx.desc[rx_idx]; + + /* Stop if descriptor is still in use */ + if ( rx->flags & cpu_to_le16 ( RDC_FL_OWNED ) ) + return; + + /* Populate I/O buffer */ + iobuf = rdc->rx_iobuf[rx_idx]; + rdc->rx_iobuf[rx_idx] = NULL; + len = le16_to_cpu ( rx->len ); + iob_put ( iobuf, len ); + iob_unput ( iobuf, 4 /* strip CRC */ ); + + /* Hand off to network stack */ + if ( rx->flags & cpu_to_le16 ( RDC_FL_OK ) ) { + DBGC2 ( rdc, "RDC %p RX %d complete (length %zd)\n", + rdc, rx_idx, len ); + netdev_rx ( netdev, iobuf ); + } else { + DBGC2 ( rdc, "RDC %p RX %d error (length %zd, " + "flags %#04x)\n", rdc, rx_idx, len, + le16_to_cpu ( rx->flags ) ); + netdev_rx_err ( netdev, iobuf, -EIO ); + } + rdc->rx.cons++; + } +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rdc_poll ( struct net_device *netdev ) { + struct rdc_nic *rdc = netdev->priv; + uint16_t misr; + + /* Check for (and acknowledge) interrupts */ + misr = readw ( rdc->regs + RDC_MISR ); + + /* Poll for TX completions, if applicable */ + if ( misr & RDC_MIRQ_TX ) + rdc_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( misr & RDC_MIRQ_RX ) + rdc_poll_rx ( netdev ); + + /* Check link state, if applicable */ + if ( misr & RDC_MIRQ_LINK ) + rdc_check_link ( netdev ); + + /* Check for unexpected interrupts */ + if ( misr & ~( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX_EARLY | + RDC_MIRQ_RX_EMPTY | RDC_MIRQ_RX ) ) { + DBGC ( rdc, "RDC %p unexpected MISR %#04x\n", rdc, misr ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -ENOTSUP ); + } + + /* Refill receive ring */ + rdc_refill_rx ( rdc ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void rdc_irq ( struct net_device *netdev, int enable ) { + struct rdc_nic *rdc = netdev->priv; + uint16_t mier; + + /* Enable/disable interrupts */ + mier = ( enable ? ( RDC_MIRQ_LINK | RDC_MIRQ_TX | RDC_MIRQ_RX ) : 0 ); + writew ( mier, rdc->regs + RDC_MIER ); +} + +/** RDC network device operations */ +static struct net_device_operations rdc_operations = { + .open = rdc_open, + .close = rdc_close, + .transmit = rdc_transmit, + .poll = rdc_poll, + .irq = rdc_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int rdc_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct rdc_nic *rdc; + union rdc_mac mac; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *rdc ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &rdc_operations ); + rdc = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( rdc, 0, sizeof ( *rdc ) ); + rdc->dma = &pci->dma; + mdio_init ( &rdc->mdio, &rdc_mii_operations ); + mii_init ( &rdc->mii, &rdc->mdio, 0 ); + rdc_init_ring ( &rdc->tx, RDC_NUM_TX_DESC, RDC_MTDSA ); + rdc_init_ring ( &rdc->rx, RDC_NUM_RX_DESC, RDC_MRDSA ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + rdc->regs = pci_ioremap ( pci, pci->membase, RDC_BAR_SIZE ); + if ( ! rdc->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Fetch MAC address */ + mac.mid[0] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID0 ) ); + mac.mid[1] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID1 ) ); + mac.mid[2] = cpu_to_le16 ( readw ( rdc->regs + RDC_MID2 ) ); + memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); + + /* Reset the NIC */ + if ( ( rc = rdc_reset ( rdc ) ) != 0 ) + goto err_reset; + + /* Initialise PHY */ + if ( ( rc = rdc_init_phy ( rdc ) ) != 0 ) + goto err_init_phy; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + rdc_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_init_phy: + rdc_reset ( rdc ); + err_reset: + iounmap ( rdc->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void rdc_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct rdc_nic *rdc = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset card */ + rdc_reset ( rdc ); + + /* Free network device */ + iounmap ( rdc->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** RDC PCI device IDs */ +static struct pci_device_id rdc_nics[] = { + PCI_ROM ( 0x17f3, 0x6040, "r6040", "RDC R6040", 0 ), +}; + +/** RDC PCI driver */ +struct pci_driver rdc_driver __pci_driver = { + .ids = rdc_nics, + .id_count = ( sizeof ( rdc_nics ) / sizeof ( rdc_nics[0] ) ), + .probe = rdc_probe, + .remove = rdc_remove, +}; diff --git a/src/drivers/net/rdc.h b/src/drivers/net/rdc.h new file mode 100644 index 000000000..ee1671fdd --- /dev/null +++ b/src/drivers/net/rdc.h @@ -0,0 +1,194 @@ +#ifndef _RDC_H +#define _RDC_H + +/** @file + * + * RDC R6040 network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** RDC BAR size */ +#define RDC_BAR_SIZE 256 + +/** An RDC descriptor */ +struct rdc_descriptor { + /** Flags */ + uint16_t flags; + /** Length */ + uint16_t len; + /** Address */ + uint32_t addr; + /** Next descriptor */ + uint32_t next; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** Descriptor is owned by NIC */ +#define RDC_FL_OWNED 0x8000 + +/** Packet OK */ +#define RDC_FL_OK 0x4000 + +/** MAC control register 0 */ +#define RDC_MCR0 0x00 +#define RDC_MCR0_FD 0x8000 /**< Full duplex */ +#define RDC_MCR0_TXEN 0x1000 /**< Transmit enable */ +#define RDC_MCR0_PROMISC 0x0020 /**< Promiscuous mode */ +#define RDC_MCR0_RXEN 0x0002 /**< Receive enable */ + +/** MAC control register 1 */ +#define RDC_MCR1 0x04 +#define RDC_MCR1_RST 0x0001 /**< MAC reset */ + +/** Maximum time to wait for reset */ +#define RDC_RESET_MAX_WAIT_MS 10 + +/** MAC transmit poll command register */ +#define RDC_MTPR 0x14 +#define RDC_MTPR_TM2TX 0x0001 /**< Trigger MAC to transmit */ + +/** MAC receive buffer size register */ +#define RDC_MRBSR 0x18 + +/** MAC MDIO control register */ +#define RDC_MMDIO 0x20 +#define RDC_MMDIO_MIIWR 0x4000 /**< MDIO write */ +#define RDC_MMDIO_MIIRD 0x2000 /**< MDIO read */ +#define RDC_MMDIO_PHYAD(x) ( (x) << 8 ) /**< PHY address */ +#define RDC_MMDIO_REGAD(x) ( (x) << 0 ) /**< Register address */ + +/** Maximum time to wait for an MII read or write */ +#define RDC_MII_MAX_WAIT_US 2048 + +/** MAC MDIO read data register */ +#define RDC_MMRD 0x24 + +/** MAC MDIO write data register */ +#define RDC_MMWD 0x28 + +/** MAC transmit descriptor start address */ +#define RDC_MTDSA 0x2c + +/** MAC receive descriptor start address */ +#define RDC_MRDSA 0x34 + +/** MAC descriptor start address low half */ +#define RDC_MxDSA_LO 0x0 + +/** MAC descriptor start address low half */ +#define RDC_MxDSA_HI 0x4 + +/** MAC interrupt status register */ +#define RDC_MISR 0x3c +#define RDC_MIRQ_LINK 0x0200 /**< Link status changed */ +#define RDC_MIRQ_TX 0x0010 /**< Transmit complete */ +#define RDC_MIRQ_RX_EARLY 0x0008 /**< Receive early interrupt */ +#define RDC_MIRQ_RX_EMPTY 0x0002 /**< Receive descriptor unavailable */ +#define RDC_MIRQ_RX 0x0001 /**< Receive complete */ + +/** MAC interrupt enable register */ +#define RDC_MIER 0x40 + +/** MAC address word 0 */ +#define RDC_MID0 0x68 + +/** MAC address word 1 */ +#define RDC_MID1 0x6a + +/** MAC address word 2 */ +#define RDC_MID2 0x6c + +/** MAC PHY status change configuration register */ +#define RDC_MPSCCR 0x88 +#define RDC_MPSCCR_EN 0x8000 /**< PHY status change enable */ +#define RDC_MPSCCR_PHYAD(x) ( (x) << 8 ) /**< PHY address */ +#define RDC_MPSCCR_SLOW 0x0007 /**< Poll slowly */ + +/** MAC state machine register */ +#define RDC_MACSM 0xac +#define RDC_MACSM_RST 0x0002 /**< Reset state machine */ + +/** Time to wait after resetting MAC state machine */ +#define RDC_MACSM_RESET_DELAY_MS 10 + +/** A MAC address */ +union rdc_mac { + /** Raw bytes */ + uint8_t raw[ETH_ALEN]; + /** MIDx registers */ + uint16_t mid[ ETH_ALEN / 2 ]; +}; + +/** A descriptor ring */ +struct rdc_ring { + /** Descriptors */ + struct rdc_descriptor *desc; + /** Descriptor ring DMA mapping */ + struct dma_mapping map; + /** Producer index */ + unsigned int prod; + /** Consumer index */ + unsigned int cons; + + /** Number of descriptors */ + unsigned int count; + /** Start address register 0 */ + unsigned int reg; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Number of descriptors + * @v reg Start address register 0 + */ +static inline __attribute__ (( always_inline )) void +rdc_init_ring ( struct rdc_ring *ring, unsigned int count, unsigned int reg ) { + + ring->count = count; + ring->reg = reg; +} + +/** Number of transmit descriptors + * + * This is a policy decision. + */ +#define RDC_NUM_TX_DESC 16 + +/** Number of receive descriptors + * + * This is a policy decision. + */ +#define RDC_NUM_RX_DESC 8 + +/** Receive buffer length */ +#define RDC_RX_MAX_LEN ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) + +/** An RDC network card */ +struct rdc_nic { + /** Registers */ + void *regs; + /** DMA device */ + struct dma_device *dma; + /** MII interface */ + struct mii_interface mdio; + /** MII device */ + struct mii_device mii; + + /** Transmit descriptor ring */ + struct rdc_ring tx; + /** Receive descriptor ring */ + struct rdc_ring rx; + /** Receive I/O buffers */ + struct io_buffer *rx_iobuf[RDC_NUM_RX_DESC]; +}; + +#endif /* _RDC_H */ diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 90c91cdb9..cf5757874 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -213,6 +213,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_usbblk ( ERRFILE_DRIVER | 0x00ce0000 ) #define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 ) #define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 ) +#define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) -- cgit v1.2.3-55-g7522 From 2690f730961e335875cac5fd9cf870a94eaef11a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 1 Jul 2021 16:32:46 +0100 Subject: [uri] Make URI schemes case-insensitive RFC 3986 section 3.1 defines URI schemes as case-insensitive (though the canonical form is always lowercase). Use strcasecmp() rather than strcmp() to allow for case insensitivity in URI schemes. Requested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/core/open.c | 3 ++- src/net/tcp/httpconn.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/open.c b/src/core/open.c index c27d8a021..f9198c9d9 100644 --- a/src/core/open.c +++ b/src/core/open.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -47,7 +48,7 @@ struct uri_opener * xfer_uri_opener ( const char *scheme ) { struct uri_opener *opener; for_each_table_entry ( opener, URI_OPENERS ) { - if ( strcmp ( scheme, opener->scheme ) == 0 ) + if ( strcasecmp ( scheme, opener->scheme ) == 0 ) return opener; } return NULL; diff --git a/src/net/tcp/httpconn.c b/src/net/tcp/httpconn.c index f9221b27e..538c4dcf6 100644 --- a/src/net/tcp/httpconn.c +++ b/src/net/tcp/httpconn.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -63,7 +64,7 @@ static struct http_scheme * http_scheme ( struct uri *uri ) { /* Identify scheme */ for_each_table_entry ( scheme, HTTP_SCHEMES ) { - if ( strcmp ( uri->scheme, scheme->name ) == 0 ) + if ( strcasecmp ( uri->scheme, scheme->name ) == 0 ) return scheme; } -- cgit v1.2.3-55-g7522 From 8d08300ad945988e0ce2fc24a34c19613154bae5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Jul 2021 13:46:34 +0100 Subject: [libc] Allow for externally-defined LITTLE_ENDIAN and BIG_ENDIAN constants When building the Linux userspace binaries, the external system headers may have already defined values for the __LITTLE_ENDIAN and __BIG_ENDIAN constants. Fix by retaining the existing values if already defined, since the actual values of these constants do not matter. Signed-off-by: Michael Brown --- src/include/endian.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/include/endian.h b/src/include/endian.h index 79c3163ee..bdae9de45 100644 --- a/src/include/endian.h +++ b/src/include/endian.h @@ -8,14 +8,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Little-endian systems should define BYTE_ORDER as LITTLE_ENDIAN. * This constant is intended to be used only at compile time. */ +#ifndef __LITTLE_ENDIAN #define __LITTLE_ENDIAN 0x44332211UL +#endif /** Constant representing big-endian byte order * * Big-endian systems should define BYTE_ORDER as BIG_ENDIAN. * This constant is intended to be used only at compile time. */ +#ifndef __BIG_ENDIAN #define __BIG_ENDIAN 0x11223344UL +#endif #include "bits/endian.h" -- cgit v1.2.3-55-g7522 From c64dfff0a93b5f967ae21339313e61baf0b7a3a0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 15 Jul 2021 15:32:31 +0100 Subject: [efi] Match signtool expectations for file alignment As of commit f1e9e2b ("[efi] Align EFI image sections by page size"), our SectionAlignment has been increased to 4kB in order to allow for page-level memory protection to be applied by the UEFI firmware, with FileAlignment left at 32 bytes. The PE specification states that the value for FileAlignment "should be a power of 2 between 512 and 64k, inclusive", and that "if the SectionAlignment is less than the architecture's page size, then FileAlignment must match SectionAlignment". Testing shows that signtool.exe will reject binaries where FileAlignment is less than 512, unless FileAlignment is equal to SectionAlignment. This indicates a somewhat zealous interpretation of the word "should" in the PE specification. Work around this interpretation by increasing FileAlignment from 32 bytes to 512 bytes, and add explanatory comments for both FileAlignment and SectionAlignment. Debugged-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/util/elf2efi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 5542b99f8..8af53aeb4 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -125,7 +125,21 @@ #define R_ARM_V4BX 40 #endif -#define EFI_FILE_ALIGN 0x20 +/** + * Alignment of raw data of sections in the image file + * + * Some versions of signtool.exe will spuriously complain if this + * value is less than 512. + */ +#define EFI_FILE_ALIGN 0x200 + +/** + * Alignment of sections when loaded into memory + * + * This must equal the architecture page size, in order to allow for + * the possibility of the firmware using page-level protection to + * enforce section attributes at runtime. + */ #define EFI_IMAGE_ALIGN 0x1000 struct elf_file { -- cgit v1.2.3-55-g7522 From 4d180be5175e176ec27680557c1d67536a847610 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 20 Jul 2021 13:19:15 +0100 Subject: [cloud] Retry DHCP aggressively in AWS EC2 The DHCP service in EC2 has been observed to occasionally stop responding for bursts of several seconds. This can easily result in a failed boot, since the current cloud boot script will attempt DHCP only once. Work around this problem by retrying DHCP in a fairly tight cycle within the cloud boot script, and falling back to a reboot after several failed DHCP attempts. Signed-off-by: Michael Brown --- src/config/cloud/aws.ipxe | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/config/cloud/aws.ipxe b/src/config/cloud/aws.ipxe index 66ad77d98..6c007398e 100644 --- a/src/config/cloud/aws.ipxe +++ b/src/config/cloud/aws.ipxe @@ -3,7 +3,22 @@ echo Amazon EC2 - iPXE boot via user-data echo CPU: ${cpuvendor} ${cpumodel} ifstat || -dhcp || + +set attempt:int8 1 +:dhcp_retry +echo DHCP attempt ${attempt} +dhcp --timeout 5000 && goto dhcp_ok || +ifstat || +inc attempt +iseq ${attempt} 10 || goto dhcp_retry + +:dhcp_fail +echo DHCP failed - rebooting +reboot || +exit + +:dhcp_ok route || chain -ar http://169.254.169.254/latest/user-data || ifstat || +exit -- cgit v1.2.3-55-g7522