diff options
Diffstat (limited to 'src/interface/efi/efi_local.c')
-rw-r--r-- | src/interface/efi/efi_local.c | 294 |
1 files changed, 162 insertions, 132 deletions
diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index d3ac3d54..b2881424 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -35,6 +35,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uri.h> #include <ipxe/iobuf.h> #include <ipxe/process.h> +#include <ipxe/errortab.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_strings.h> #include <ipxe/efi/efi_path.h> @@ -48,6 +49,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/* Disambiguate the various error causes */ +#define EINFO_EEFI_OPEN \ + __einfo_uniqify ( EINFO_EPLATFORM, 0x01, "Could not open" ) +#define EINFO_EEFI_OPEN_NOT_FOUND \ + __einfo_platformify ( EINFO_EEFI_OPEN, EFI_NOT_FOUND, \ + "Not found" ) +#define EEFI_OPEN_NOT_FOUND \ + __einfo_error ( EINFO_EEFI_OPEN_NOT_FOUND ) +#define EEFI_OPEN( efirc ) EPLATFORM ( EINFO_EEFI_OPEN, efirc, \ + EEFI_OPEN_NOT_FOUND ) + /** Download blocksize */ #define EFI_LOCAL_BLKSIZE 4096 @@ -60,6 +72,13 @@ struct efi_local { /** Download process */ struct process process; + /** Download URI */ + struct uri *uri; + /** Volume name, or NULL to use loaded image's device */ + const char *volume; + /** File path */ + const char *path; + /** EFI root directory */ EFI_FILE_PROTOCOL *root; /** EFI file */ @@ -68,6 +87,24 @@ struct efi_local { size_t len; }; +/** Human-readable error messages */ +struct errortab efi_local_errors[] __errortab = { + __einfo_errortab ( EINFO_EEFI_OPEN_NOT_FOUND ), +}; + +/** + * Free local file + * + * @v refcnt Reference count + */ +static void efi_local_free ( struct refcnt *refcnt ) { + struct efi_local *local = + container_of ( refcnt, struct efi_local, refcnt ); + + uri_put ( local->uri ); + free ( local ); +} + /** * Close local file * @@ -96,91 +133,6 @@ static void efi_local_close ( struct efi_local *local, int rc ) { } /** - * Local file process - * - * @v local Local file - */ -static void efi_local_step ( struct efi_local *local ) { - EFI_FILE_PROTOCOL *file = local->file; - struct io_buffer *iobuf = NULL; - size_t remaining; - size_t frag_len; - UINTN size; - EFI_STATUS efirc; - int rc; - - /* Wait until data transfer interface is ready */ - if ( ! xfer_window ( &local->xfer ) ) - return; - - /* Presize receive buffer */ - remaining = local->len; - xfer_seek ( &local->xfer, remaining ); - xfer_seek ( &local->xfer, 0 ); - - /* Get file contents */ - while ( remaining ) { - - /* Calculate length for this fragment */ - frag_len = remaining; - if ( frag_len > EFI_LOCAL_BLKSIZE ) - frag_len = EFI_LOCAL_BLKSIZE; - - /* Allocate I/O buffer */ - iobuf = xfer_alloc_iob ( &local->xfer, frag_len ); - if ( ! iobuf ) { - rc = -ENOMEM; - goto err; - } - - /* Read block */ - size = frag_len; - if ( ( efirc = file->Read ( file, &size, iobuf->data ) ) != 0 ){ - rc = -EEFI ( efirc ); - DBGC ( local, "LOCAL %p could not read from file: %s\n", - local, strerror ( rc ) ); - goto err; - } - assert ( size <= frag_len ); - iob_put ( iobuf, size ); - - /* Deliver data */ - if ( ( rc = xfer_deliver_iob ( &local->xfer, - iob_disown ( iobuf ) ) ) != 0 ) { - DBGC ( local, "LOCAL %p could not deliver data: %s\n", - local, strerror ( rc ) ); - goto err; - } - - /* Move to next block */ - remaining -= frag_len; - } - - /* Close download */ - efi_local_close ( local, 0 ); - - return; - - err: - free_iob ( iobuf ); - efi_local_close ( local, rc ); -} - -/** Data transfer interface operations */ -static struct interface_operation efi_local_operations[] = { - INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ), - INTF_OP ( intf_close, struct efi_local *, efi_local_close ), -}; - -/** Data transfer interface descriptor */ -static struct interface_descriptor efi_local_xfer_desc = - INTF_DESC ( struct efi_local, xfer, efi_local_operations ); - -/** Process descriptor */ -static struct process_descriptor efi_local_process_desc = - PROC_DESC_ONCE ( struct efi_local, process, efi_local_step ); - -/** * Check for matching volume name * * @v local Local file @@ -354,15 +306,14 @@ static int efi_local_open_volume_index ( struct efi_local *local, * Open root filesystem of specified volume * * @v local Local file - * @v volume Volume name, or NULL to use loaded image's device * @ret rc Return status code */ -static int efi_local_open_volume ( struct efi_local *local, - const char *volume ) { +static int efi_local_open_volume ( struct efi_local *local ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; int ( * check ) ( struct efi_local *local, EFI_HANDLE device, EFI_FILE_PROTOCOL *root, const char *volume ); + const char *volume = local->volume; EFI_DEVICE_PATH_PROTOCOL *path; EFI_FILE_PROTOCOL *root; EFI_HANDLE *handles; @@ -461,7 +412,7 @@ static int efi_local_open_resolved ( struct efi_local *local, /* Open file */ if ( ( efirc = local->root->Open ( local->root, &file, name, EFI_FILE_MODE_READ, 0 ) ) != 0 ) { - rc = -EEFI ( efirc ); + rc = -EEFI_OPEN ( efirc ); DBGC ( local, "LOCAL %p could not open \"%s\": %s\n", local, resolved, strerror ( rc ) ); return rc; @@ -475,11 +426,9 @@ static int efi_local_open_resolved ( struct efi_local *local, * Open specified path * * @v local Local file - * @v filename Path to file relative to our own image * @ret rc Return status code */ -static int efi_local_open_path ( struct efi_local *local, - const char *filename ) { +static int efi_local_open_path ( struct efi_local *local ) { EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image->FilePath; EFI_DEVICE_PATH_PROTOCOL *next; FILEPATH_DEVICE_PATH *fp; @@ -510,7 +459,7 @@ static int efi_local_open_path ( struct efi_local *local, } /* Resolve path */ - resolved = resolve_path ( base, filename ); + resolved = resolve_path ( base, local->path ); if ( ! resolved ) { rc = -ENOMEM; goto err_resolve; @@ -580,6 +529,106 @@ static int efi_local_len ( struct efi_local *local ) { } /** + * Local file process + * + * @v local Local file + */ +static void efi_local_step ( struct efi_local *local ) { + struct io_buffer *iobuf = NULL; + size_t remaining; + size_t frag_len; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Wait until data transfer interface is ready */ + if ( ! xfer_window ( &local->xfer ) ) + return; + + /* Open specified volume root directory, if not yet open */ + if ( ( ! local->root ) && + ( ( rc = efi_local_open_volume ( local ) ) != 0 ) ) + goto err; + + /* Open specified file, if not yet open */ + if ( ( ! local->file ) && + ( ( rc = efi_local_open_path ( local ) ) != 0 ) ) + goto err; + + /* Get file length, if not yet known */ + if ( ( ! local->len ) && + ( ( rc = efi_local_len ( local ) ) != 0 ) ) + goto err; + + /* Presize receive buffer */ + remaining = local->len; + xfer_seek ( &local->xfer, remaining ); + xfer_seek ( &local->xfer, 0 ); + + /* Get file contents */ + while ( remaining ) { + + /* Calculate length for this fragment */ + frag_len = remaining; + if ( frag_len > EFI_LOCAL_BLKSIZE ) + frag_len = EFI_LOCAL_BLKSIZE; + + /* Allocate I/O buffer */ + iobuf = xfer_alloc_iob ( &local->xfer, frag_len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err; + } + + /* Read block */ + size = frag_len; + if ( ( efirc = local->file->Read ( local->file, &size, + iobuf->data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( local, "LOCAL %p could not read from file: %s\n", + local, strerror ( rc ) ); + goto err; + } + assert ( size <= frag_len ); + iob_put ( iobuf, size ); + + /* Deliver data */ + if ( ( rc = xfer_deliver_iob ( &local->xfer, + iob_disown ( iobuf ) ) ) != 0 ) { + DBGC ( local, "LOCAL %p could not deliver data: %s\n", + local, strerror ( rc ) ); + goto err; + } + + /* Move to next block */ + remaining -= frag_len; + } + + /* Close download */ + efi_local_close ( local, 0 ); + + return; + + err: + free_iob ( iobuf ); + efi_local_close ( local, rc ); +} + +/** Data transfer interface operations */ +static struct interface_operation efi_local_operations[] = { + INTF_OP ( xfer_window_changed, struct efi_local *, efi_local_step ), + INTF_OP ( intf_close, struct efi_local *, efi_local_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor efi_local_xfer_desc = + INTF_DESC ( struct efi_local, xfer, efi_local_operations ); + +/** Process descriptor */ +static struct process_descriptor efi_local_process_desc = + PROC_DESC_ONCE ( struct efi_local, process, efi_local_step ); + +/** * Open local file * * @v xfer Data transfer interface @@ -588,33 +637,26 @@ static int efi_local_len ( struct efi_local *local ) { */ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { struct efi_local *local; - const char *volume; - const char *path; - int rc, vol; - - /* Parse URI */ - volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); - path = ( uri->opaque ? uri->opaque : uri->path ); + int vol; + int rc = 0; /* Allocate and initialise structure */ local = zalloc ( sizeof ( *local ) ); - if ( ! local ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &local->refcnt, NULL ); - intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); - process_init_stopped ( &local->process, &efi_local_process_desc, - &local->refcnt ); - - if ( volume && strcmp ( volume, "*" ) == 0 ) { + if ( ! local ) + return -ENOMEM; + ref_init ( &local->refcnt, efi_local_free ); + local->uri = uri_get ( uri ); + local->volume = ( ( uri->host && uri->host[0] ) ? uri->host : NULL ); + local->path = ( uri->opaque ? uri->opaque : uri->path ); + + if ( local->path && local->volume && strcmp ( local->volume, "*" ) == 0 ) { /* Open on any volume */ vol = 0; while ( ( rc = efi_local_open_volume_index ( local, vol++ ) ) != -ENOENT ) { if ( rc != 0 ) continue; /* Open specified path */ - if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) { + if ( ( rc = efi_local_open_path ( local ) ) != 0 ) { local->root->Close ( local->root ); local->root = NULL; continue; @@ -622,21 +664,17 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { /* Success */ break; } - if ( rc != 0 ) - goto err_open_root; - } else { - /* Open specified volume */ - if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) - goto err_open_root; - - /* Open specified path */ - if ( ( rc = efi_local_open_path ( local, path ) ) != 0 ) - goto err_open_file; + if ( rc != 0 ) { + DBGC ( local, "LOCAL %p could not find %s on any partition\n", + local, local->path ); + ref_put ( &local->refcnt ); + return -ENOENT; + } } - /* Get length of file */ - if ( ( rc = efi_local_len ( local ) ) != 0 ) - goto err_len; + intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); + process_init_stopped ( &local->process, &efi_local_process_desc, + &local->refcnt ); /* Start download process */ process_add ( &local->process ); @@ -645,14 +683,6 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { intf_plug_plug ( &local->xfer, xfer ); ref_put ( &local->refcnt ); return 0; - - err_len: - err_open_file: - err_open_root: - efi_local_close ( local, 0 ); - ref_put ( &local->refcnt ); - err_alloc: - return rc; } /** EFI local file URI opener */ |