我们正在通过iPXE设置一些系统到PXEboot,根据主服务器状态,通过wimboot和MDT正常启动或重新映像。 系统configuration为首先从networking启动。 iPXE和wimboot都在UEFI下运行。
它工作得很好,除了在Windows安装结束时,BIOS已经被修改为指向新的Windows启动pipe理器作为主启动设备。 所以如果不进入bios和更改设置就不能再次成像。
我明白为什么启动顺序会因为wimboot / MDT进程涉及多次重启而发生改变。 但是我真的希望保持PXE作为主引导,或者操作引导顺序,以便在完成时首先拥有networking。 (我的PXE服务器将传递networking启动机会以允许安装工作,或者在不需要成像时将系统保持独立。)
更新 –我看到两种可能性:
了解以下内容:
一旦理解了引导顺序和Windows API的UEFI规范,代码(C ++,为我们正在使用的64位而构build)并不算太坏。 这需要build立一个需要pipe理权限,并静态链接Windows运行时的exe,然后在重新启动之前安装操作系统后,我在MDT中运行它。
首先,您必须声明调用API的权限。 使用一个小帮手:
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); }
然后打电话
SetPrivilege(GetCurrentProcess(), SE_SYSTEM_ENVIRONMENT_NAME, TRUE));
接下来,获取启动选项列表(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; }
然后,您可以遍历每个启动选项,为其创buildBoot ####variables名称,然后使用它获取有关该选项的信息的结构。 您需要查看第一个活动选项是否具有与“Windows启动pipe理器”相同的“说明”。 描述是结构体中偏移量为6的以空字符结尾的宽string。
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 } }
现在,如果find活动的WBM和非WBM引导选项,并且第一个WBM选项位于wbmOffset,并且第一个非WBM选项位于nonWBMOffset,并且wbmOffset <nonWBMOffset,则将BootOrdervariables中的条目与以下内容交换:
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; }