aboutsummaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface')
-rw-r--r--src/interface/efi/efi_file.c117
1 files changed, 104 insertions, 13 deletions
diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c
index 4c482d2e..f8713750 100644
--- a/src/interface/efi/efi_file.c
+++ b/src/interface/efi/efi_file.c
@@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Protocol/BlockIo.h>
+#include <ipxe/efi/Protocol/DiskIo.h>
#include <ipxe/efi/Guid/FileInfo.h>
#include <ipxe/efi/Guid/FileSystemInfo.h>
#include <ipxe/efi/efi_strings.h>
@@ -55,6 +56,10 @@ static EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID;
static EFI_GUID efi_block_io_protocol_guid
= EFI_BLOCK_IO_PROTOCOL_GUID;
+/** EFI disk I/O protocol GUID */
+static EFI_GUID efi_disk_io_protocol_guid
+ = EFI_DISK_IO_PROTOCOL_GUID;
+
/** EFI media ID */
#define EFI_MEDIA_ID_MAGIC 0x69505845
@@ -506,7 +511,7 @@ static EFI_STATUS EFIAPI
efi_block_io_read_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused,
UINT32 MediaId __unused, EFI_LBA lba __unused,
UINTN len __unused, VOID *data __unused ) {
- return EFI_DEVICE_ERROR;
+ return EFI_NO_MEDIA;
}
/** Dummy block I/O write */
@@ -514,7 +519,7 @@ static EFI_STATUS EFIAPI
efi_block_io_write_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused,
UINT32 MediaId __unused, EFI_LBA lba __unused,
UINTN len __unused, VOID *data __unused ) {
- return EFI_DEVICE_ERROR;
+ return EFI_NO_MEDIA;
}
/** Dummy block I/O flush */
@@ -541,6 +546,29 @@ static EFI_BLOCK_IO_PROTOCOL efi_block_io_protocol = {
.FlushBlocks = efi_block_io_flush_blocks,
};
+/** Dummy disk I/O read */
+static EFI_STATUS EFIAPI
+efi_disk_io_read_disk ( EFI_DISK_IO_PROTOCOL *this __unused,
+ UINT32 MediaId __unused, UINT64 offset __unused,
+ UINTN len __unused, VOID *data __unused ) {
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy disk I/O write */
+static EFI_STATUS EFIAPI
+efi_disk_io_write_disk ( EFI_DISK_IO_PROTOCOL *this __unused,
+ UINT32 MediaId __unused, UINT64 offset __unused,
+ UINTN len __unused, VOID *data __unused ) {
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy EFI disk I/O protocol */
+static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = {
+ .Revision = EFI_DISK_IO_PROTOCOL_REVISION,
+ .ReadDisk = efi_disk_io_read_disk,
+ .WriteDisk = efi_disk_io_write_disk,
+};
+
/**
* Install EFI simple file system protocol
*
@@ -549,29 +577,79 @@ static EFI_BLOCK_IO_PROTOCOL efi_block_io_protocol = {
*/
int efi_file_install ( EFI_HANDLE *handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_DISK_IO_PROTOCOL *diskio;
+ void *interface;
+ } diskio;
EFI_STATUS efirc;
int rc;
- /* Install the simple file system protocol and the block I/O
- * protocol. We don't have a block device, but large parts of
- * the EDK2 codebase make the assumption that file systems are
- * normally attached to block devices, and so we create a
- * dummy block device on the same handle just to keep things
- * looking normal.
+ /* Install the simple file system protocol, block I/O
+ * protocol, and disk I/O protocol. We don't have a block
+ * device, but large parts of the EDK2 codebase make the
+ * assumption that file systems are normally attached to block
+ * devices, and so we create a dummy block device on the same
+ * handle just to keep things looking normal.
*/
if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
handle,
&efi_block_io_protocol_guid,
&efi_block_io_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
&efi_simple_file_system_protocol_guid,
&efi_simple_file_system_protocol, NULL ) ) != 0 ) {
rc = -EEFI ( efirc );
- DBGC ( handle, "Could not install simple file system protocol: "
- "%s\n", strerror ( rc ) );
- return rc;
+ DBGC ( handle, "Could not install simple file system "
+ "protocols: %s\n", strerror ( rc ) );
+ goto err_install;
+ }
+
+ /* The FAT filesystem driver has a bug: if a block device
+ * contains no FAT filesystem but does have an
+ * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instance, the FAT driver
+ * will assume that it must have previously installed the
+ * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. This causes the FAT
+ * driver to claim control of our device, and to refuse to
+ * stop driving it, which prevents us from later uninstalling
+ * correctly.
+ *
+ * Work around this bug by opening the disk I/O protocol
+ * ourselves, thereby preventing the FAT driver from opening
+ * it.
+ *
+ * Note that the alternative approach of opening the block I/O
+ * protocol (and thereby in theory preventing DiskIo from
+ * attaching to the block I/O protocol) causes an endless loop
+ * of calls to our DRIVER_STOP method when starting the EFI
+ * shell. I have no idea why this is.
+ */
+ if ( ( efirc = bs->OpenProtocol ( *handle, &efi_disk_io_protocol_guid,
+ &diskio.interface, efi_image_handle,
+ *handle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER ) ) != 0){
+ rc = -EEFI ( efirc );
+ DBGC ( handle, "Could not open disk I/O protocol: %s\n",
+ strerror ( rc ) );
+ goto err_open;
}
+ assert ( diskio.diskio == &efi_disk_io_protocol );
return 0;
+
+ bs->CloseProtocol ( *handle, &efi_disk_io_protocol_guid,
+ efi_image_handle, *handle );
+ err_open:
+ bs->UninstallMultipleProtocolInterfaces (
+ *handle,
+ &efi_simple_file_system_protocol_guid,
+ &efi_simple_file_system_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
+ &efi_block_io_protocol_guid,
+ &efi_block_io_protocol, NULL );
+ err_install:
+ return rc;
}
/**
@@ -581,16 +659,29 @@ int efi_file_install ( EFI_HANDLE *handle ) {
*/
void efi_file_uninstall ( EFI_HANDLE handle ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Close our own disk I/O protocol */
+ bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
+ efi_image_handle, handle );
/* We must install the file system protocol first, since
* otherwise the EDK2 code will attempt to helpfully uninstall
* it when the block I/O protocol is uninstalled, leading to a
* system lock-up.
*/
- bs->UninstallMultipleProtocolInterfaces (
+ if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
handle,
&efi_simple_file_system_protocol_guid,
&efi_simple_file_system_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
&efi_block_io_protocol_guid,
- &efi_block_io_protocol, NULL );
+ &efi_block_io_protocol, NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( handle, "Could not uninstall simple file system "
+ "protocols: %s\n", strerror ( rc ) );
+ /* Oh dear */
+ }
}