diff --git a/include/libkernelflinger/lib.h b/include/libkernelflinger/lib.h index b6bb170..eb5ba7f 100644 --- a/include/libkernelflinger/lib.h +++ b/include/libkernelflinger/lib.h @@ -200,6 +200,8 @@ void qsort(void *base, size_t num, size_t width, int (*compare)(const void *, const void *)) __attribute__((weak)); +INTN StrcaseCmp(CHAR16 *s1, CHAR16 *s2); + /* * misc */ diff --git a/include/libkernelflinger/uefi_utils.h b/include/libkernelflinger/uefi_utils.h index 6506690..9f75b25 100644 --- a/include/libkernelflinger/uefi_utils.h +++ b/include/libkernelflinger/uefi_utils.h @@ -57,5 +57,10 @@ EFI_STATUS uefi_create_directory_root(EFI_FILE_IO_INTERFACE *io, CHAR16 *dirname EFI_STATUS uefi_rename_file(EFI_FILE_IO_INTERFACE *io, CHAR16 *oldname, CHAR16 *newname); EFI_STATUS verify_image(EFI_HANDLE handle, CHAR16 *path); EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name); +EFI_STATUS uefi_enter_binary(EFI_HANDLE part_handle, CHAR16 *path, + BOOLEAN delete, UINT32 load_options_size, VOID *load_options); +EFI_STATUS uefi_check_upgrade(EFI_LOADED_IMAGE *loaded_image, + CHAR16 *partition, CHAR16 *upgrade_file, + CHAR16 *self_path1, CHAR16 *bak_path1, CHAR16 *self_path2, CHAR16 *bak_path2); #endif /* __UEFI_UTILS_H__ */ diff --git a/kernelflinger.c b/kernelflinger.c index 4e96b10..eaba2a6 100644 --- a/kernelflinger.c +++ b/kernelflinger.c @@ -830,57 +830,6 @@ static EFI_STATUS load_boot_image( } #endif -/* Chainload another EFI application on the ESP with the specified path, - * optionally deleting the file before entering - */ -static EFI_STATUS enter_efi_binary(CHAR16 *path, BOOLEAN delete, UINT32 load_options_size, VOID *load_options) -{ - EFI_DEVICE_PATH *edp; - EFI_STATUS ret; - EFI_HANDLE image; - EFI_LOADED_IMAGE *loaded_image; - - - edp = FileDevicePath(g_disk_device, path); - if (!edp) { - error(L"Couldn't generate a path"); - return EFI_INVALID_PARAMETER; - } - - ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, - edp, NULL, 0, &image); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"BS->LoadImage '%s'", path); - } else { - if (delete) { - ret = file_delete(g_disk_device, path); - if (EFI_ERROR(ret)) - efi_perror(ret, L"Couldn't delete %s", path); - } - if (load_options_size > 0) { - // Set the command line option - ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, - &LoadedImageProtocol, (VOID **)&loaded_image, - image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"OpenProtocol: LoadedImageProtocol"); - return ret; - } - if (loaded_image == NULL) { - error(L"LoadedImageProtocol, but return image is NULL"); - return EFI_INVALID_PARAMETER; - } - loaded_image->LoadOptionsSize = load_options_size; - loaded_image->LoadOptions = load_options; - } - ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); - uefi_call_wrapper(BS->UnloadImage, 1, image); - } - FreePool(edp); - return ret; -} - - #define OEMVARS_MAGIC "#OEMVARS\n" #define OEMVARS_MAGIC_SZ 9 @@ -1260,99 +1209,6 @@ static void flash_bootloader_policy(__attribute__((__unused__)) UINT8 boot_state } #endif -EFI_STATUS check_kf_upgrade(void) -{ - EFI_STATUS ret; - EFI_FILE_IO_INTERFACE *io = NULL; - EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; - EFI_HANDLE esp_handle = NULL; - CHAR16 *self_path = BOOTLOADER_FILE; - CHAR16 *bak_path = BOOTLOADER_FILE_BAK; - - ret = gpt_get_partition_handle(BOOTLOADER_LABEL, LOGICAL_UNIT_USER, - &esp_handle); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to get ESP partition"); - goto out; - } - - ret = handle_protocol(esp_handle, &SimpleFileSystemProtocol, - (void **)&io); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"HandleProtocol for ESP partition failed"); - goto out; - } - - if (!uefi_exist_file_root(io, KFUPDATE_FILE)) { - debug(L"Kernelflinger upgrade file is not exist"); - goto out; - } - debug(L"Kernelflinger upgrade file is exist"); - - ret = verify_image(esp_handle, KFUPDATE_FILE); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Verify upgrade image failed"); - uefi_delete_file(io, KFUPDATE_FILE); - goto out; - } - debug(L"Success to verify the upgrade image"); - - if (g_loaded_image != NULL - && g_loaded_image->FilePath != NULL - && g_loaded_image->FilePath->Type == MEDIA_DEVICE_PATH - && g_loaded_image->FilePath->SubType == MEDIA_FILEPATH_DP) { - debug(L"Self path name: %s", ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName); - self_path = ((FILEPATH_DEVICE_PATH *)(g_loaded_image->FilePath))->PathName; - if (StriCmp(self_path, BOOTLOADER_FILE)) { - if (StriCmp(self_path, KFSELF_FILE)) { - error(L"Skip check the upgrade file"); - goto out; - } - bak_path = KFBACKUP_FILE; - } - } else { - // maybe loaded by the "fastboot boot" command, or the BIOS not support - // Use the default value - error(L"Loaded image or FilePath is NULL"); - } - - // Verify it again - if (!uefi_exist_file_root(io, self_path)) { - error(L"Can't find file %s", self_path); - ret = EFI_NOT_FOUND; - goto out; - } - - if (uefi_exist_file_root(io, bak_path)) { - ret = uefi_delete_file(io, bak_path); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to delete %s", bak_path); - goto out; - } - debug(L"Success to delete old %s", bak_path); - } - ret = uefi_rename_file(io, self_path, bak_path); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to rename the %s to %s", self_path, bak_path); - goto out; - } - debug(L"Success rename file %s to %s", self_path, bak_path); - ret = uefi_rename_file(io, KFUPDATE_FILE, self_path); - if (EFI_ERROR(ret)) { - efi_perror(ret, L"Failed to rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - goto out; - } - debug(L"Success rename the upgrade file %s to %s", KFUPDATE_FILE, self_path); - - error(L"I am about to load the new boot loader after upgrade it"); - if (g_loaded_image != NULL) - enter_efi_binary(self_path, FALSE, g_loaded_image->LoadOptionsSize, g_loaded_image->LoadOptions); - reboot(NULL, EfiResetCold); - -out: - return ret; -} - EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS ret; @@ -1408,7 +1264,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) uefi_bios_update_capsule(g_disk_device, FWUPDATE_FILE); - check_kf_upgrade(); + uefi_check_upgrade(g_loaded_image, BOOTLOADER_LABEL, KFUPDATE_FILE, + BOOTLOADER_FILE, BOOTLOADER_FILE_BAK, KFSELF_FILE, KFBACKUP_FILE); #ifdef USE_TPM if (!is_boot_device_removable()) { @@ -1518,7 +1375,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) debug(L"entering EFI binary"); if (!target_path) return EFI_INVALID_PARAMETER; - ret = enter_efi_binary(target_path, oneshot, 0, NULL); + ret = uefi_enter_binary(g_disk_device, target_path, oneshot, 0, NULL); if (EFI_ERROR(ret)) { efi_perror(ret, L"EFI Application exited abnormally"); pause(3); diff --git a/libkernelflinger/lib.c b/libkernelflinger/lib.c index 04fa3b1..a867333 100644 --- a/libkernelflinger/lib.c +++ b/libkernelflinger/lib.c @@ -1143,6 +1143,34 @@ VOID __stack_chk_fail() panic(L"stack-protector: kernelflinger stack is corrupted"); } +INTN StrcaseCmp(CHAR16 *s1, CHAR16 *s2) +{ + CHAR16 *p1 = s1; + CHAR16 *p2 = s2; + CHAR16 c1, c2; + + if (s1 == NULL) + return (s2 == NULL) ? 0 : -1; + if (s2 == NULL) + return 1; + + while (*p1 != 0) { + c1 = *p1; + if (c1 >= L'A' && c1 <= L'Z') + c1 += L'a' - L'A'; + c2 = *p2; + if (c2 >= L'A' && c2 <= L'Z') + c2 += L'a' - L'A'; + if (c1 > c2) + return 1; + if (c1 < c2) + return -1; + p1++; + p2++; + } + + return (*p2 == 0) ? 0 : -1; +} + /* vim: softtabstop=8:shiftwidth=8:expandtab */ - diff --git a/libkernelflinger/uefi_utils.c b/libkernelflinger/uefi_utils.c index 42f2f14..b1cf97c 100644 --- a/libkernelflinger/uefi_utils.c +++ b/libkernelflinger/uefi_utils.c @@ -38,6 +38,7 @@ #include #include "protocol.h" #include "uefi_utils.h" +#include "options.h" /* GUID for ESP partition on gmin */ const EFI_GUID esp_ptn_guid = { 0x2568845d, 0x2332, 0x4675, @@ -498,3 +499,181 @@ EFI_STATUS uefi_bios_update_capsule(EFI_HANDLE root_dir, CHAR16 *name) return ret; } + +/* Chainload another EFI application on the ESP with the specified path, + * optionally deleting the file before entering + */ +EFI_STATUS uefi_enter_binary(EFI_HANDLE part_handle, CHAR16 *path, + BOOLEAN delete, UINT32 load_options_size, VOID *load_options) +{ + EFI_DEVICE_PATH *edp; + EFI_STATUS ret; + EFI_HANDLE image; + EFI_LOADED_IMAGE *loaded_image; + + edp = FileDevicePath(part_handle, path); + if (!edp) { + error(L"Couldn't generate a path"); + return EFI_INVALID_PARAMETER; + } + + ret = uefi_call_wrapper(BS->LoadImage, 6, FALSE, g_parent_image, + edp, NULL, 0, &image); + FreePool(edp); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"BS->LoadImage '%s'", path); + return ret; + } + if (delete) { + ret = file_delete(part_handle, path); + if (EFI_ERROR(ret)) + efi_perror(ret, L"Couldn't delete %s", path); + } + if (load_options_size > 0) { + // Set the command line option + ret = uefi_call_wrapper(BS->OpenProtocol, 6, image, + &LoadedImageProtocol, (VOID **)&loaded_image, + image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"OpenProtocol: LoadedImageProtocol"); + goto out; + } + if (loaded_image == NULL) { + error(L"LoadedImageProtocol, but return image is NULL"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + loaded_image->LoadOptionsSize = load_options_size; + loaded_image->LoadOptions = load_options; + } + ret = uefi_call_wrapper(BS->StartImage, 3, image, NULL, NULL); + +out: + uefi_call_wrapper(BS->UnloadImage, 1, image); + + return ret; +} + +EFI_STATUS uefi_check_upgrade(EFI_LOADED_IMAGE *loaded_image, + CHAR16 *partition, CHAR16 *upgrade_file, + CHAR16 *self_path1, CHAR16 *bak_path1, CHAR16 *self_path2, CHAR16 *bak_path2) +{ + EFI_STATUS ret; + EFI_FILE_IO_INTERFACE *io = NULL; + EFI_GUID SimpleFileSystemProtocol = SIMPLE_FILE_SYSTEM_PROTOCOL; + EFI_HANDLE part_handle = NULL; + CHAR16 *self_path = NULL; + UINTN self_path_len; + CHAR16 efi_full_path[512]; + CHAR16 *bak_path = NULL; + UINTN argc; + CHAR16 **argv; + + if (loaded_image == NULL + || loaded_image->FilePath == NULL + || loaded_image->FilePath->Type != MEDIA_DEVICE_PATH + || loaded_image->FilePath->SubType != MEDIA_FILEPATH_DP) { + // maybe loaded by the "fastboot boot" command, or the BIOS not support + debug(L"Loaded image or FilePath is NULL"); + return EFI_INVALID_PARAMETER; + } + + self_path = ((FILEPATH_DEVICE_PATH *)(loaded_image->FilePath))->PathName; + ret = get_argv(loaded_image, &argc, &argv); + if (EFI_ERROR(ret)) + goto out; + if (argc > 0 && argv[0][0] != L'-') { + // If load from EFI shell, then the loaded_image->FilePath is the working directory of shell, + // and argv[0] is the efi application path. + // If load from BIOS boot manager, or other EFI application, then the loaded_image->FilePath + // is the full path of efi application path. + self_path_len = StrLen(self_path); + if (self_path_len > 0) { + // Build the full path of efi application path. + if (self_path[self_path_len - 1] == L'\\') { + // Loaded from EFI shell root directory, ended with '\'. + SPrint(efi_full_path, sizeof(efi_full_path), L"%s%s", self_path, argv[0]); + self_path = efi_full_path; + } else if (self_path_len <= 4 || StrcaseCmp(self_path + self_path_len - 4, L".EFI")) { + // Loaded from EFI shell and not root directory, need add '\'. + SPrint(efi_full_path, sizeof(efi_full_path), L"%s\\%s", self_path, argv[0]); + self_path = efi_full_path; + } + } else + self_path = argv[0]; + } + FreePool(argv); + debug(L"EFI path: %s", self_path); + + if (!StrcaseCmp(self_path, self_path1)) + bak_path = bak_path1; + else if (!StrcaseCmp(self_path, self_path2)) + bak_path = bak_path2; + else { + debug(L"Unsupported running path for check upgrade"); + goto out; + } + + ret = gpt_get_partition_handle(partition, LOGICAL_UNIT_USER, &part_handle); + if (EFI_ERROR(ret)) { + if (ret != EFI_NOT_FOUND) + efi_perror(ret, L"Failed to find partition %s", partition); + goto out; + } + + ret = handle_protocol(part_handle, &SimpleFileSystemProtocol, (void **)&io); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"HandleProtocol for FAT in partition %s failed", partition); + goto out; + } + + if (!uefi_exist_file_root(io, upgrade_file)) { + debug(L"Upgrade file %s is not exist", upgrade_file); + goto out; + } + debug(L"Upgrade file %s is exist", upgrade_file); + + ret = verify_image(part_handle, upgrade_file); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Verify upgrade image failed"); + uefi_delete_file(io, upgrade_file); + goto out; + } + debug(L"Success to verify the upgrade image"); + + // Verify it again + if (!uefi_exist_file_root(io, self_path)) { + error(L"Can't find file %s", self_path); + ret = EFI_NOT_FOUND; + goto out; + } + + if (uefi_exist_file_root(io, bak_path)) { + ret = uefi_delete_file(io, bak_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to delete %s", bak_path); + goto out; + } + debug(L"Success to delete old %s", bak_path); + } + ret = uefi_rename_file(io, self_path, bak_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the %s to %s", self_path, bak_path); + goto out; + } + debug(L"Success rename file %s to %s", self_path, bak_path); + ret = uefi_rename_file(io, upgrade_file, self_path); + if (EFI_ERROR(ret)) { + efi_perror(ret, L"Failed to rename the upgrade file %s to %s", upgrade_file, self_path); + goto out; + } + debug(L"Success rename the upgrade file %s to %s", upgrade_file, self_path); + + error(L"I am about to load the new boot loader after upgrade it"); + if (loaded_image != NULL) + uefi_enter_binary(part_handle, self_path, FALSE, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + reboot(NULL, EfiResetCold); + +out: + return ret; +}