Use BCDEDIT para configurar a inicialização PXE como opção de inicialização padrão

2

Eu tenho um laboratório cheio de computadores usando UEFI que eu quero sempre tentar inicializar o PXE antes de todas as outras opções de inicialização. No entanto, após a criação automática de imagens em um PC com o Windows 8.1 / Windows 10, a ordem de inicialização do UEFI é alterada (sem surpresa) pelo Windows para o Gerenciador de inicialização do Windows.

Como posso alterar programaticamente a ordem de inicialização para que a reinicialização do PXE (IPv4) seja redefinida para o padrão usando BCDEDIT (ou alguma outra ferramenta baseada no Windows)? O BCDEDIT tem um GUID bem conhecido ou semelhante para a inicialização PXE?

    
por jessicah 25.02.2016 / 10:20

3 respostas

1

Enquanto os comentários do @ nex84 sobre o BCD estar em um nível mais alto do que o menu de inicialização do BIOS estão corretos, não é estritamente assim. Em máquinas UEFI, as entradas BCD realmente agrupam o "gerenciador de inicialização" nativo do firmware e o gerenciador de inicialização do Windows.

Você pode enumerar todas as entradas usando bcdedit /enum all e isso incluirá a opção de inicialização PXE - assumindo, é claro, que já existe em sua "BIOS". Você pode então manipular a ordem de inicialização com os comandos usuais bcdedit /displayorder .

Você também pode querer usar EasyBCD para uma opção de GUI freeware. Por padrão, a versão mais recente do EasyBCD oculta entradas no nível de UEFI da exibição, mas se você ativar o "Modo de especialista" nas opções, elas ficarão disponíveis. (Divulgação: Estou com a NeoSmart Technologies, autores do EasyBCD)

Por favor, tenha muito cuidado com o bcdedit ao brincar com variáveis de inicialização UEFI. Eu pessoalmente experimentei dispositivos que foram permanentemente descartados porque eles apresentam seu aplicativo de configuração de firmware (também conhecido como configuração de BIOS) apenas como uma função deste menu de inicialização, configurando incorretamente poderia ser permanente (a menos que você tenha um programador EEPROM na mão para reflash o firmware , e você é muito útil com a solda surfacemount).

    
por 25.02.2016 / 18:41
1

Eu me encontrei com o mesmo problema e perguntei em serverfault . Demorou um bom dia de google-stumbling antes de eu juntar o suficiente para resolvê-lo. Aqui vai:

  1. No Linux, isso seria bastante simples, via efibootmgr
  2. O EasyUEFI me deixaria fazer o que eu quero também - o suporte à linha de comando requer uma licença relativamente barata; mas não me sinto bem dependendo de uma ferramenta de nicho como essa, especialmente se houver outras opções.
  3. bcdedit em uma máquina UEFI modifica as configurações de UEFI . Eu acho que funcionaria.
  4. A especificação da UEFI para a ordem de inicialização não é muito complicada. A API é realmente apenas GetVariable / SetVariable com variáveis chamadas BootOrder (para obter / definir a lista de opções de inicialização na ordem em que serão tentadas) e Boot #### (para obter / definir informações sobre cada opção de inicialização). / li>
  5. Eu não tenho ideia de como eu escreveria um aplicativo do Windows contra a API da UEFI no Windows (alguém?)
  6. O Windows fornece uma API que, entre outras coisas , envolve o GetVariable / SetVariable do UEFI.

Depois que entendi a especificação UEFI para a ordem de inicialização e a API do Windows, o código (C ++, construído para 64 bits, já que é tudo o que estamos usando) não foi tão ruim. Isso precisa ser construído em um exe que requer privilégios administrativos e vincula estaticamente o tempo de execução do Windows e, em seguida, eu o executo no MDT após o sistema operacional ser instalado antes da reinicialização.

Primeiro, você precisa reivindicar um privilégio para chamar a API. Use um pequeno ajudante:

struct CloseHandleHelper
{
    void operator()(void *p) const
    {
        CloseHandle(p);
    }
};

BOOL SetPrivilege(HANDLE process, LPCWSTR name, BOOL on)
{
    HANDLE token;
    if (!OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES, &token))
        return FALSE;
    std::unique_ptr<void, CloseHandleHelper> tokenLifetime(token);
    TOKEN_PRIVILEGES tp;
    tp.PrivilegeCount = 1;
    if (!LookupPrivilegeValueW(NULL, name, &tp.Privileges[0].Luid))
        return FALSE;
    tp.Privileges[0].Attributes = on ? SE_PRIVILEGE_ENABLED : 0;
    return AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL);
}

ligue para

SetPrivilege(GetCurrentProcess(), SE_SYSTEM_ENVIRONMENT_NAME, TRUE));

Em seguida, obtenha a lista de opções de inicialização (uma concatenação de valores uint16_t):

const int BUFFER_SIZE = 4096;
BYTE bootOrderBuffer[BUFFER_SIZE];
DWORD bootOrderLength = 0;
const TCHAR bootOrderName[] = TEXT("BootOrder");
const TCHAR globalGuid[] = TEXT("{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}");
DWORD bootOrderAttributes;
bootOrderLength = GetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, BUFFER_SIZE, &bootOrderAttributes);
if (bootOrderLength == 0)
{
    std::cout << "Failed getting BootOrder with error " << GetLastError() << std::endl;
    return 1;
}

Você pode, então, iterar sobre cada opção de inicialização, formar o nome da variável Boot #### para ela e usá-la para obter uma estrutura com informações sobre a opção. Você vai querer ver se a primeira opção ativa tem "Descrição" igual a "Windows Boot Manager". Descrição é uma cadeia de caracteres larga terminada por caractere nulo no deslocamento 6 na estrutura.

for (DWORD i = 0; i < bootOrderLength; i += 2)
{
    std::wstringstream bootOptionNameBuilder;
    bootOptionNameBuilder << "Boot" << std::uppercase << std::setfill(L'0') << std::setw(4) << std::hex << *reinterpret_cast<uint16_t*>(bootOrderBuffer + i);
    std::wstring bootOptionName(bootOptionNameBuilder.str());
    BYTE bootOptionInfoBuffer[BUFFER_SIZE];
    DWORD bootOptionInfoLength = GetFirmwareEnvironmentVariableEx(bootOptionName.c_str(), globalGuid, bootOptionInfoBuffer, BUFFER_SIZE, nullptr);
    if (bootOptionInfoLength == 0)
    {
        std::cout << "Failed getting option info for option at offset " << i << std::endl;
        return 1;
    }
    uint32_t* bootOptionInfoAttributes = reinterpret_cast<uint32_t*>(bootOptionInfoBuffer);
    //First 4 bytes make a uint32_t comprised of flags. 0x1 means the boot option is active (not disabled)
    if (((*bootOptionInfoAttributes) & 0x1) != 0)
    {
        std::wstring description(reinterpret_cast<wchar_t*>(bootOptionInfoBuffer + sizeof(uint32_t) + sizeof(uint16_t)));
        bool isWBM = boost::algorithm::to_upper_copy<std::wstring>(description) == L"WINDOWS BOOT MANAGER";
        // details - keep track of the value of i for the first WBM and non-WBM options you find, and the fact that you found them
    }
}

Agora, se você encontrou opções de inicialização WBM e não-WBM ativas e a primeira opção WBM está em wbmOffset, e a primeira opção não-WBM está em nonWBMOffset, com wbmOffset < nonWBMOffset, troque as entradas na variável BootOrder com o seguinte:

    uint16_t *wbmBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + wbmOffset);
    uint16_t *nonWBMBootOrderEntry = reinterpret_cast<uint16_t*>(bootOrderBuffer + nonWBMOffset);
    std::swap(*wbmBootOrderEntry, *nonWBMBootOrderEntry);
    if (SetFirmwareEnvironmentVariableEx(bootOrderName, globalGuid, bootOrderBuffer, bootOrderLength, bootOrderAttributes))
    {
        std::cout << "Swapped WBM boot entry at offset " << wbmOffset << " with non-WBM boot entry at offset " << nonWBMOffset << std::endl;
    }
    else
    {
        std::cout << "Failed to swap WBM boot entry with non-WBM boot entry, error " << GetLastError() << std::endl;
        return 1;
    }
    
por 09.11.2016 / 23:37
0

A inicialização do PXE é configurada na ordem de inicialização do BIOS.

O carregador de boot BCD (para Windows) é iniciado após as etapas do BIOS, portanto, não pode causar impacto nele.

Para inicializar no PXE, você deve configurá-lo antes do dispositivo que hospeda o carregador de boot BCD na ordem de inicialização do BIOS.

    
por 25.02.2016 / 18:32