From cd19beea0f46f2648e8fbf145bc6014e7096e693 Mon Sep 17 00:00:00 2001 From: Zhao Lei Date: Mon, 25 Feb 2019 18:04:10 +0800 Subject: [PATCH] Workaround for EFI Bug (Plan3) Signed-off-by: Zhao Lei --- grub-core/disk/efi/efidisk.c | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index f04f20b..3786438 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -27,12 +27,19 @@ #include #include +typedef enum { + GRUB_EFI_BIOS_OVERFLOW_INIT, + GRUB_EFI_BIOS_OVERFLOW_EXIST, + GRUB_EFI_BIOS_OVERFLOW_NOEXIST +} grub_efi_bios_overflow_t; + struct grub_efidisk_data { grub_efi_handle_t handle; grub_efi_device_path_t *device_path; grub_efi_device_path_t *last_device_path; grub_efi_block_io_t *block_io; + grub_efi_bios_overflow_t bios_overflow; struct grub_efidisk_data *next; }; @@ -92,6 +99,7 @@ make_devices (void) d->device_path = dp; d->last_device_path = ldp; d->block_io = bio; + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_INIT; d->next = devices; devices = d; } @@ -519,8 +527,9 @@ grub_efidisk_close (struct grub_disk *disk __attribute__ ((unused))) grub_dprintf ("efidisk", "closing %s\n", disk->name); } + static grub_efi_status_t -grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector, +__grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector, grub_size_t size, char *buf, int wr) { struct grub_efidisk_data *d; @@ -563,6 +572,103 @@ grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector, return status; } +static void grub_efidisk_set_overflow(struct grub_disk *disk) +{ + struct grub_efidisk_data *d = disk->data; + + char *buf; + int read_len; + int buf_len; + + static char magic_list[] = {0x55, 0xaa}; + unsigned int magic_index; + + if (d->bios_overflow != GRUB_EFI_BIOS_OVERFLOW_INIT) + return; + + if (d->block_io->media->removable_media) { + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_NOEXIST; + return; + } + + read_len = (1 << disk->log_sector_size); + + /* read_len + 9 is enough, we use more */ + buf_len = (read_len + 8) * 2; + + buf = grub_malloc(buf_len); + if (!buf) { + grub_printf("grub_efidisk_set_overflow: malloc failed, ignore operation %s\n", disk->name); + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_EXIST; + return; + } + + for (magic_index = 0; magic_index < sizeof(magic_list)/sizeof(magic_list[0]); magic_index++) { + int buf_index; + + grub_memset(buf + read_len, magic_list[magic_index], buf_len - read_len); + + /* + * If disk can not read, we can not determine overflow state now, + * just leave GRUB_EFI_BIOS_OVERFLOW_INIT state and re-check on + * next operation of this disk. + */ + if (GRUB_EFI_SUCCESS != __grub_efidisk_readwrite(disk, 0, 1, buf, 0)) { + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_INIT; + goto out; + } + + for (buf_index = read_len; buf_index < buf_len; buf_index++) { + if (buf[buf_index] != magic_list[magic_index]) { + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_EXIST; + goto out; + } + } + } + + d->bios_overflow = GRUB_EFI_BIOS_OVERFLOW_NOEXIST; + +out: + grub_free(buf); + return; +} + +static int grub_efidisk_check_overflow(struct grub_disk *disk) +{ + struct grub_efidisk_data *d = disk->data; + int ret; + + grub_efidisk_set_overflow(disk); + + switch (d->bios_overflow) { + case GRUB_EFI_BIOS_OVERFLOW_INIT: + ret = 0; + break; + case GRUB_EFI_BIOS_OVERFLOW_NOEXIST: + ret = 0; + break; + case GRUB_EFI_BIOS_OVERFLOW_EXIST: + ret = 1; + break; + default: + grub_printf("grub_efidisk_check_overflow: internal error in bios_overflow value(%d), ignore operation %s\n", d->bios_overflow, disk->name); + ret = 1; + break; + } + + return ret; +} + +static grub_efi_status_t +grub_efidisk_readwrite (struct grub_disk *disk, grub_disk_addr_t sector, + grub_size_t size, char *buf, int wr) +{ + if (grub_efidisk_check_overflow(disk)) + return GRUB_ERR_UNKNOWN_DEVICE; + + return __grub_efidisk_readwrite(disk, sector, size, buf, wr); +} + static grub_err_t grub_efidisk_read (struct grub_disk *disk, grub_disk_addr_t sector, grub_size_t size, char *buf)