[Solved]Problem chainloading windows bootloader through code

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
sleepy762
Posts: 6
Joined: Wed Aug 11, 2021 4:58 am

[Solved]Problem chainloading windows bootloader through code

Post by sleepy762 »

I'm making a UEFI bootloader and I want one of the features to be being able to boot Windows, however it doesn't work.

(Might be worth mentioning, I'm using POSIX-UEFI and not GNU-EFI)

I've removed error handling code from the code snippets.
This snippet is to give some context. In the next code snippet is where I have an error.

Code: Select all

    // efi_file_handle_t* rootDir = .....; // Was initialized earlier
    // Opening the bootloader file
    efi_file_handle_t* winBootMgrHandle = NULL;
    uint16_t path[] = u"EFI\\Microsoft\\Boot\\bootmgfw.efi";
    rootDir->Open(rootDir, &winBootMgrHandle, path, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY);

    // Getting file information in order to create a buffer of the right size
    efi_guid_t infoGuid = EFI_FILE_INFO_GUID;
    efi_file_info_t fileInfo;
    uintn_t infoSize = sizeof(fileInfo);
    winBootMgrHandle->GetInfo(winBootMgrHandle, &infoGuid, &infoSize, &fileInfo);
    
    // Reading the file into a buffer
    uintn_t winBootMgrSize = fileInfo.FileSize;
    char* winBootMgrData = (char*)malloc(winBootMgrSize + 1);
    winBootMgrData[winBootMgrSize] = 0;
    winBootMgrHandle->Read(winBootMgrHandle, &winBootMgrSize, winBootMgrData);
Here I use the UEFI functions LoadImage() and StartImage() in order to start the bootloader image but StartImage() fails and returns EFI_INVALID_PARAMETER.
IM is the image handle which was passed to the main function of my bootloader.
LIP is the loaded image protocol of IM.
BS is boot services.

Code: Select all

    efi_handle_t imgHandle;
    BS->LoadImage(0, IM, LIP->FilePath, winBootMgrData, winBootMgrSize, &imgHandle);
    BS->StartImage(imgHandle, NULL, NULL);  // Invalid parameter error here
I don't know what's causing this error, I've tried loading a test efi file(which just prints a string to the screen) this way and it works perfectly fine, but the windows bootloader fails to load this way.
Last edited by sleepy762 on Sat Aug 14, 2021 2:42 am, edited 1 time in total.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: Problem chainloading windows bootloader through code

Post by Octocontrabass »

StartImage() isn't failing, the Windows boot manager is.

You need to pass the correct device path to LoadImage(). Right now you're passing your bootloader's device path instead of the bootmgfw.efi device path.

I don't know if there's anything else you need to change to make it work.
sleepy762
Posts: 6
Joined: Wed Aug 11, 2021 4:58 am

Re: Problem chainloading windows bootloader through code

Post by sleepy762 »

That sounds like it could be the cause, but I just can't figure out how to get the DevicePath to the bootmgfw.efi file.
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Problem chainloading windows bootloader through code

Post by nexos »

If you obtain the device path of the simple file system protocol, then you should be good. Here is an example (EDK2)

Code: Select all

// Locate the handle buffer, first obtain the proper size
    EFI_GUID sfsguid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
    EFI_STATUS status = gBS->LocateHandle(ByProtocol, &sfsguid, NULL, &handlebufsz, handles);
    // This must return EFI_BUFFER_TOO_SMALL
    if(status != EFI_BUFFER_TOO_SMALL)
        efi_panicearly(L"unable to locate simple file system protocol handles\r\n");
    // Now allocate the buffer
    handles = (EFI_HANDLE*)efi_alloc(handlebufsz);
    if(!handles)
        efi_panicearly(L"out of memory\r\n");
    // Attempt to open up the handle buffer now
    status = gBS->LocateHandle(ByProtocol, &sfsguid, NULL, &handlebufsz, handles);
    if(EFI_ERROR(status))
        efi_panicearly(L"unable to locate simple file system protocol handles\r\n");
    // Now we must loop through every handle returned, and open it up
    UINTN numhandles = handlebufsz / sizeof(EFI_HANDLE);
    // Prepare an SFS protocol and device path protocol
    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* sfsprot = NULL;
    EFI_DEVICE_PATH_PROTOCOL* devpath = NULL;
    EFI_GUID devguid = EFI_DEVICE_PATH_PROTOCOL_GUID;
    for(int i = 0; i < numhandles; ++i)
    {
        // Attempt to open up the SFS protocol
        EFI_HANDLE handle = handles[i];
        status = gBS->HandleProtocol(handle, &sfsguid, (VOID**)&sfsprot);
        // Check for an error
        if(EFI_ERROR(status))
        {
            if(i + 1 == numhandles)
                efi_panicearly(L"unable to obtain simple file system protocol\r\n");
            continue;
        }
        // Attempt to grab a device path
        status = gBS->HandleProtocol(handle, &devguid, (VOID**)&devpath);
        // Check for an error
        if(EFI_ERROR(status))
        {
            if(i + 1 == numhandles)
                efi_panicearly(L"unable to obtain simple file system protocol device path\r\n");
            continue;
        }
        else
            break;
    }

    // We found an SFS protocol and a device path for it. Note that this assumes there is only one FAT volume on the machine,
    // and that it is the ESP. If these assumptions are untrue... Problems
This code isn't perfect, and has some problems. I'll say its licensed under the CC0 license. Basically, if you didn't know, every protocol in UEFI can have handles attached to it. The boot service LocateHandle() is used to obtain the handle database. Every handle can have multiple protocols attached to it. This code finds the first valid one. This code was tested on my loader, though I don't know if Windows Boot Manager would work. You would use sfsprot->OpenVolume() to obtain the root directory after this code, and devpath would be passed to LoadImage. It loaded up GRUB just fine. Let me reboot and test Windows Boot manager and I'll update here
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Problem chainloading windows bootloader through code

Post by nexos »

Well, after disabling secure boot, and doing lots of file copies, it managed to load up Windows, but it did crash in boot manager with "file signature could not be verified". Not sure if this is because I renamed bootmgfw.efi, or maybe because secure boot was disabled. Tell me if you have issues
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
sleepy762
Posts: 6
Joined: Wed Aug 11, 2021 4:58 am

Re: Problem chainloading windows bootloader through code

Post by sleepy762 »

This works! Thanks a lot!

You mentioned that your code assumes that there is only one FAT volume on the machine.
If there is more than one, what should I do in order to make sure that I'm accessing the volume I want?
nexos
Member
Member
Posts: 1081
Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos

Re: Problem chainloading windows bootloader through code

Post by nexos »

Not easily :) . You would have to use EFI_LOAD_FILE_PROTOCOL, passing in the device path to the desired partition. That would be very hard, as you have to get the disk part of it, and then figure out the partition number and append it to the device path. Or, you could use the SFS protocol to check for a certain file in the root directory that identifies the volume.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
Post Reply