/** * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtldr/protocol.c * DESCRIPTION: XT Boot Loader protocol support * DEVELOPERS: Rafal Kupiec */ #include /** * Closes a protocol on a provided handle. * * @param Handle * Supplies a handle for the protocol interface that was previously opened. * * @param ProtocolGuid * Supplies a unique protocol GUID. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlCloseProtocol(IN PEFI_HANDLE Handle, IN PEFI_GUID ProtocolGuid) { return EfiSystemTable->BootServices->CloseProtocol(Handle, ProtocolGuid, EfiImageHandle, NULL); } /** * Finds a boot protocol for specified system type. * * @param SystemType * Specifies the system type to search for. * * @param BootProtocolGuid * Receives the GUID of the registered boot protocol, that supports specified system. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlFindBootProtocol(IN PWCHAR SystemType, OUT PEFI_GUID BootProtocolGuid) { PXTBL_KNOWN_BOOT_PROTOCOL ProtocolEntry; PLIST_ENTRY ProtocolListEntry; ProtocolListEntry = BlpBootProtocols.Flink; while(ProtocolListEntry != &BlpBootProtocols) { /* Get boot protocol entry */ ProtocolEntry = CONTAIN_RECORD(ProtocolListEntry, XTBL_KNOWN_BOOT_PROTOCOL, Flink); /* Check if this boot protocol supports specified system */ if(RtlCompareWideStringInsensitive(ProtocolEntry->SystemType, SystemType, 0) == 0) { /* Boot protocol matched, return success */ *BootProtocolGuid = ProtocolEntry->Guid; return STATUS_EFI_SUCCESS; } /* Move to the next registered boot protocol */ ProtocolListEntry = ProtocolListEntry->Flink; } /* Boot protocol not found, return error */ return STATUS_EFI_NOT_FOUND; } /** * Returns a linked list of all loaded modules. * * @return This routine returns a pointer to a linked list of all loaded modules. * * @since XT 1.0 * * @todo This is a temporary solution and it should be replaced by a complex API allowing to map modules. */ XTCDECL PLIST_ENTRY BlGetModulesList() { /* Return a pointer to a list of all loaded modules */ return &BlpLoadedModules; } /** * Installs XTLDR protocol interface. * * @param Guid * Specifies a unique protocol GUID. * * @param Interface * Supplies a pointer to the protocol interface, or NULL if there is no structure associated. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlInstallProtocol(IN PVOID Interface, IN PEFI_GUID Guid) { EFI_HANDLE Handle = NULL; /* Install protocol interface */ return EfiSystemTable->BootServices->InstallProtocolInterface(&Handle, Guid, EFI_NATIVE_INTERFACE, Interface); } /** * Loads a specified XTLDR module from disk. * * @param ModuleName * Specifies the name of the module to load. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlLoadModule(IN PWCHAR ModuleName) { EFI_GUID LIPGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID; EFI_MEMMAP_DEVICE_PATH ModuleDevicePath[2]; PEFI_LOADED_IMAGE_PROTOCOL LoadedImage; PEFI_FILE_HANDLE DirHandle, FsHandle; EFI_HANDLE DiskHandle, ModuleHandle; PPECOFF_IMAGE_SECTION_HEADER SectionHeader; PPECOFF_IMAGE_DOS_HEADER DosHeader; PPECOFF_IMAGE_PE_HEADER PeHeader; PXTBL_MODULE_DEPS ModuleDependencies; PXTBL_MODULE_INFO ModuleInfo; PLIST_ENTRY ModuleListEntry; WCHAR ModuleFileName[24]; USHORT SectionIndex; SIZE_T ModuleSize; EFI_STATUS Status; PVOID ModuleData; PWCHAR DepsData; ModuleListEntry = BlpLoadedModules.Flink; while(ModuleListEntry != &BlpLoadedModules) { /* Get module information */ ModuleInfo = CONTAIN_RECORD(ModuleListEntry, XTBL_MODULE_INFO, Flink); if(RtlCompareWideStringInsensitive(ModuleInfo->ModuleName, ModuleName, 0) == 0) { /* Module already loaded */ BlDebugPrint(L"WARNING: Module '%S' already loaded!\n", ModuleName); return STATUS_EFI_SUCCESS; } /* Move to the module */ ModuleListEntry = ModuleListEntry->Flink; } /* Print debug message */ BlDebugPrint(L"Loading module '%S' ...\n", ModuleName); /* Set module path */ RtlCopyMemory(ModuleFileName, ModuleName, sizeof(ModuleFileName) / sizeof(WCHAR)); RtlConcatenateWideString(ModuleFileName, L".EFI", 0); /* Open EFI volume */ Status = BlOpenVolume(NULL, &DiskHandle, &FsHandle); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open a volume */ return Status; } /* Open XTLDR modules common directory */ Status = FsHandle->Open(FsHandle, &DirHandle, XTBL_MODULES_DIRECTORY_PATH, EFI_FILE_MODE_READ, 0); if(Status != STATUS_EFI_SUCCESS) { /* Modules directory not found, attempt to open XTLDR architecture specific modules directory */ Status = FsHandle->Open(FsHandle, &DirHandle, XTBL_ARCH_MODULES_DIRECTORY_PATH, EFI_FILE_MODE_READ, 0); } /* Close FS handle */ FsHandle->Close(FsHandle); /* Check if modules directory opened successfully */ if(Status != STATUS_EFI_SUCCESS) { /* Failed to open directory */ BlCloseVolume(DiskHandle); return Status; } /* Read module file from disk and close directory and EFI volume */ Status = BlReadFile(DirHandle, ModuleFileName, &ModuleData, &ModuleSize); DirHandle->Close(DirHandle); BlCloseVolume(DiskHandle); /* Make sure module file was read successfully */ if(Status != STATUS_EFI_SUCCESS) { /* Failed to read file */ return Status; } /* Allocate memory for module information block */ Status = BlAllocateMemoryPool(sizeof(XTBL_MODULE_INFO), (PVOID*)&ModuleInfo); if(Status != STATUS_EFI_SUCCESS) { /* Failed to allocate memory */ return Status; } /* Zero module information block and initialize dependencies list */ RtlZeroMemory(ModuleInfo, sizeof(XTBL_MODULE_INFO)); RtlInitializeListHead(&ModuleInfo->Dependencies); /* Setup PE/COFF EFI image headers */ DosHeader = (PPECOFF_IMAGE_DOS_HEADER)ModuleData; PeHeader = (PPECOFF_IMAGE_PE_HEADER)(ModuleData + DosHeader->PeHeaderOffset); SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&PeHeader->OptionalHeader + PeHeader->FileHeader.SizeOfOptionalHeader); /* Look for .moddeps and .modinfo sections */ for(SectionIndex = 0; SectionIndex < PeHeader->FileHeader.NumberOfSections; SectionIndex++) { /* Check section name */ if(RtlCompareString((PCHAR)SectionHeader[SectionIndex].Name, ".moddeps", 8) == 0) { /* Store address of .moddeps data segment */ DepsData = ModuleData + SectionHeader[SectionIndex].PointerToRawData; /* Iterate over all dependencies stored */ while(*DepsData != 0) { /* Load dependency module */ BlDebugPrint(L"Module '%S' requires '%S' ...\n", ModuleName, DepsData); Status = BlLoadModule(DepsData); if(Status != STATUS_EFI_SUCCESS) { /* Failed to load module, print error message and return status code */ BlDebugPrint(L"Failed to load dependency module '%S', (Status Code: 0x%lx)\n", DepsData, Status); return STATUS_EFI_UNSUPPORTED; } /* Allocate memory for module dependency */ Status = BlAllocateMemoryPool(sizeof(XTBL_MODULE_DEPS), (PVOID*)&ModuleDependencies); if(Status == STATUS_EFI_SUCCESS) { /* Memory allocated successfully, store module's dependency */ ModuleDependencies->ModuleName = DepsData; RtlInsertTailList(&ModuleInfo->Dependencies, &ModuleDependencies->Flink); } /* Get next dependency module name */ DepsData += 9; } } else if(RtlCompareString((PCHAR)SectionHeader[SectionIndex].Name, ".modinfo", 8) == 0) { /* Store module description */ ModuleInfo->ModuleDescription = ModuleData + SectionHeader[SectionIndex].PointerToRawData; } } /* Finally, store module name */ ModuleInfo->ModuleName = ModuleName; /* Setup module device path */ ModuleDevicePath[0].Header.Length[0] = sizeof(EFI_MEMMAP_DEVICE_PATH); ModuleDevicePath[0].Header.Length[1] = sizeof(EFI_MEMMAP_DEVICE_PATH) >> 8; ModuleDevicePath[0].Header.Type = EFI_HARDWARE_DEVICE_PATH; ModuleDevicePath[0].Header.SubType = EFI_HARDWARE_MEMMAP_DP; ModuleDevicePath[0].MemoryType = EfiLoaderData; ModuleDevicePath[0].StartingAddress = (UINT_PTR)ModuleData; ModuleDevicePath[0].EndingAddress = (UINT_PTR)ModuleData + ModuleSize; ModuleDevicePath[1].Header.Length[0] = sizeof(EFI_DEVICE_PATH_PROTOCOL); ModuleDevicePath[1].Header.Length[1] = sizeof(EFI_DEVICE_PATH_PROTOCOL) >> 8; ModuleDevicePath[1].Header.Type = EFI_END_DEVICE_PATH; ModuleDevicePath[1].Header.SubType = EFI_END_ENTIRE_DP; /* Load EFI image */ Status = BlLoadEfiImage((PEFI_DEVICE_PATH_PROTOCOL)ModuleDevicePath, ModuleData, ModuleSize, &ModuleHandle); if(Status != STATUS_EFI_SUCCESS) { /* Check if caused by secure boot */ if(Status == STATUS_EFI_ACCESS_DENIED && BlpStatus.SecureBoot >= 1) { /* SecureBoot signature validation failed */ BlDebugPrint(L"ERROR: SecureBoot signature validation failed, module '%S' will not be loaded\n", ModuleName); } else { /* Failed to load module */ BlDebugPrint(L"ERROR: Unable to load module '%S' (Status Code: 0x%lx)\n", ModuleName, Status); } /* Return error status code */ return Status; } /* Access module interface for further module type check */ Status = EfiSystemTable->BootServices->OpenProtocol(ModuleHandle, &LIPGuid, (PVOID *)&LoadedImage, EfiImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if(Status != STATUS_EFI_SUCCESS) { /* Failed to open LoadedImage protocol */ BlDebugPrint(L"ERROR: Unable to access module interface (Status Code: 0x%lx)\n", Status); return Status; } /* Some firmwares do not allow to start drivers which are not of 'boot system driver' type, so check it */ if(LoadedImage->ImageCodeType != EfiBootServicesCode) { /* Different type set, probably 'runtime driver', refuse to load it */ BlDebugPrint(L"ERROR: Loaded module is not a boot system driver\n"); /* Close protocol and skip module */ EfiSystemTable->BootServices->CloseProtocol(LoadedImage, &LIPGuid, LoadedImage, NULL); } /* Save module information */ ModuleInfo->ModuleBase = LoadedImage->ImageBase; ModuleInfo->ModuleSize = LoadedImage->ImageSize; ModuleInfo->Revision = LoadedImage->Revision; ModuleInfo->UnloadModule = LoadedImage->Unload; /* Close loaded image protocol */ EfiSystemTable->BootServices->CloseProtocol(LoadedImage, &LIPGuid, LoadedImage, NULL); /* Start EFI image */ Status = BlStartEfiImage(ModuleHandle); if(Status != STATUS_EFI_SUCCESS) { /* Failed to start module image */ BlDebugPrint(L"ERROR: Failed to start module '%S' (Status Code: 0x%lx)\n", ModuleName, Status); return Status; } /* Add module to the list of loaded modules */ RtlInsertTailList(&BlpLoadedModules, &ModuleInfo->Flink); /* Return success */ return STATUS_EFI_SUCCESS; } /** * Helper routine to load all modules supplied in the configuration file. * * @param ModulesList * Supplies a space separated list of XTLDR modules to load (mostly read from configuration file). * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlLoadModules(IN PWCHAR ModulesList) { PWCHAR LastModule, Module; EFI_STATUS ReturnStatus, Status; /* Set default return value */ ReturnStatus = STATUS_EFI_SUCCESS; if(ModulesList != NULL) { /* Tokenize provided list of modules */ Module = RtlTokenizeWideString(ModulesList, L" ", &LastModule); /* Iterate over all arguments passed to boot loader */ while(Module != NULL) { Status = BlLoadModule(Module); if(Status != STATUS_EFI_SUCCESS) { /* Failed to load module, print error message and set new return value */ BlDebugPrint(L"ERROR: Failed to load module '%S', (Status Code: 0x%lx)\n", Module, Status); ReturnStatus = STATUS_EFI_LOAD_ERROR; } /* Take next module from the list */ Module = RtlTokenizeWideString(NULL, L" ", &LastModule); } } /* Return success */ return ReturnStatus; } /** * Returns an array of handles that support the requested protocol. * * @param Handles * Supplies the address where a pointer to all handles found for the protocol interface. * * @param Count * Provides a number of the returned handles. * * @param ProtocolGuid * Supplies a pointer to the unique protocol GUID. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlLocateProtocolHandles(OUT PEFI_HANDLE *Handles, OUT PUINT_PTR Count, IN PEFI_GUID ProtocolGuid) { return EfiSystemTable->BootServices->LocateHandleBuffer(ByProtocol, ProtocolGuid, NULL, Count, Handles); } /** * Locates and opens the requested XT Boot Loader or EFI protocol. * * @param Handle * Supplies the address where a pointer to the handle for the protocol interface. * * @param ProtocolHandler * Supplies the address where a pointer to the opened protocol is returned. * * @param ProtocolGuid * Supplies a pointer to the unique protocol GUID. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlOpenProtocol(OUT PEFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid) { PEFI_HANDLE Handles = NULL; EFI_STATUS Status; UINT_PTR Count; UINT Index; /* Try to locate the handles */ Status = BlLocateProtocolHandles(&Handles, &Count, ProtocolGuid); if(Status != STATUS_EFI_SUCCESS) { /* Unable to get handles */ return Status; } /* Check if any handles returned */ if(Count > 0) { /* Iterate through all given handles */ for(Index = 0; Index < Count; Index++) { /* Try to open protocol */ Status = BlOpenProtocolHandle(Handles[Index], ProtocolHandler, ProtocolGuid); /* Check if successfully opened the loader protocol */ if(Status == STATUS_EFI_SUCCESS) { /* Protocol found and successfully opened */ *Handle = Handles[Index]; break; } } } /* Free handles */ EfiSystemTable->BootServices->FreePool(Handles); /* Make sure the loaded protocol has been found */ if(*ProtocolHandler == NULL) { /* Protocol not found */ return STATUS_EFI_NOT_FOUND; } /* Return success */ return STATUS_EFI_SUCCESS; } /** * Opens the requested XT Boot Loader or EFI protocol, if it is supported by the handle. * * @param Handle * Supplies a handle for the protocol interface that is being opened. * * @param ProtocolHandler * Supplies the address where a pointer to the opened protocol is returned. * * @param ProtocolGuid * Supplies a pointer to the unique protocol GUID. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlOpenProtocolHandle(IN EFI_HANDLE Handle, OUT PVOID *ProtocolHandler, IN PEFI_GUID ProtocolGuid) { return EfiSystemTable->BootServices->OpenProtocol(Handle, ProtocolGuid, ProtocolHandler, EfiImageHandle, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); } /** * Registers a boot menu callback routine, that will be used to display alternative boot menu. * * @param BootMenuRoutine * Supplies a pointer to the boot menu callback routine. * * @return This routine does not return any value. * * @since XT 1.0 */ XTCDECL VOID BlRegisterBootMenu(IN PVOID BootMenuRoutine) { /* Set boot menu routine */ BlpStatus.BootMenu = BootMenuRoutine; } /** * Registers a known boot protocol for a specified OS. * * @param SystemType * Supplies the type of the OS, such as "LINUX", "XTOS", etc. that is supported by the boot protocol. * * @param BootProtocolGuid * Supplies a pointer to the unique protocol GUID. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlRegisterBootProtocol(IN PWCHAR SystemType, IN PEFI_GUID BootProtocolGuid) { PXTBL_KNOWN_BOOT_PROTOCOL ProtocolEntry; PLIST_ENTRY ProtocolListEntry; EFI_STATUS Status; ProtocolListEntry = BlpBootProtocols.Flink; while(ProtocolListEntry != &BlpBootProtocols) { /* Get boot protocol entry */ ProtocolEntry = CONTAIN_RECORD(ProtocolListEntry, XTBL_KNOWN_BOOT_PROTOCOL, Flink); /* Check if boot protocol already registered for specified system */ if(RtlCompareWideStringInsensitive(ProtocolEntry->SystemType, SystemType, 0) == 0) { /* Boot protocol already registered */ return STATUS_EFI_ABORTED; } /* Move to the next registered boot protocol */ ProtocolListEntry = ProtocolListEntry->Flink; } /* Create new boot protocol entry */ Status = BlAllocateMemoryPool(sizeof(XTBL_BOOT_PROTOCOL), (PVOID *)&ProtocolEntry); if(Status != STATUS_EFI_SUCCESS) { /* Memory allocation failure */ return STATUS_EFI_OUT_OF_RESOURCES; } /* Set protocol properties and add it to the list */ ProtocolEntry->SystemType = SystemType; ProtocolEntry->Guid = *BootProtocolGuid; RtlInsertTailList(&BlpBootProtocols, &ProtocolEntry->Flink); /* Return success */ return STATUS_EFI_SUCCESS; } /** * This routine installs XTLDR protocol for further usage by modules. * * @return This routine returns a status code. * * @since XT 1.0 */ XTCDECL EFI_STATUS BlpInstallXtLoaderProtocol() { EFI_GUID Guid = XT_BOOT_LOADER_PROTOCOL_GUID; /* Set all routines available via loader protocol */ BlpLdrProtocol.Boot.FindProtocol = BlFindBootProtocol; BlpLdrProtocol.Boot.InitializeMenuList = BlInitializeBootMenuList; BlpLdrProtocol.Boot.InvokeProtocol = BlInvokeBootProtocol; BlpLdrProtocol.Boot.RegisterMenu = BlRegisterBootMenu; BlpLdrProtocol.Boot.RegisterProtocol = BlRegisterBootProtocol; BlpLdrProtocol.Config.GetValue = BlGetConfigValue; BlpLdrProtocol.Console.ClearLine = BlClearConsoleLine; BlpLdrProtocol.Console.ClearScreen = BlClearConsoleScreen; BlpLdrProtocol.Console.DisableCursor = BlDisableConsoleCursor; BlpLdrProtocol.Console.EnableCursor = BlEnableConsoleCursor; BlpLdrProtocol.Console.Print = BlConsolePrint; BlpLdrProtocol.Console.QueryMode = BlQueryConsoleMode; BlpLdrProtocol.Console.ReadKeyStroke = BlReadKeyStroke; BlpLdrProtocol.Console.ResetInputBuffer = BlResetConsoleInputBuffer; BlpLdrProtocol.Console.SetAttributes = BlSetConsoleAttributes; BlpLdrProtocol.Console.SetCursorPosition = BlSetCursorPosition; BlpLdrProtocol.Console.Write = BlConsoleWrite; BlpLdrProtocol.Debug.Print = BlDebugPrint; BlpLdrProtocol.Disk.CloseVolume = BlCloseVolume; BlpLdrProtocol.Disk.OpenVolume = BlOpenVolume; BlpLdrProtocol.Disk.ReadFile = BlReadFile; BlpLdrProtocol.Memory.AllocatePages = BlAllocateMemoryPages; BlpLdrProtocol.Memory.AllocatePool = BlAllocateMemoryPool; BlpLdrProtocol.Memory.BuildPageMap = BlBuildPageMap; BlpLdrProtocol.Memory.CopyMemory = RtlCopyMemory; BlpLdrProtocol.Memory.FreePages = BlFreeMemoryPages; BlpLdrProtocol.Memory.FreePool = BlFreeMemoryPool; BlpLdrProtocol.Memory.GetMappingsCount = BlGetMappingsCount; BlpLdrProtocol.Memory.GetMemoryMap = BlGetMemoryMap; BlpLdrProtocol.Memory.GetVirtualAddress = BlGetVirtualAddress; BlpLdrProtocol.Memory.InitializePageMap = BlInitializePageMap; BlpLdrProtocol.Memory.MapEfiMemory = BlMapEfiMemory; BlpLdrProtocol.Memory.MapPage = BlMapPage; BlpLdrProtocol.Memory.MapVirtualMemory = BlMapVirtualMemory; BlpLdrProtocol.Memory.PhysicalAddressToVirtual = BlPhysicalAddressToVirtual; BlpLdrProtocol.Memory.PhysicalListToVirtual = BlPhysicalListToVirtual; BlpLdrProtocol.Memory.SetMemory = RtlSetMemory; BlpLdrProtocol.Memory.ZeroMemory = RtlZeroMemory; BlpLdrProtocol.Protocol.Close = BlCloseProtocol; BlpLdrProtocol.Protocol.GetModulesList = BlGetModulesList; BlpLdrProtocol.Protocol.Install = BlInstallProtocol; BlpLdrProtocol.Protocol.LocateHandles = BlLocateProtocolHandles; BlpLdrProtocol.Protocol.Open = BlOpenProtocol; BlpLdrProtocol.Protocol.OpenHandle = BlOpenProtocolHandle; BlpLdrProtocol.Tui.DisplayErrorDialog = BlDisplayErrorDialog; BlpLdrProtocol.Tui.DisplayInfoDialog = BlDisplayInfoDialog; BlpLdrProtocol.Tui.DisplayInputDialog = BlDisplayInputDialog; BlpLdrProtocol.Tui.DisplayProgressDialog = BlDisplayProgressDialog; BlpLdrProtocol.Tui.UpdateProgressBar = BlUpdateProgressBar; BlpLdrProtocol.Util.ExitBootServices = BlExitBootServices; BlpLdrProtocol.Util.GetSecureBootStatus = BlGetSecureBootStatus; BlpLdrProtocol.Util.LoadEfiImage = BlLoadEfiImage; BlpLdrProtocol.Util.RebootSystem = BlRebootSystem; BlpLdrProtocol.Util.ShutdownSystem = BlShutdownSystem; BlpLdrProtocol.Util.SleepExecution = BlSleepExecution; BlpLdrProtocol.Util.StartEfiImage = BlStartEfiImage; BlpLdrProtocol.Util.WaitForEfiEvent = BlWaitForEfiEvent; /* Register XTLDR loader protocol */ BlDebugPrint(L"Registering XT loader protocol\n"); return BlInstallProtocol(&BlpLdrProtocol, &Guid); }