/*++ Copyright (c) 1993 Microsoft Corporation Module Name: arc.c Abstract: ARC/NV-RAM manipulation routines for 32-bit winnt setup. Author: Ted Miller (tedm) 19-December-1993 Revision History: --*/ #include "precomp.h" #pragma hdrstop #include "initguid.h" #include "diskguid.h" #ifdef UNICODE // Always true for ARC, never true for Win9x upgrade #if defined(_X86_) LOGICAL IsArcChecked = FALSE; LOGICAL IsArcMachine; #endif #if defined(EFI_NVRAM_ENABLED) #include // for ALIGN_UP LOGICAL IsEfiChecked = FALSE; LOGICAL IsEfiMachine; DWORD InitializeEfiStuff( IN HWND Parent ); NTSTATUS (*AddBootEntry) ( IN PBOOT_ENTRY BootEntry, OUT PULONG Id OPTIONAL ); NTSTATUS (*DeleteBootEntry) ( IN ULONG Id ); NTSTATUS (*EnumerateBootEntries) ( OUT PVOID Buffer, IN OUT PULONG BufferLength ); NTSTATUS (*QueryBootEntryOrder) ( OUT PULONG Ids, IN OUT PULONG Count ); NTSTATUS (*SetBootEntryOrder) ( IN PULONG Ids, IN ULONG Count ); NTSTATUS (*QueryBootOptions) ( OUT PBOOT_OPTIONS BootOptions, IN OUT PULONG BootOptionsLength ); NTSTATUS (*SetBootOptions) ( IN PBOOT_OPTIONS BootOptions, IN ULONG FieldsToChange ); PBOOT_OPTIONS BootOptions = NULL; PBOOT_OPTIONS OriginalBootOptions = NULL; PULONG OriginalBootEntryOrder = NULL; ULONG OriginalBootEntryOrderCount; PBOOT_ENTRY_LIST BootEntries = NULL; // // MY_BOOT_ENTRY is the internal representation of an EFI NVRAM boot item. // The NtBootEntry item is the structure passed to/from the NT boot entry APIs. // typedef struct _MY_BOOT_ENTRY { struct _MY_BOOT_ENTRY *Next; PUCHAR AllocationEnd; ULONG Status; PWSTR FriendlyName; ULONG FriendlyNameLength; PWSTR OsLoadOptions; ULONG OsLoadOptionsLength; PFILE_PATH BootFilePath; PFILE_PATH OsFilePath; BOOT_ENTRY NtBootEntry; } MY_BOOT_ENTRY, *PMY_BOOT_ENTRY; #define MBE_STATUS_ORDERED 0x00000001 #define MBE_STATUS_NEW 0x00000002 #define MBE_STATUS_DELETED 0x00000004 #define MBE_STATUS_COMMITTED 0x00000008 #define IS_BOOT_ENTRY_ACTIVE(_be) \ (((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_ACTIVE) != 0) #define IS_BOOT_ENTRY_WINDOWS(_be) \ (((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_WINDOWS) != 0) #define IS_BOOT_ENTRY_REMOVABLE_MEDIA(_be) \ (((_be)->NtBootEntry.Attributes & BOOT_ENTRY_ATTRIBUTE_REMOVABLE_MEDIA) != 0) #define IS_BOOT_ENTRY_ORDERED(_be) \ (((_be)->Status & MBE_STATUS_ORDERED) != 0) #define IS_BOOT_ENTRY_NEW(_be) \ (((_be)->Status & MBE_STATUS_NEW) != 0) #define IS_BOOT_ENTRY_DELETED(_be) \ (((_be)->Status & MBE_STATUS_DELETED) != 0) #define IS_BOOT_ENTRY_COMMITTED(_be) \ (((_be)->Status & MBE_STATUS_COMMITTED) != 0) PMY_BOOT_ENTRY MyBootEntries = NULL; NTSTATUS ConvertBootEntries( VOID ); BOOL CreateBootEntry( PWSTR BootFileDevice, PWSTR BootFilePath, PWSTR OsLoadDevice, PWSTR OsLoadPath, PWSTR OsLoadOptions, PWSTR FriendlyName ); #define ADD_OFFSET(_p,_o) (PVOID)((PUCHAR)(_p) + (_p)->_o) #endif // defined(EFI_NVRAM_ENABLED) UINT SystemPartitionCount; PWSTR* SystemPartitionNtNames; PWSTR SystemPartitionNtName; PWSTR SystemPartitionVolumeGuid; typedef enum { BootVarSystemPartition, BootVarOsLoader, BootVarOsLoadPartition, BootVarOsLoadFilename, BootVarLoadIdentifier, BootVarOsLoadOptions, BootVarMax } BootVars; LPCWSTR BootVarNames[BootVarMax] = { L"SYSTEMPARTITION", L"OSLOADER", L"OSLOADPARTITION", L"OSLOADFILENAME", L"LOADIDENTIFIER", L"OSLOADOPTIONS" }; LPCWSTR szAUTOLOAD = L"AUTOLOAD"; LPCWSTR szCOUNTDOWN = L"COUNTDOWN"; LPWSTR BootVarValues[BootVarMax]; LPCWSTR OriginalBootVarValues[BootVarMax]; LPCWSTR OriginalCountdown; LPCWSTR OriginalAutoload; DWORD BootVarComponentCount[BootVarMax]; LPWSTR *BootVarComponents[BootVarMax]; DWORD LargestComponentCount; LPWSTR DosDeviceTargets[26]; // // Flag indicating whether we messed with NV-RAM and thus need to // try to restore it in case the user cancels. // BOOL CleanUpNvRam; // // Leave as array because some code uses sizeof(ArcNameDirectory) // WCHAR ArcNameDirectory[] = L"\\ArcName"; #define GLOBAL_ROOT L"\\\\?\\GLOBALROOT" #define MAX_COMPONENTS 20 WCHAR ForcedSystemPartition; // // Helper macro to make object attribute initialization a little cleaner. // #define INIT_OBJA(Obja,UnicodeString,UnicodeText) \ \ RtlInitUnicodeString((UnicodeString),(UnicodeText)); \ \ InitializeObjectAttributes( \ (Obja), \ (UnicodeString), \ OBJ_CASE_INSENSITIVE, \ NULL, \ NULL \ ) UINT NormalizeArcPath( IN PCWSTR Path, OUT LPWSTR *NormalizedPath ) /*++ Routine Description: Transform an ARC path into one with no sets of empty parenthesis (ie, transforom all instances of () to (0).). Arguments: Path - ARC path to be normalized. NormalizedPath - if successful, receives a pointer to the normalized arc path. The caller must free with FREE(). Return Value: Win32 error code indicating outcome. --*/ { LPWSTR r; LPCWSTR p,q; LPWSTR normalizedPath; if(normalizedPath = MALLOC((lstrlen(Path)+100)*sizeof(WCHAR))) { ZeroMemory(normalizedPath,(lstrlen(Path)+100)*sizeof(WCHAR)); } else { return(ERROR_NOT_ENOUGH_MEMORY); } for(p=Path; q=wcsstr(p,L"()"); p=q+2) { r = normalizedPath + lstrlen(normalizedPath); lstrcpyn(r,p,(int)(q-p)+1); lstrcat(normalizedPath,L"(0)"); } lstrcat(normalizedPath,p); if(r = REALLOC(normalizedPath,(lstrlen(normalizedPath)+1)*sizeof(WCHAR))) { *NormalizedPath = r; return(NO_ERROR); } else { FREE(normalizedPath); return(ERROR_NOT_ENOUGH_MEMORY); } } DWORD GetVarComponents( IN PCWSTR VarValue, OUT LPWSTR **Components, OUT PDWORD ComponentCount ) /*++ Routine Description: Split a semi-colon delineated list of arc paths up into a set of individual strings. For each component leading and trailing spaces are stripped out. Arguments: VarValue - supplies string with list of arc paths to be split apart. Components - receives array of pointers to individual components on the variable specified in VarValue. ComponentCount - receives number of separate arc paths in the Components array. Return Value: Win32 error indicating outcome. If NO_ERROR then the caller must free the Components array and the strings pointed to by its elements. --*/ { LPWSTR *components; LPWSTR *temp; DWORD componentCount; LPCWSTR p; LPCWSTR Var; LPWSTR comp; DWORD len; UINT ec; components = MALLOC(MAX_COMPONENTS * sizeof(LPWSTR)); if(!components) { return(ERROR_NOT_ENOUGH_MEMORY); } ZeroMemory(components,MAX_COMPONENTS * sizeof(LPWSTR)); ec = NO_ERROR; for(Var=VarValue,componentCount=0; *Var; ) { // // Skip leading spaces. // while((*Var == L' ') || (*Var == L'\t')) { Var++; } if(*Var == 0) { break; } p = Var; while(*p && (*p != L';')) { p++; } len = (DWORD)((PUCHAR)p - (PUCHAR)Var); comp = MALLOC(len + sizeof(WCHAR)); if(!comp) { ec = ERROR_NOT_ENOUGH_MEMORY; break; } len /= sizeof(WCHAR); lstrcpynW(comp,Var,len+1); ec = NormalizeArcPath(comp,&components[componentCount]); FREE(comp); if(ec != NO_ERROR) { break; } componentCount++; if(componentCount == MAX_COMPONENTS) { break; } Var = p; if(*Var) { Var++; // skip ; } } if(ec == NO_ERROR) { if(componentCount) { temp = REALLOC(components,componentCount*sizeof(LPWSTR)); if(!temp) { ec = ERROR_NOT_ENOUGH_MEMORY; } } else { temp = NULL; } } if(ec == NO_ERROR) { *Components = temp; *ComponentCount = componentCount; } else { for(len=0; components[len] && (len= canonName.MaximumLength) { return STATUS_BUFFER_TOO_SMALL; } RtlCopyMemory(canonName.Buffer, name.Buffer, name.Length); canonName.Length = name.Length; canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0; for (CurrentDepth = 0; CurrentDepth < MaxDepth; CurrentDepth++) { InitializeObjectAttributes(&oa, &canonName, OBJ_CASE_INSENSITIVE, 0, 0); status = NtOpenSymbolicLinkObject(&handle, READ_CONTROL | SYMBOLIC_LINK_QUERY, &oa); if (!NT_SUCCESS(status)) { break; } status = NtQuerySymbolicLinkObject(handle, &canonName, NULL); NtClose(handle); if (!NT_SUCCESS(status)) { return status; } canonName.Buffer[canonName.Length/sizeof(WCHAR)] = 0; } return STATUS_SUCCESS; } // // Structure to map from old NT partition names like // \device\harddisk0\partition1 to new NT partition names // like \device\harddiskvolume1 // typedef struct _NAME_TRANSLATIONS { WCHAR OldNtName[MAX_PATH]; WCHAR NewNtName[MAX_PATH]; } NT_NAME_TRANSLATION, * PNT_NAME_TRANSLATION; // // Map of old style NT partition names to new style NT // partition names // NT_NAME_TRANSLATION OldNewNtNames[256] = {0}; PWSTR OldNtNameToNewNtName( IN PCWSTR OldNtName ) /*++ Routine Description: Given a old format NT name tries to lookup at new format NT name in the global map Arguments: OldNtName - The partition name specified in the old format Return Value: The new NT name if there exists one, otherwise NULL. --*/ { ULONG Index = 0; ULONG MaxEntries = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION); PWSTR NewNtName = NULL; for (Index = 0; (Index < MaxEntries); Index++) { if (OldNewNtNames[Index].OldNtName[0] && !_wcsicmp(OldNewNtNames[Index].OldNtName, OldNtName)) { NewNtName = OldNewNtNames[Index].NewNtName; } } return NewNtName; } PWSTR NewNtNameToOldNtName( IN PCWSTR NewNtName ) /*++ Routine Description: Given a new format NT name tries to lookup at old format NT name in the global map Arguments: NewNtName - The partition name specified in the new format Return Value: The old NT name if there exists one, otherwise NULL. --*/ { ULONG Index = 0; ULONG MaxEntries = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION); PWSTR OldNtName = NULL; for (Index=0; (Index < MaxEntries); Index++) { if (OldNewNtNames[Index].NewNtName[0] && !_wcsicmp(OldNewNtNames[Index].NewNtName, NewNtName)) { OldNtName = OldNewNtNames[Index].OldNtName; } } return OldNtName; } DWORD InitOldToNewNtNameTranslations( VOID ) /*++ Routine Description: Initializes the global old NT partition names to new NT partition names mapping. Arguments: None. Return Value: The number of valid entries in the map --*/ { DWORD MappingCount = 0; SYSTEM_DEVICE_INFORMATION SysDevInfo = {0}; NTSTATUS Status; OBJECT_ATTRIBUTES ObjAttrs; UNICODE_STRING ObjName; Status = NtQuerySystemInformation(SystemDeviceInformation, &SysDevInfo, sizeof(SYSTEM_DEVICE_INFORMATION), NULL); if (NT_SUCCESS(Status)) { ULONG Index; WCHAR OldNtPath[MAX_PATH]; DWORD ErrorCode = 0; ULONG SlotIndex = 0; ULONG MaxSlots = sizeof(OldNewNtNames)/sizeof(NT_NAME_TRANSLATION); for (Index=0; (!ErrorCode) && (Index < SysDevInfo.NumberOfDisks) && (SlotIndex < MaxSlots); Index++) { HANDLE DirectoryHandle; swprintf(OldNtPath, L"\\device\\Harddisk%d", Index); // // Open the disk directory. // INIT_OBJA(&ObjAttrs, &ObjName, OldNtPath); Status = NtOpenDirectoryObject(&DirectoryHandle, DIRECTORY_QUERY, &ObjAttrs); if(NT_SUCCESS(Status)) { BOOLEAN RestartScan = TRUE; ULONG Context = 0; BOOLEAN MoreEntries = TRUE; WCHAR Buffer[MAX_PATH * 2] = {0}; POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer; do { Status = NtQueryDirectoryObject( DirectoryHandle, Buffer, sizeof(Buffer), TRUE, // return single entry RestartScan, &Context, NULL // return length ); if(NT_SUCCESS(Status)) { // // Make sure this name is a symbolic link. // if(DirInfo->Name.Length && (DirInfo->TypeName.Length >= 24) && CharUpperBuff((LPWSTR)DirInfo->TypeName.Buffer,12) && !memcmp(DirInfo->TypeName.Buffer,L"SYMBOLICLINK",24)) { WCHAR EntryName[MAX_PATH]; lstrcpy(EntryName, OldNtPath); ConcatenatePaths(EntryName, DirInfo->Name.Buffer, (DWORD)(-1)); Status = QueryCanonicalName(EntryName, -1, Buffer, sizeof(Buffer)); if (NT_SUCCESS(Status)) { wcscpy(OldNewNtNames[SlotIndex].OldNtName, EntryName); wcscpy(OldNewNtNames[SlotIndex].NewNtName, Buffer); SlotIndex++; } } } else { MoreEntries = FALSE; if(Status == STATUS_NO_MORE_ENTRIES) { Status = STATUS_SUCCESS; } ErrorCode = RtlNtStatusToDosError(Status); } RestartScan = FALSE; } while(MoreEntries && (SlotIndex < MaxSlots)); NtClose(DirectoryHandle); } else { ErrorCode = RtlNtStatusToDosError(Status); } } if (!ErrorCode && NT_SUCCESS(Status)) { MappingCount = SlotIndex; } } return MappingCount; } DWORD NtNameToArcPath ( IN PCWSTR NtName, OUT LPWSTR *ArcPath ) /*++ Routine Description: Convert an NT volume name to an ARC path. Arguments: NtName - supplies name of drive to be converted. ArcPath - receives pointer to buffer containing arc path if the routine is successful. Caller must free with FREE(). Return Value: Win32 error code indicating outcome. --*/ { UNICODE_STRING UnicodeString; HANDLE DirectoryHandle; OBJECT_ATTRIBUTES Obja; NTSTATUS Status; BOOLEAN RestartScan; DWORD Context; BOOL MoreEntries; LPWSTR ArcName = NULL; WCHAR Buffer[512]; WCHAR ArcDiskName[MAX_PATH] = {0}; WCHAR NtDiskName[MAX_PATH] = {0}; WCHAR ArcPartitionName[MAX_PATH] = {0}; PWSTR PartitionName = NULL; PWSTR PartitionNumStr = NULL; POBJECT_DIRECTORY_INFORMATION DirInfo = (POBJECT_DIRECTORY_INFORMATION)Buffer; DWORD ErrorCode; ErrorCode = NO_ERROR; *ArcPath = NULL; // // Get hold of the NT disk name // PartitionName = NewNtNameToOldNtName(NtName); if (PartitionName) { PWSTR PartitionNameStart = PartitionName; PartitionName = wcsrchr(PartitionName, L'\\'); if (PartitionName && wcsstr(PartitionName, L"Partition")) { wcsncpy(NtDiskName, PartitionNameStart, PartitionName - PartitionNameStart); wcscat(NtDiskName, L"\\Partition0"); PartitionNumStr = PartitionName + wcslen(L"\\Partition"); } } // // Open the \ArcName directory. // INIT_OBJA(&Obja,&UnicodeString,ArcNameDirectory); Status = NtOpenDirectoryObject(&DirectoryHandle,DIRECTORY_QUERY,&Obja); if(NT_SUCCESS(Status)) { RestartScan = TRUE; Context = 0; MoreEntries = TRUE; do { Status = NtQueryDirectoryObject( DirectoryHandle, Buffer, sizeof(Buffer), TRUE, // return single entry RestartScan, &Context, NULL // return length ); if(NT_SUCCESS(Status)) { CharLower(DirInfo->Name.Buffer); // // Make sure this name is a symbolic link. // if(DirInfo->Name.Length && (DirInfo->TypeName.Length >= 24) && CharUpperBuff((LPWSTR)DirInfo->TypeName.Buffer,12) && !memcmp(DirInfo->TypeName.Buffer,L"SYMBOLICLINK",24)) { WCHAR OldNtName[MAX_PATH] = {0}; ArcName = MALLOC(DirInfo->Name.Length + sizeof(ArcNameDirectory) + sizeof(WCHAR)); if(!ArcName) { ErrorCode = ERROR_NOT_ENOUGH_MEMORY; break; } lstrcpy(ArcName,ArcNameDirectory); ConcatenatePaths(ArcName,DirInfo->Name.Buffer,(DWORD)(-1)); // // We have the entire arc name in ArcName. Now open the first // level symbolic link. // Status = QueryCanonicalName(ArcName, 1, Buffer, sizeof(Buffer)); if (NT_SUCCESS(Status)) { wcscpy(OldNtName, Buffer); // // Now resolve the complete symbolic link // Status = QueryCanonicalName(ArcName, -1, Buffer, sizeof(Buffer)); if (NT_SUCCESS(Status)) { if(!lstrcmpi(Buffer, NtName)) { *ArcPath = ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR)); } else { if (!lstrcmpi(OldNtName, NtDiskName)) { wcscpy(ArcDiskName, ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR))); } } } else { if(!lstrcmpi(OldNtName, NtName)) { *ArcPath = ArcName + (sizeof(ArcNameDirectory)/sizeof(WCHAR)); } } } if(!(*ArcPath)) { FREE(ArcName); ArcName = NULL; } } } else { MoreEntries = FALSE; if(Status == STATUS_NO_MORE_ENTRIES) { Status = STATUS_SUCCESS; } ErrorCode = RtlNtStatusToDosError(Status); } RestartScan = FALSE; } while(MoreEntries && !(*ArcPath)); NtClose(DirectoryHandle); } else { ErrorCode = RtlNtStatusToDosError(Status); } // // If we found a match for the disk but not for the actual // partition specified then guess thepartition number // (based on the current nt partition number ) // if ((!*ArcPath) && ArcDiskName[0] && PartitionName && PartitionNumStr) { PWSTR EndPtr = NULL; ULONG PartitionNumber = wcstoul(PartitionNumStr, &EndPtr, 10); if (PartitionNumber) { swprintf(ArcPartitionName, L"%wspartition(%d)", ArcDiskName, PartitionNumber); *ArcPath = DupString(ArcPartitionName); ErrorCode = NO_ERROR; DebugLog( Winnt32LogInformation, TEXT("\nCould not find arcname mapping for %1 partition.\r\n") TEXT("Guessing the arcname to be %2"), 0, NtName, ArcPartitionName); } } if (ErrorCode == NO_ERROR) { if(*ArcPath) { // // ArcPath points into the middle of a buffer. // The caller needs to be able to free it, so place it in its // own buffer here. // *ArcPath = DupString(*ArcPath); if (ArcName) { FREE(ArcName); } if(*ArcPath == NULL) { ErrorCode = ERROR_NOT_ENOUGH_MEMORY; } } else { // // No matching drive. // ErrorCode = ERROR_INVALID_DRIVE; } } return ErrorCode; } DWORD DriveLetterToArcPath( IN WCHAR DriveLetter, OUT LPWSTR *ArcPath ) /*++ Routine Description: Convert a drive letter to an ARC path. This routine relies on the DosDeviceTargets array being set up beforehand. Arguments: DriveLetter - supplies letter of drive to be converted. ArcPath - receives pointer to buffer containing arc path if the routine is successful. Caller must free with FREE(). Return Value: Win32 error code indicating outcome. --*/ { LPWSTR NtPath; NtPath = DosDeviceTargets[(WCHAR)CharUpper((PWCHAR)DriveLetter)-L'A']; if(!NtPath) { return(ERROR_INVALID_DRIVE); } return NtNameToArcPath (NtPath, ArcPath); } DWORD ArcPathToDriveLetterAndNtName ( IN PCWSTR ArcPath, OUT PWCHAR DriveLetter, OUT PWSTR NtName, IN DWORD BufferSizeInBytes ) /*++ Routine Description: Convert an arc path to a drive letter. This routine relies on the DosDeviceTargets array being set up beforehand. Arguments: ArcPath - specifies arc path to be converted. DriveLetter - if successful, receives letter of drive. Return Value: Win32 error code indicating outcome. --*/ { NTSTATUS Status; WCHAR drive; LPWSTR arcPath; DWORD ec; // // Assume failure // *DriveLetter = 0; arcPath = MALLOC(((lstrlen(ArcPath)+1)*sizeof(WCHAR)) + sizeof(ArcNameDirectory)); if(!arcPath) { return(ERROR_NOT_ENOUGH_MEMORY); } lstrcpy(arcPath,ArcNameDirectory); lstrcat(arcPath,L"\\"); lstrcat(arcPath,ArcPath); Status = QueryCanonicalName(arcPath, -1, NtName, BufferSizeInBytes); if (NT_SUCCESS(Status)) { ec = ERROR_INVALID_DRIVE; for(drive=L'A'; drive<=L'Z'; drive++) { if(DosDeviceTargets[drive-L'A'] && !lstrcmpi(NtName,DosDeviceTargets[drive-L'A'])) { *DriveLetter = drive; ec = NO_ERROR; break; } } } else { ec = RtlNtStatusToDosError(Status); } FREE(arcPath); return(ec); } DWORD InitDriveNameTranslations( VOID ) { WCHAR DriveName[15]; WCHAR Drive; WCHAR Buffer[512]; NTSTATUS status; swprintf(DriveName, L"\\DosDevices\\c:"); // // Calculate NT names for all local hard disks C-Z. // for(Drive=L'A'; Drive<=L'Z'; Drive++) { DosDeviceTargets[Drive-L'A'] = NULL; if(MyGetDriveType(Drive) == DRIVE_FIXED) { DriveName[12] = Drive; status = QueryCanonicalName(DriveName, -1, Buffer, sizeof(Buffer)); if (NT_SUCCESS(status)) { DosDeviceTargets[Drive-L'A'] = DupString(Buffer); if(!DosDeviceTargets[Drive-L'A']) { return(ERROR_NOT_ENOUGH_MEMORY); } } } } // // Initialize old Nt Parition names to new partition name // mapping // InitOldToNewNtNameTranslations(); return(NO_ERROR); } DWORD DetermineSystemPartitions( VOID ) { LPWSTR *SyspartComponents; DWORD NumSyspartComponents; DWORD d; DWORD rc; UINT u; WCHAR drive; WCHAR DeviceNtName[512]; SyspartComponents = BootVarComponents[BootVarSystemPartition]; NumSyspartComponents = BootVarComponentCount[BootVarSystemPartition]; SystemPartitionNtNames = MALLOC ((NumSyspartComponents + 1) * sizeof (PWSTR)); if (!SystemPartitionNtNames) { return ERROR_NOT_ENOUGH_MEMORY; } ZeroMemory(SystemPartitionNtNames, (NumSyspartComponents + 1) * sizeof (PWSTR)); ZeroMemory(SystemPartitionDriveLetters,27*sizeof(WCHAR)); // // Convert each system partition to a drive letter. // for(d=0; d 0) { for (u = 0; u < SystemPartitionCount; u++) { if (lstrcmpi (SyspartComponents[d], SystemPartitionNtNames[u]) == 0) { break; } } if (u < SystemPartitionCount) { continue; } } rc = ArcPathToDriveLetterAndNtName ( SyspartComponents[d], &drive, DeviceNtName, (DWORD) sizeof (DeviceNtName) ); if(rc == ERROR_NOT_ENOUGH_MEMORY) { return(ERROR_NOT_ENOUGH_MEMORY); } if (rc == ERROR_SUCCESS) { SystemPartitionDriveLetters[SystemPartitionCount] = drive; } SystemPartitionNtNames[SystemPartitionCount++] = DupString (DeviceNtName); } return(NO_ERROR); } DWORD DoInitializeArcStuff( VOID ) { DWORD ec; DWORD var; UNICODE_STRING UnicodeString; NTSTATUS Status; WCHAR Buffer[4096]; ec = InitDriveNameTranslations(); if(ec != NO_ERROR) { goto c0; } // // Get relevent boot vars. // // Enable privilege -- since we check this privilege up front // in main() this should not fail. // if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) { ec = ERROR_ACCESS_DENIED; goto c0; } for(var=0; var LargestComponentCount) { LargestComponentCount = BootVarComponentCount[var]; } } // // Get original countdown and autoload values. // If not successful, oh well, we won't be able to restore them // if the user cancels. // RtlInitUnicodeString(&UnicodeString,szCOUNTDOWN); Status = NtQuerySystemEnvironmentValue( &UnicodeString, Buffer, sizeof(Buffer) / sizeof(WCHAR), NULL ); if(NT_SUCCESS(Status)) { OriginalCountdown = DupString(Buffer); } else { OriginalCountdown = DupString(L""); } RtlInitUnicodeString(&UnicodeString,szAUTOLOAD); Status = NtQuerySystemEnvironmentValue( &UnicodeString, Buffer, sizeof(Buffer) / sizeof(WCHAR), NULL ); if(NT_SUCCESS(Status)) { OriginalAutoload = DupString(Buffer); } else { OriginalAutoload = DupString(L"NO"); } ec = DetermineSystemPartitions(); if(ec != NO_ERROR) { goto c2; } return(NO_ERROR); c2: c0: return(ec); } BOOL ArcInitializeArcStuff( IN HWND Parent ) { DWORD ec; BOOL b; HKEY key; DWORD type; DWORD size; PBYTE buffer = NULL; DWORD i; #if defined(EFI_NVRAM_ENABLED) // // Try to initialize as an EFI machine. If we're on an EFI machine, // this will succeed. Otherwise it will fail, in which case we try // to initialize as an ARC machine. // ec = InitializeEfiStuff(Parent); if (!IsEfi()) #endif { // // Try to initialize as an ARC machine. This is expect to // always succeed. // ec = DoInitializeArcStuff(); } switch(ec) { case NO_ERROR: #if defined(EFI_NVRAM_ENABLED) // // On an EFI machine, the rest of this code (determining system // partitions) is not necessary. // if (IsEfi()) { b = TRUE; } else #endif { // // Make sure there is at least one valid system partition. // if(!SystemPartitionCount) { MessageBoxFromMessage( Parent, MSG_SYSTEM_PARTITION_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); b = FALSE; } else { i = 0; // // On ARC machines we set up a local boot directory that is // placed in the root of the system partition. // // // read the SystemPartition value from registry // ec = RegOpenKey (HKEY_LOCAL_MACHINE, TEXT("System\\Setup"), &key); if (ec == ERROR_SUCCESS) { ec = RegQueryValueEx (key, TEXT("SystemPartition"), NULL, &type, NULL, &size); if (ec == ERROR_SUCCESS && type == REG_SZ) { buffer = MALLOC (size); if (buffer) { ec = RegQueryValueEx (key, TEXT("SystemPartition"), NULL, &type, buffer, &size); if (ec != ERROR_SUCCESS) { FREE (buffer); buffer = NULL; } } } RegCloseKey (key); } #if defined(EFI_NVRAM_ENABLED) // // we just trust the value that comes from the regkey -- EFI // systems only have one system partition, so it doesn't make // sense to try to match this up against a list of potential // system partitions. // SystemPartitionNtName = (PWSTR) buffer; #else // // look for this system partition to make sure things are OK // if (buffer) { while (i < SystemPartitionCount) { if (lstrcmpi (SystemPartitionNtNames[i], (PCTSTR)buffer) == 0) { SystemPartitionNtName = SystemPartitionNtNames[i]; break; } i++; } FREE (buffer); } #endif if(!SystemPartitionNtName) { MessageBoxFromMessage( Parent, MSG_SYSTEM_PARTITION_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); b = FALSE; break; } #if !defined(EFI_NVRAM_ENABLED) if (SystemPartitionDriveLetters[i]) { SystemPartitionDriveLetter = ForcedSystemPartition ? ForcedSystemPartition : SystemPartitionDriveLetters[i]; LocalBootDirectory[0] = SystemPartitionDriveLetter; LocalBootDirectory[1] = TEXT(':'); LocalBootDirectory[2] = TEXT('\\'); LocalBootDirectory[3] = 0; } else #endif { // SystemPartitionNtNtname is valid at this point thanks to // the check above. size = sizeof(GLOBAL_ROOT) + lstrlen(SystemPartitionNtName)*sizeof(WCHAR) + sizeof(WCHAR) + sizeof(WCHAR); SystemPartitionVolumeGuid = MALLOC (size); if(!SystemPartitionVolumeGuid) { goto MemoryError; } lstrcpy (SystemPartitionVolumeGuid, GLOBAL_ROOT); lstrcat (SystemPartitionVolumeGuid, SystemPartitionNtName); lstrcat (SystemPartitionVolumeGuid, L"\\"); lstrcpy (LocalBootDirectory, SystemPartitionVolumeGuid); } b = TRUE; } } break; case ERROR_NOT_ENOUGH_MEMORY: MemoryError: MessageBoxFromMessage( Parent, MSG_OUT_OF_MEMORY, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); b = FALSE; break; default: // // Some other unknown error. // MessageBoxFromMessage( Parent, MSG_COULDNT_READ_NVRAM, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); b = FALSE; break; } #if defined(EFI_NVRAM_ENABLED) // // make sure the system partition is on a GPT disk. // if (b) { HANDLE hDisk; PARTITION_INFORMATION_EX partitionEx; DWORD sizePartitionEx = 0; UNICODE_STRING uString; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatus; NTSTATUS Status; PWSTR p,q; b = FALSE; MYASSERT( SystemPartitionVolumeGuid != NULL ); // // SystemPartitionVolumeGuid may have a '\' at the end of it. // delete this character or we won't open the partition properly // p = DupString( SystemPartitionVolumeGuid + wcslen(GLOBAL_ROOT) ); if (p) { if (*(p+wcslen(p)-1) == L'\\') { *(p+wcslen(p)-1) = L'\0'; } INIT_OBJA( &ObjectAttributes, &uString, p ); Status = NtCreateFile(&hDisk, (ACCESS_MASK)FILE_GENERIC_READ, &ObjectAttributes, &IoStatus, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if (NT_SUCCESS(Status)) { Status = NtDeviceIoControlFile( hDisk, NULL, NULL, NULL, &IoStatus, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0, &partitionEx, sizeof(PARTITION_INFORMATION_EX) ); if (NT_SUCCESS(Status)) { if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT) { b = TRUE; } } else if (Status == STATUS_INVALID_DEVICE_REQUEST) { // // we must be running on an older build where the IOCTL // code is different // Status = NtDeviceIoControlFile( hDisk, NULL, NULL, NULL, &IoStatus, CTL_CODE(IOCTL_DISK_BASE, 0x0012, METHOD_BUFFERED, FILE_READ_ACCESS), NULL, 0, &partitionEx, sizeof(PARTITION_INFORMATION_EX) ); if (NT_SUCCESS(Status)) { if (partitionEx.PartitionStyle == PARTITION_STYLE_GPT) { b = TRUE; } } } NtClose(hDisk); } FREE( p ); } if (!b) { MessageBoxFromMessage( Parent, MSG_SYSTEM_PARTITIONTYPE_INVALID, FALSE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); } } #endif return(b); } #if defined(EFI_NVRAM_ENABLED) DWORD LocateEfiSystemPartition( OUT PWSTR SystemPartitionName ) /*++ Routine Description: Locates the EFI system partition on a GPT disk by scanning all the available hard disks. Arguments: SystemPartitionName : Buffer to receive system partition name, if one is present Return Value: Win32 error code indicating outcome. --*/ { DWORD ErrorCode = ERROR_BAD_ARGUMENTS; if (SystemPartitionName) { SYSTEM_DEVICE_INFORMATION SysDevInfo; NTSTATUS Status; *SystemPartitionName = UNICODE_NULL; // // Get hold of number of hard disks on the system // ZeroMemory(&SysDevInfo, sizeof(SYSTEM_DEVICE_INFORMATION)); Status = NtQuerySystemInformation(SystemDeviceInformation, &SysDevInfo, sizeof(SYSTEM_DEVICE_INFORMATION), NULL); if (NT_SUCCESS(Status)) { ULONG HardDiskCount = SysDevInfo.NumberOfDisks; ULONG CurrentDisk; ULONG BufferSize = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + (sizeof(PARTITION_INFORMATION_EX) * 128); PCHAR Buffer = MALLOC(BufferSize); BOOL Found = FALSE; if (Buffer) { // // Go through each disk and find out its partition // layout // for (CurrentDisk = 0; (!Found && (CurrentDisk < HardDiskCount)); CurrentDisk++) { WCHAR DiskName[MAX_PATH]; HANDLE DiskHandle; swprintf(DiskName, L"\\\\.\\PHYSICALDRIVE%d", CurrentDisk); DiskHandle = CreateFile(DiskName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if ((DiskHandle) && (DiskHandle != INVALID_HANDLE_VALUE)) { DWORD ReturnSize = 0; ZeroMemory(Buffer, BufferSize); if (DeviceIoControl(DiskHandle, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, Buffer, BufferSize, &ReturnSize, NULL)) { // // Only search in GPT disks on IA64 // PDRIVE_LAYOUT_INFORMATION_EX DriveLayout; DriveLayout = (PDRIVE_LAYOUT_INFORMATION_EX)Buffer; if (DriveLayout->PartitionStyle == PARTITION_STYLE_GPT) { ULONG PartitionIndex; for (PartitionIndex = 0; (PartitionIndex < DriveLayout->PartitionCount); PartitionIndex++) { PPARTITION_INFORMATION_EX Partition; GUID *PartitionType; Partition = DriveLayout->PartitionEntry + PartitionIndex; PartitionType = &(Partition->Gpt.PartitionType); if (IsEqualGUID(PartitionType, &PARTITION_SYSTEM_GUID)) { swprintf(SystemPartitionName, L"\\Device\\Harddisk%d\\Partition%d", CurrentDisk, Partition->PartitionNumber ); Found = TRUE; break; } } } } CloseHandle(DiskHandle); } } FREE(Buffer); } else { ErrorCode = ERROR_NOT_ENOUGH_MEMORY; } if (!Found) { ErrorCode = ERROR_FILE_NOT_FOUND; } else { ErrorCode = ERROR_SUCCESS; } } } return ErrorCode; } DWORD InitializeEfiStuff( IN HWND Parent ) { DWORD ec; NTSTATUS status; HMODULE h; WCHAR dllName[MAX_PATH]; ULONG length; HKEY key; DWORD type; LONG i; PMY_BOOT_ENTRY bootEntry; PMY_BOOT_ENTRY previousBootEntry; MYASSERT(!IsEfiChecked); // // IsEfi() uses IsEfiMachine to determine its return value. Assume that // we're not on an EFI machine. // IsEfiChecked = TRUE; IsEfiMachine = FALSE; // // Enable the privilege that is necessary to query/set NVRAM. // if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) { ec = GetLastError(); return ec; } // // Load ntdll.dll from the system directory. // GetSystemDirectory(dllName, MAX_PATH); ConcatenatePaths(dllName, TEXT("ntdll.dll"), MAX_PATH); h = LoadLibrary(dllName); if (h == NULL) { ec = GetLastError(); return ec; } // // Get the addresses of the NVRAM APIs that we need to use. If any of // these APIs are not available, this must be a pre-EFI NVRAM build. // (FARPROC)AddBootEntry = GetProcAddress(h, "NtAddBootEntry"); (FARPROC)DeleteBootEntry = GetProcAddress(h, "NtDeleteBootEntry"); (FARPROC)EnumerateBootEntries = GetProcAddress(h, "NtEnumerateBootEntries"); (FARPROC)QueryBootEntryOrder = GetProcAddress(h, "NtQueryBootEntryOrder"); (FARPROC)SetBootEntryOrder = GetProcAddress(h, "NtSetBootEntryOrder"); (FARPROC)QueryBootOptions = GetProcAddress(h, "NtQueryBootOptions"); (FARPROC)SetBootOptions = GetProcAddress(h, "NtSetBootOptions"); if ((AddBootEntry == NULL) || (DeleteBootEntry == NULL) || (EnumerateBootEntries == NULL) || (QueryBootEntryOrder == NULL) || (SetBootEntryOrder == NULL) || (QueryBootOptions == NULL) || (SetBootOptions == NULL)) { return ERROR_OLD_WIN_VERSION; } // // Get the global system boot options. If the call fails with // STATUS_NOT_IMPLEMENTED, this is not an EFI machine. // length = 0; status = QueryBootOptions(NULL, &length); if (status != STATUS_NOT_IMPLEMENTED) { IsEfiMachine = TRUE; } if (status != STATUS_BUFFER_TOO_SMALL) { if (status == STATUS_SUCCESS) { status = STATUS_UNSUCCESSFUL; } return RtlNtStatusToDosError(status); } BootOptions = MALLOC(length); OriginalBootOptions = MALLOC(length); if ((BootOptions == NULL) || (OriginalBootOptions == NULL)) { return RtlNtStatusToDosError(ERROR_NOT_ENOUGH_MEMORY); } status = QueryBootOptions(BootOptions, &length); if (status != STATUS_SUCCESS) { FREE(BootOptions); FREE(OriginalBootOptions); BootOptions = NULL; OriginalBootOptions = NULL; return RtlNtStatusToDosError(status); } memcpy(OriginalBootOptions, BootOptions, length); // // Get the system boot order list. // length = 0; status = QueryBootEntryOrder(NULL, &length); if (status != STATUS_BUFFER_TOO_SMALL) { if (status == STATUS_SUCCESS) { status = STATUS_UNSUCCESSFUL; } return RtlNtStatusToDosError(status); } OriginalBootEntryOrder = MALLOC(length * sizeof(ULONG)); if (OriginalBootEntryOrder == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } status = QueryBootEntryOrder(OriginalBootEntryOrder, &length); if (status != STATUS_SUCCESS) { FREE(OriginalBootEntryOrder); OriginalBootEntryOrder = NULL; return RtlNtStatusToDosError(status); } OriginalBootEntryOrderCount = length; // // Get all existing boot entries. // length = 0; status = EnumerateBootEntries(NULL, &length); if (status != STATUS_BUFFER_TOO_SMALL) { if (status == STATUS_SUCCESS) { status = STATUS_UNSUCCESSFUL; } return RtlNtStatusToDosError(status); } BootEntries = MALLOC(length); if (BootEntries == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } status = EnumerateBootEntries(BootEntries, &length); if (status != STATUS_SUCCESS) { FREE(BootEntries); BootEntries = NULL; return RtlNtStatusToDosError(status); } // // Initialize drive name translations, which are needed for converting // the boot entries into their internal representations. // ec = InitDriveNameTranslations(); if(ec != NO_ERROR) { return ec; } // // Convert the boot entries into an internal representation. // status = ConvertBootEntries(); if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); } // // Free the enumeration buffer. // FREE(BootEntries); BootEntries = NULL; // // Boot entries are returned in an unspecified order. They are currently // in the MyBootEntries list in the order in which they were returned. // Sort the boot entry list based on the boot order. Do this by walking // the boot order array backwards, reinserting the entry corresponding to // each element of the array at the head of the list. // for (i = (LONG)OriginalBootEntryOrderCount - 1; i >= 0; i--) { for (previousBootEntry = NULL, bootEntry = MyBootEntries; bootEntry != NULL; previousBootEntry = bootEntry, bootEntry = bootEntry->Next) { if (bootEntry->NtBootEntry.Id == OriginalBootEntryOrder[i] ) { // // We found the boot entry with this ID. If it's not already // at the front of the list, move it there. // bootEntry->Status |= MBE_STATUS_ORDERED; if (previousBootEntry != NULL) { previousBootEntry->Next = bootEntry->Next; bootEntry->Next = MyBootEntries; MyBootEntries = bootEntry; } else { ASSERT(MyBootEntries == bootEntry); } break; } } } // // Get the NT name of the system partition from the registry. // ec = RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("System\\Setup"), &key); if (ec == ERROR_SUCCESS) { ec = RegQueryValueEx(key, TEXT("SystemPartition"), NULL, &type, NULL, &length); if (ec == ERROR_SUCCESS) { if (type == REG_SZ) { SystemPartitionNtName = MALLOC(length); if (SystemPartitionNtName != NULL) { ec = RegQueryValueEx( key, TEXT("SystemPartition"), NULL, &type, (PBYTE)SystemPartitionNtName, &length); if (ec != ERROR_SUCCESS) { FREE(SystemPartitionNtName); } } else { return ERROR_NOT_ENOUGH_MEMORY; } } else { return ERROR_INVALID_PARAMETER; } } RegCloseKey (key); } if (ec != NO_ERROR) { if (IsWinPEMode()) { WCHAR OldSysPartName[MAX_PATH] = {0}; WCHAR NewSysPartName[MAX_PATH] = {0}; ec = LocateEfiSystemPartition(OldSysPartName); if ((ec == NO_ERROR) && OldSysPartName[0]) { NTSTATUS Status = QueryCanonicalName(OldSysPartName, -1, NewSysPartName, sizeof(NewSysPartName)); if (NT_SUCCESS(Status) && NewSysPartName[0]) { SystemPartitionNtName = DupString(NewSysPartName); } else { ec = ERROR_FILE_NOT_FOUND; } } if ((ec == NO_ERROR) && (NewSysPartName[0] == UNICODE_NULL)) { ec = ERROR_FILE_NOT_FOUND; } } if (ec != NO_ERROR) { return ec; } } // // Get the volume name for the NT name. // length = sizeof(GLOBAL_ROOT) + lstrlen(SystemPartitionNtName)*sizeof(WCHAR) + sizeof(WCHAR) + sizeof(WCHAR); SystemPartitionVolumeGuid = MALLOC (length); if(!SystemPartitionVolumeGuid) { return ERROR_NOT_ENOUGH_MEMORY; } lstrcpy (SystemPartitionVolumeGuid, GLOBAL_ROOT); lstrcat (SystemPartitionVolumeGuid, SystemPartitionNtName); lstrcat (SystemPartitionVolumeGuid, L"\\"); lstrcpy (LocalBootDirectory, SystemPartitionVolumeGuid); return NO_ERROR; } // InitializeEfiStuff #endif // defined(EFI_NVRAM_ENABLED) ///////////////////////////////////////////////////////////////////// // // Everything above this line is concerned with reading NV-RAM. // Everything below this line is concerned with setting NV-RAM. // ///////////////////////////////////////////////////////////////////// BOOL DoSetNvRamVar( IN LPCWSTR VarName, IN LPCWSTR VarValue ) { UNICODE_STRING U1,U2; RtlInitUnicodeString(&U1,VarName); RtlInitUnicodeString(&U2,VarValue); return(NT_SUCCESS(NtSetSystemEnvironmentValue(&U1,&U2))); } BOOL WriteNewBootSetVar( IN DWORD var, IN PTSTR NewPart ) { WCHAR Buffer[2048]; DWORD i; // // Write the new part first. // lstrcpy(Buffer,NewPart); // // Append all components that were not deleted. // for(i=0; iNext) { if (IS_BOOT_ENTRY_WINDOWS(bootEntry)) { if (!lstrcmpi(bootEntry->OsLoadOptions, L"WINNT32")) { // // Delete this boot entry. Note that we don't update the // boot entry order list at this point. CreateBootEntry() // will do that. // status = DeleteBootEntry(bootEntry->NtBootEntry.Id); bootEntry->Status |= MBE_STATUS_DELETED; } } } // // Now create a new boot entry for textmode setup. // MYASSERT(LocalSourceDrive); NtPath = DosDeviceTargets[(WCHAR)CharUpper((PWCHAR)LocalSourceDrive)-L'A']; LoadString(hInst,IDS_RISCBootString,LoadId,sizeof(LoadId)/sizeof(TCHAR)); b = CreateBootEntry( SystemPartitionNtName, L"\\" SETUPLDR_FILENAME, NtPath, LocalSourceWithPlatform + 2, L"WINNT32", LoadId ); if (b) { // // Set up for automatic startup, 10 second countdown. We don't // care if this fails. // // Set the boot entry we added to be booted automatically on // the next boot, without waiting for a timeout at the boot menu. // // NB: CreateBootEntry() sets BootOptions->NextBootEntryId. // BootOptions->Timeout = 10; status = SetBootOptions( BootOptions, BOOT_OPTIONS_FIELD_TIMEOUT | BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID ); } return b; } #endif // defined(EFI_NVRAM_ENABLED) // // We get here if we're NOT on an EFI machine. // // Find and remove any remnants of previously attempted // winnt32 runs. Such runs are identified by 'winnt32' // in their osloadoptions. // for(set=0; setNext) { if (IS_BOOT_ENTRY_COMMITTED(bootEntry)) { MYASSERT(IS_BOOT_ENTRY_NEW(bootEntry)); status = DeleteBootEntry(bootEntry->NtBootEntry.Id); if (!NT_SUCCESS(status)) { b = FALSE; } } } // // Restore the original boot order list and the original timeout. // status = SetBootEntryOrder(OriginalBootEntryOrder, OriginalBootEntryOrderCount); if (!NT_SUCCESS(status)) { b = FALSE; } status = SetBootOptions(OriginalBootOptions, BOOT_OPTIONS_FIELD_TIMEOUT); if (!NT_SUCCESS(status)) { b = FALSE; } } } else { #endif // defined(EFI_NVRAM_ENABLED) for(var=0; varTimeout ); } else #endif // defined(EFI_NVRAM_ENABLED) { RtlInitUnicodeString(&UnicodeString,szCOUNTDOWN); Status = NtQuerySystemEnvironmentValue( &UnicodeString, Buffer, sizeof(Buffer) / sizeof(WCHAR), NULL ); if(NT_SUCCESS(Status)) { lstrcpy(Timeout,Buffer); } } } #if defined(_X86_) BOOL IsArc( VOID ) /*++ Routine Description: Run time check to determine if this is an Arc system. We attempt to read an Arc variable using the Hal. This will fail for Bios based systems. Arguments: None Return Value: True = This is an Arc system. --*/ { UNICODE_STRING UnicodeString; NTSTATUS Status; WCHAR Buffer[4096]; // // If we've already done the check once, don't bother doing it again. // if (IsArcChecked) { return IsArcMachine; } IsArcChecked = TRUE; IsArcMachine = FALSE; if(!EnablePrivilege(SE_SYSTEM_ENVIRONMENT_NAME,TRUE)) { return FALSE; // need better error handling? } // // Get the env var into the temp buffer. // RtlInitUnicodeString(&UnicodeString,BootVarNames[BootVarOsLoader]); Status = NtQuerySystemEnvironmentValue( &UnicodeString, Buffer, sizeof(Buffer)/sizeof(WCHAR), NULL ); if (NT_SUCCESS(Status)) { IsArcMachine = TRUE; } return IsArcMachine; } #endif // defined(_X86_) #if defined(EFI_NVRAM_ENABLED) BOOL IsEfi( VOID ) /*++ Routine Description: Run time check to determine if this is an EFI system. Arguments: None Return Value: True = This is an EFI system. --*/ { // // InitializeEfiStuff() must be called first to do the actual check. // MYASSERT(IsEfiChecked); return IsEfiMachine; } // IsEfi NTSTATUS ConvertBootEntries( VOID ) /*++ Routine Description: Convert boot entries read from EFI NVRAM into our internal format. Arguments: None. Return Value: NTSTATUS - Not STATUS_SUCCESS if an unexpected error occurred. --*/ { PBOOT_ENTRY_LIST bootEntryList; PBOOT_ENTRY bootEntry; PBOOT_ENTRY bootEntryCopy; PMY_BOOT_ENTRY myBootEntry; PMY_BOOT_ENTRY previousEntry; PWINDOWS_OS_OPTIONS osOptions; ULONG length; bootEntryList = BootEntries; previousEntry = NULL; while (TRUE) { bootEntry = &bootEntryList->BootEntry; // // Calculate the length of our internal structure. This includes // the base part of MY_BOOT_ENTRY plus the NT BOOT_ENTRY. // length = FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry) + bootEntry->Length; myBootEntry = MALLOC(length); if (myBootEntry == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(myBootEntry, length); // // Link the new entry into the list. // if (previousEntry != NULL) { previousEntry->Next = myBootEntry; } else { MyBootEntries = myBootEntry; } previousEntry = myBootEntry; // // Copy the NT BOOT_ENTRY into the allocated buffer. // bootEntryCopy = &myBootEntry->NtBootEntry; // // work around till bootentry has the correct length specified // __try { memcpy(bootEntryCopy, bootEntry, bootEntry->Length); } __except(EXCEPTION_EXECUTE_HANDLER) { if (bootEntry->Length > sizeof(ULONG)) { bootEntry->Length -= sizeof(ULONG); memcpy(bootEntryCopy, bootEntry, bootEntry->Length); } else { // // Lets atleast AV rather than having invalid // in memory data structures // memcpy(bootEntryCopy, bootEntry, bootEntry->Length); } } // // Fill in the base part of the structure. // myBootEntry->Next = NULL; myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + length - 1; myBootEntry->FriendlyName = ADD_OFFSET(bootEntryCopy, FriendlyNameOffset); myBootEntry->FriendlyNameLength = (wcslen(myBootEntry->FriendlyName) + 1) * sizeof(WCHAR); myBootEntry->BootFilePath = ADD_OFFSET(bootEntryCopy, BootFilePathOffset); // // If this is an NT boot entry, capture the NT-specific information in // the OsOptions. // osOptions = (PWINDOWS_OS_OPTIONS)bootEntryCopy->OsOptions; if (!IS_BOOT_ENTRY_WINDOWS(myBootEntry)) { // // The original implementation of NtEnumerateBootEntries() didn't // set BOOT_ENTRY_ATTRIBUTE_WINDOWS, so we need to check for that // here. // if ((bootEntryCopy->OsOptionsLength >= FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions)) && (strcmp(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE) == 0)) { myBootEntry->NtBootEntry.Attributes |= BOOT_ENTRY_ATTRIBUTE_WINDOWS; } } if (IS_BOOT_ENTRY_WINDOWS(myBootEntry)) { myBootEntry->OsLoadOptions = osOptions->OsLoadOptions; myBootEntry->OsLoadOptionsLength = (wcslen(myBootEntry->OsLoadOptions) + 1) * sizeof(WCHAR); myBootEntry->OsFilePath = ADD_OFFSET(osOptions, OsLoadPathOffset); } else { // // It's not an NT entry. Check to see if it represents a removable // media device. We want to know this so that we don't put our // boot entry ahead of the floppy or the CD, if they're already // at the front of the list. A boot entry represents a } // // Move to the next entry in the enumeration list, if any. // if (bootEntryList->NextEntryOffset == 0) { break; } bootEntryList = ADD_OFFSET(bootEntryList, NextEntryOffset); } return STATUS_SUCCESS; } // ConvertBootEntries BOOL CreateBootEntry( PWSTR BootFileDevice, PWSTR BootFilePath, PWSTR OsLoadDevice, PWSTR OsLoadPath, PWSTR OsLoadOptions, PWSTR FriendlyName ) /*++ Routine Description: Create an internal-format boot entry. Arguments: BootFileDevice - The NT name of the device on which the OS loader resides. BootFilePath - The volume-relative path to the OS loader. Must start with a backslash. OsLoadDevice - The NT name ofthe device on which the OS resides. OsLoadPath - The volume-relative path to the OS root directory (\WINDOWS). Must start with a backslash. OsLoadOptions - Boot options for the OS. Can be an empty string. FriendlyName - The user-visible name for the boot entry. (This is ARC's LOADIDENTIFIER.) Return Value: BOOLEAN - FALSE if an unexpected error occurred. --*/ { NTSTATUS status; ULONG requiredLength; ULONG osOptionsOffset; ULONG osLoadOptionsLength; ULONG osLoadPathOffset; ULONG osLoadPathLength; ULONG osOptionsLength; ULONG friendlyNameOffset; ULONG friendlyNameLength; ULONG bootPathOffset; ULONG bootPathLength; PMY_BOOT_ENTRY myBootEntry; PMY_BOOT_ENTRY previousBootEntry; PMY_BOOT_ENTRY nextBootEntry; PBOOT_ENTRY ntBootEntry; PWINDOWS_OS_OPTIONS osOptions; PFILE_PATH osLoadPath; PWSTR friendlyName; PFILE_PATH bootPath; PWSTR p; PULONG order; ULONG count; ULONG savedAttributes; // // Calculate how long the internal boot entry needs to be. This includes // our internal structure, plus the BOOT_ENTRY structure that the NT APIs // use. // // Our structure: // requiredLength = FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry); // // Base part of NT structure: // requiredLength += FIELD_OFFSET(BOOT_ENTRY, OsOptions); // // Save offset to BOOT_ENTRY.OsOptions. Add in base part of // WINDOWS_OS_OPTIONS. Calculate length in bytes of OsLoadOptions // and add that in. // osOptionsOffset = requiredLength; requiredLength += FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions); osLoadOptionsLength = (wcslen(OsLoadOptions) + 1) * sizeof(WCHAR); requiredLength += osLoadOptionsLength; // // Round up to a ULONG boundary for the OS FILE_PATH in the // WINDOWS_OS_OPTIONS. Save offset to OS FILE_PATH. Add in base part // of FILE_PATH. Add in length in bytes of OS device NT name and OS // directory. Calculate total length of OS FILE_PATH and of // WINDOWS_OS_OPTIONS. // requiredLength = ALIGN_UP(requiredLength, ULONG); osLoadPathOffset = requiredLength; requiredLength += FIELD_OFFSET(FILE_PATH, FilePath); requiredLength += (wcslen(OsLoadDevice) + 1 + wcslen(OsLoadPath) + 1) * sizeof(WCHAR); osLoadPathLength = requiredLength - osLoadPathOffset; osOptionsLength = requiredLength - osOptionsOffset; // // Round up to a ULONG boundary for the friendly name in the BOOT_ENTRY. // Save offset to friendly name. Calculate length in bytes of friendly name // and add that in. // requiredLength = ALIGN_UP(requiredLength, ULONG); friendlyNameOffset = requiredLength; friendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR); requiredLength += friendlyNameLength; // // Round up to a ULONG boundary for the boot FILE_PATH in the BOOT_ENTRY. // Save offset to boot FILE_PATH. Add in base part of FILE_PATH. Add in // length in bytes of boot device NT name and boot file. Calculate total // length of boot FILE_PATH. // requiredLength = ALIGN_UP(requiredLength, ULONG); bootPathOffset = requiredLength; requiredLength += FIELD_OFFSET(FILE_PATH, FilePath); requiredLength += (wcslen(BootFileDevice) + 1 + wcslen(BootFilePath) + 1) * sizeof(WCHAR); bootPathLength = requiredLength - bootPathOffset; // // Allocate memory for the boot entry. // myBootEntry = MALLOC(requiredLength); if (myBootEntry == NULL) { return FALSE; } RtlZeroMemory(myBootEntry, requiredLength); // // Calculate addresses of various substructures using the saved offsets. // ntBootEntry = &myBootEntry->NtBootEntry; osOptions = (PWINDOWS_OS_OPTIONS)ntBootEntry->OsOptions; osLoadPath = (PFILE_PATH)((PUCHAR)myBootEntry + osLoadPathOffset); friendlyName = (PWSTR)((PUCHAR)myBootEntry + friendlyNameOffset); bootPath = (PFILE_PATH)((PUCHAR)myBootEntry + bootPathOffset); // // Fill in the internal-format structure. // myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + requiredLength; myBootEntry->Status = MBE_STATUS_NEW | MBE_STATUS_ORDERED; myBootEntry->FriendlyName = friendlyName; myBootEntry->FriendlyNameLength = friendlyNameLength; myBootEntry->OsLoadOptions = osOptions->OsLoadOptions; myBootEntry->OsLoadOptionsLength = osLoadOptionsLength; myBootEntry->BootFilePath = bootPath; myBootEntry->OsFilePath = osLoadPath; // // Fill in the base part of the NT boot entry. // ntBootEntry->Version = BOOT_ENTRY_VERSION; ntBootEntry->Length = requiredLength - FIELD_OFFSET(MY_BOOT_ENTRY, NtBootEntry); ntBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE | BOOT_ENTRY_ATTRIBUTE_WINDOWS; ntBootEntry->FriendlyNameOffset = (ULONG)((PUCHAR)friendlyName - (PUCHAR)ntBootEntry); ntBootEntry->BootFilePathOffset = (ULONG)((PUCHAR)bootPath - (PUCHAR)ntBootEntry); ntBootEntry->OsOptionsLength = osOptionsLength; // // Fill in the base part of the WINDOWS_OS_OPTIONS, including the // OsLoadOptions. // strcpy(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE); osOptions->Version = WINDOWS_OS_OPTIONS_VERSION; osOptions->Length = osOptionsLength; osOptions->OsLoadPathOffset = (ULONG)((PUCHAR)osLoadPath - (PUCHAR)osOptions); wcscpy(osOptions->OsLoadOptions, OsLoadOptions); // // Fill in the OS FILE_PATH. // osLoadPath->Version = FILE_PATH_VERSION; osLoadPath->Length = osLoadPathLength; osLoadPath->Type = FILE_PATH_TYPE_NT; p = (PWSTR)osLoadPath->FilePath; wcscpy(p, OsLoadDevice); p += wcslen(p) + 1; wcscpy(p, OsLoadPath); // // Copy the friendly name. // wcscpy(friendlyName, FriendlyName); // // Fill in the boot FILE_PATH. // bootPath->Version = FILE_PATH_VERSION; bootPath->Length = bootPathLength; bootPath->Type = FILE_PATH_TYPE_NT; p = (PWSTR)bootPath->FilePath; wcscpy(p, BootFileDevice); p += wcslen(p) + 1; wcscpy(p, BootFilePath); // // Add the new boot entry. // // NB: The original implementation of NtAddBootEntry didn't like it // when attribute bits other than _ACTIVE and _DEFAULT were set, so // we need to mask the other bits off here. // savedAttributes = ntBootEntry->Attributes; ntBootEntry->Attributes &= (BOOT_ENTRY_ATTRIBUTE_DEFAULT | BOOT_ENTRY_ATTRIBUTE_ACTIVE); status = AddBootEntry(ntBootEntry, &ntBootEntry->Id); ntBootEntry->Attributes = savedAttributes; if (!NT_SUCCESS(status)) { FREE(myBootEntry); return FALSE; } myBootEntry->Status |= MBE_STATUS_COMMITTED; // // Remember the ID of the new boot entry as the entry to be booted // immediately on the next boot. // BootOptions->NextBootEntryId = ntBootEntry->Id; // // Link the new boot entry into the list, after any removable media // entries that are at the front of the list. // previousBootEntry = NULL; nextBootEntry = MyBootEntries; while ((nextBootEntry != NULL) && IS_BOOT_ENTRY_REMOVABLE_MEDIA(nextBootEntry)) { previousBootEntry = nextBootEntry; nextBootEntry = nextBootEntry->Next; } myBootEntry->Next = nextBootEntry; if (previousBootEntry == NULL) { MyBootEntries = myBootEntry; } else { previousBootEntry->Next = myBootEntry; } // // Build the new boot order list. Insert all boot entries with // MBE_STATUS_ORDERED into the list. (Don't insert deleted entries.) // count = 0; nextBootEntry = MyBootEntries; while (nextBootEntry != NULL) { if (IS_BOOT_ENTRY_ORDERED(nextBootEntry) && !IS_BOOT_ENTRY_DELETED(nextBootEntry)) { count++; } nextBootEntry = nextBootEntry->Next; } order = MALLOC(count * sizeof(ULONG)); if (order == NULL) { return FALSE; } count = 0; nextBootEntry = MyBootEntries; while (nextBootEntry != NULL) { if (IS_BOOT_ENTRY_ORDERED(nextBootEntry) && !IS_BOOT_ENTRY_DELETED(nextBootEntry)) { order[count++] = nextBootEntry->NtBootEntry.Id; } nextBootEntry = nextBootEntry->Next; } // // Write the new boot entry order list. // status = SetBootEntryOrder(order, count); FREE(order); if (!NT_SUCCESS(status)) { return FALSE; } return TRUE; } // CreateBootEntry #endif // defined(EFI_NVRAM_ENABLED) #endif // UNICODE