/*++ Copyright (c) 1998 Intel Corporation Module Name: bcfg.c Abstract: Shell app "bcfg" Boot time driver config Revision History --*/ #include "shell.h" #define MAX_ENV_SIZE 1024 #define BCFG_NONE 0 #define BCFG_DUMP 1 #define BCFG_MOVE 2 #define BCFG_REMOVE 3 #define BCFG_ADD 4 #define BCFG_USAGE 5 typedef struct { UINT32 Attributes; CHAR16 *Description; EFI_DEVICE_PATH *FilePath; VOID *LoadOptions; UINTN LoadOptionsSize; CHAR16 *FilePathStr; } BCFG_LOAD_OPTION; /* * */ EFI_STATUS InitializeBCfg ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); VOID DumpFileInfo ( IN SHELL_FILE_ARG *Arg ); VOID BCfgDumpBootList ( IN CHAR16 *BootOrder, IN CHAR16 *BootOption ); BCFG_LOAD_OPTION * BCfgParseLoadOption ( UINT8 *Data, UINTN DataSize ); VOID BCfgFreeLoadOption ( BCFG_LOAD_OPTION *Option ); VOID BCfgSetOperation ( UINTN *OldOper, UINTN NewOper ); VOID BCfgUsage ( VOID ); VOID BCfgRemove ( IN UINTN Position ); VOID BCfgMove ( IN UINTN Src, IN UINTN Dest ); VOID BCfgAdd ( IN UINTN Position, IN CHAR16 *File, IN CHAR16 *Desc ); /* * */ BOOLEAN BCfgVerbose = FALSE; /* * Selected list */ CHAR16 *BCfgSelOrder; CHAR16 *BCfgSelOption; CHAR16 *BCfgSelName; UINT32 BCfgAttributes; /* * Scratch memory */ UINTN BCfgOrderCount; UINT16 *BCfgOrder; UINT8 *BCfgData; /* * */ EFI_DRIVER_ENTRY_POINT(InitializeBCfg) EFI_STATUS InitializeBCfg ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { CHAR16 **Argv; UINTN Argc; EFI_STATUS Status; UINTN Index, BufferSize; UINTN No1, No2; CHAR16 *p, *File, *Desc; UINTN Oper; /* * Check to see if the app is to install as a "internal command" * to the shell */ InstallInternalShellCommand ( ImageHandle, SystemTable, InitializeBCfg, L"bcfg", /* command */ L"bcfg -?", /* command syntax */ L"Configures boot driver & load options", /* 1 line descriptor */ NULL /* command help page */ ); /* * We are not being installed as an internal command driver, initialize * as an nshell app and run */ InitializeShellApplication (ImageHandle, SystemTable); Argv = SI->Argv; Argc = SI->Argc; BCfgVerbose = FALSE; BCfgSelName = NULL; BCfgSelOrder = NULL; BCfgOrderCount = 0; No1 = 0; No2 = 0; File = NULL; Desc = NULL; BCfgOrder = AllocatePool(MAX_ENV_SIZE + 32); BCfgData = AllocatePool(MAX_ENV_SIZE + 32); /* * Scan args for flags */ Oper = BCFG_NONE; for (Index = 1; Index < Argc; Index += 1) { p = Argv[Index]; if (StrCmp(p, L"?") == 0) { BCfgSetOperation (&Oper, BCFG_USAGE); } else if (StrCmp(p, L"driver") == 0) { BCfgSelOrder = VarDriverOrder; BCfgSelOption = VarDriverOption; BCfgSelName = L"boot driver"; } else if (StrCmp(p, L"boot") == 0) { BCfgSelOrder = VarBootOrder; BCfgSelOption = VarBootOption; BCfgSelName = L"boot option"; } else if (StrCmp(p, L"dump") == 0) { BCfgSetOperation (&Oper, BCFG_DUMP); } else if (StrCmp(p, L"v") == 0) { BCfgVerbose = TRUE; } else if (StrCmp(p, L"rm") == 0) { Index += 1; if (Index < Argc) { No1 = Atoi(Argv[Index]); } BCfgSetOperation (&Oper, BCFG_REMOVE); } else if (StrCmp(p, L"mv") == 0) { Index += 1; if (Index < Argc) { No1 = Atoi(Argv[Index]); } Index += 1; if (Index < Argc) { No2 = Atoi(Argv[Index]); } BCfgSetOperation (&Oper, BCFG_MOVE); } else if (StrCmp(p, L"add") == 0) { Index += 1; if (Index < Argc) { No1 = Atoi(Argv[Index]); } Index += 1; if (Index < Argc) { File = Argv[Index]; } Index += 1; if (Index < Argc) { Desc = Argv[Index]; } BCfgSetOperation (&Oper, BCFG_ADD); } else { Print (L"bfg: unknown flag '%h'\n", p); Oper = BCFG_USAGE; break; } } if (BCfgSelOrder) { /* * Read the boot order var */ BufferSize = MAX_ENV_SIZE; Status = RT->GetVariable ( BCfgSelOrder, &EfiGlobalVariable, &BCfgAttributes, &BufferSize, BCfgOrder ); if (EFI_ERROR(Status)) { BufferSize = 0; BCfgAttributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS; if (BCfgSelOrder == VarBootOrder) { BCfgAttributes = BCfgAttributes | EFI_VARIABLE_RUNTIME_ACCESS; } } BCfgOrderCount = BufferSize / sizeof(UINT16); } if (Oper == BCFG_NONE) { Oper = BCFG_USAGE; } if (Oper != BCFG_USAGE && !BCfgSelName) { Print (L"bcfg: must supply 'driver' or 'boot'\n"); Oper = BCFG_NONE; } switch (Oper) { case BCFG_NONE: break; case BCFG_USAGE: BCfgUsage(); break; case BCFG_DUMP: Print (L"The %s list is:\n", BCfgSelName); BCfgDumpBootList (BCfgSelOrder, BCfgSelOption); break; case BCFG_ADD: BCfgAdd (No1, File, Desc); break; case BCFG_MOVE: BCfgMove (No1, No2); break; case BCFG_REMOVE: BCfgRemove (No1); break; } /* * Done */ if (BCfgOrder) { FreePool (BCfgOrder); } if (BCfgData) { FreePool (BCfgData); } return EFI_SUCCESS; } VOID BCfgSetOperation ( UINTN *OldOper, UINTN NewOper ) { if (*OldOper != BCFG_NONE && *OldOper != BCFG_USAGE) { Print (L"bcfg: only one operation may be specified at a time\n"); *OldOper = BCFG_USAGE; } *OldOper = NewOper; } VOID BCfgUsage ( VOID ) { Print (L"bcfg driver|boot [dump [-v]] [add # file \"desc\"] [rm #] [mv # #]\n"); Print (L" driver selects boot driver list\n"); Print (L" boot selects boot option list\n"); Print (L" dump dumps selected list\n"); Print (L" v dumps verbose (includes load options)\n"); Print (L" add add 'file' with 'desc' at position #\n"); Print (L" rm remove #\n"); Print (L" mv move # to #\n"); } VOID BCfgAdd ( IN UINTN Position, IN CHAR16 *File, IN CHAR16 *Desc ) { EFI_STATUS Status; EFI_DEVICE_PATH *DevicePath, *FilePath, *FileNode; CHAR16 *Str, *p; UINT8 *p8; SHELL_FILE_ARG *Arg; LIST_ENTRY FileList; CHAR16 OptionStr[40]; UINTN DescSize, FilePathSize; BOOLEAN Found; UINTN Target, Index; Str = NULL; FilePath = NULL; FileNode = NULL; InitializeListHead (&FileList); if (Position < 1) { Position = 1; } Position = Position - 1; if (Position > BCfgOrderCount) { Position = BCfgOrderCount; } if (!File || !Desc) { Print (L"bcfg: missing parameter for 'add' operation\n"); Print (L"cfg: driver|boot add # file \"desc\"\n"); goto Done; } /* * Get file info */ ShellFileMetaArg (File, &FileList); /* * If filename expadned to multiple names, fail */ if (FileList.Flink->Flink != &FileList) { Print (L"bcfg: too many source files\n"); goto Done; } Arg = CR(FileList.Flink, SHELL_FILE_ARG, Link, SHELL_FILE_ARG_SIGNATURE); Status = Arg->Status; if (EFI_ERROR(Status)) { Print (L"bcfg: file %hs - %r\n", Arg->FileName, Status); goto Done; } /* * Build FilePath to the filename */ /* split full name at device string */ for(p=Arg->FullName; *p && *p != ':'; p++) ; if (!*p) { Print (L"bcfg: unsupported file name '%hs'\n", Arg->FullName); Status = EFI_UNSUPPORTED; goto Done; } /* get the device path */ *p = 0; DevicePath = (EFI_DEVICE_PATH *) ShellGetMap(Arg->FullName); if (!DevicePath) { Print (L"bcfg: no device path for %s\n", Arg->FullName); Status = EFI_UNSUPPORTED; goto Done; } /* append the file */ FileNode = FileDevicePath(NULL, p+1); FilePath = AppendDevicePath(DevicePath, FileNode); /* * Find a free target # (bugbug: brute force implementation) */ Found = FALSE; for (Target=1; Target < 0xFFFF; Target += 1) { Found = TRUE; for (Index=0; Index < BCfgOrderCount; Index += 1) { if (BCfgOrder[Index] == Target) { Found = FALSE; break; } } if (Found) { break; } } if (Target == 0xFFFF) { Print (L"bcfg: Failed to find available variable name\n"); goto Done; } Print (L"Target = %d\n", Target); /* * Add the option */ DescSize = StrSize(Desc); FilePathSize = DevicePathSize(FilePath); p8 = BCfgData; *((UINT32 *) p8) = 0; /* Attributes */ p8 += sizeof (UINT32); CopyMem (p8, Desc, DescSize); p8 += DescSize; CopyMem (p8, FilePath, FilePathSize); SPrint (OptionStr, sizeof(OptionStr), BCfgSelOption, Target); Status = RT->SetVariable ( OptionStr, &EfiGlobalVariable, BCfgAttributes, sizeof(UINT32) + DescSize + FilePathSize, BCfgData ); if (EFI_ERROR(Status)) { Print (L"bcfg: failed to add %hs - %hr\n", OptionStr, Status); goto Done; } /* * Insert target into order list */ BCfgOrderCount += 1; for (Index=BCfgOrderCount-1; Index > Position; Index -= 1) { BCfgOrder[Index] = BCfgOrder[Index-1]; } BCfgOrder[Position] = (UINT16) Target; Status = RT->SetVariable ( BCfgSelOrder, &EfiGlobalVariable, BCfgAttributes, BCfgOrderCount * sizeof(UINT16), BCfgOrder ); if (EFI_ERROR(Status)) { Print (L"bcfg: failed to update %hs - %hr\n", BCfgSelOrder, Status); goto Done; } /* * Done */ Print (L"bcfg: %s added as %d\n", BCfgSelName, Position+1); Done: if (FileNode) { FreePool (FileNode); } if (FilePath) { FreePool (FilePath); } if (Str) { FreePool(Str); } ShellFreeFileList (&FileList); } VOID BCfgRemove ( IN UINTN Position ) { CHAR16 OptionStr[40]; EFI_STATUS Status; UINTN Index; UINT16 Target; if (Position < 1 || Position > BCfgOrderCount) { Print (L"bcfg: %hd not removed. Value is out of range\n", Position); return ; } Target = BCfgOrder[Position-1]; /* * remove from order list */ BCfgOrderCount = BCfgOrderCount - 1; for (Index=Position-1; Index < BCfgOrderCount; Index += 1) { BCfgOrder[Index] = BCfgOrder[Index+1]; } Status = RT->SetVariable ( BCfgSelOrder, &EfiGlobalVariable, BCfgAttributes, BCfgOrderCount * sizeof(UINT16), BCfgOrder ); /* * Remove the option */ SPrint (OptionStr, sizeof(OptionStr), BCfgSelOption, Target); RT->SetVariable (OptionStr, &EfiGlobalVariable, BCfgAttributes, 0, NULL); /* * Done */ if (EFI_ERROR(Status)) { Print (L"bcfg: failed to remove - %hr\n", Status); } else { Print (L"bcfg: %s %d removed\n", BCfgSelName, Position); } } VOID BCfgMove ( IN UINTN Src, IN UINTN Dest ) { UINT16 Target; UINTN Index; EFI_STATUS Status; if (Src < 1 || Src > BCfgOrderCount) { Print (L"bcfg: %hd not moved. Value is out of range\n", Src); return ; } if (Dest < 1) { Dest = 1; } if (Dest > BCfgOrderCount) { Dest = BCfgOrderCount; } /* * */ Src = Src - 1; Dest = Dest - 1; Target = BCfgOrder[Src]; /* * Remove the item */ for (Index=Src; Index < BCfgOrderCount-1; Index += 1) { BCfgOrder[Index] = BCfgOrder[Index+1]; } /* * Insert it */ for (Index=BCfgOrderCount-1; Index > Dest; Index -= 1) { BCfgOrder[Index] = BCfgOrder[Index-1]; } BCfgOrder[Dest] = Target; /* * Update the order */ Status = RT->SetVariable ( BCfgSelOrder, &EfiGlobalVariable, BCfgAttributes, BCfgOrderCount * sizeof(UINT16), BCfgOrder ); /* * Done */ if (EFI_ERROR(Status)) { Print (L"bcfg: failed to move option - %hr\n", Status); } else { Print (L"bcfg: %s %d moved to %d\n", BCfgSelName, Src+1, Dest+1); } } VOID BCfgDumpBootList ( IN CHAR16 *BootOrder, IN CHAR16 *BootOption ) { EFI_STATUS Status; UINTN DataSize; UINT32 Attributes; CHAR16 OptionStr[40]; BCFG_LOAD_OPTION *Option; UINTN Index; for (Index=0; Index < BCfgOrderCount; Index++) { SPrint (OptionStr, sizeof(OptionStr), BootOption, BCfgOrder[Index]); DataSize = MAX_ENV_SIZE; Status = RT->GetVariable ( OptionStr, &EfiGlobalVariable, &Attributes, &DataSize, BCfgData ); Print (L"%02x. ", Index+1); if (!EFI_ERROR(Status)) { Option = BCfgParseLoadOption ((UINT8 *) BCfgData, DataSize); if (!Option) { Print (L"%Hcould not parse option%N\n"); continue; } Print (L"%s %H\"%ns\"%s%N\n", Option->FilePathStr, Option->Description, Option->LoadOptionsSize ? L" OPT" : L"" ); BCfgFreeLoadOption (Option); } else { Print (L"%hr\n", Status); } } } BCFG_LOAD_OPTION * BCfgParseLoadOption ( UINT8 *Data, UINTN DataSize ) { BCFG_LOAD_OPTION *Option; BOOLEAN Valid; UINT8 *End; EFI_DEVICE_PATH *DevicePathNode; Valid = FALSE; Option = AllocateZeroPool(sizeof(BCFG_LOAD_OPTION)); /* * Parse the load option into the Option structure */ if (DataSize < 10) { goto Done; } /* * First 32 bits are the load option attributes */ CopyMem (&Option->Attributes, Data, sizeof(UINT32)); Data += sizeof(UINT32); DataSize -= sizeof(UINT32); /* * Next is a null terminated string */ Option->Description = AllocatePool(DataSize); CopyMem (Option->Description, Data, DataSize); /* find the string terminator */ Data = (UINT8 *) Option->Description; End = Data + DataSize; while (*((CHAR16 *) Data)) { if (Data > End - sizeof(CHAR16) - 1) { goto Done; } Data += sizeof(UINT16); } Data += sizeof(UINT16); DataSize = End - Data; /* * Next is the file path */ Option->FilePath = AllocatePool (DataSize); CopyMem (Option->FilePath, Data, DataSize); /* find the end of path terminator */ DevicePathNode = (EFI_DEVICE_PATH *) Data; while (!IsDevicePathEnd (DevicePathNode)) { DevicePathNode = NextDevicePathNode (DevicePathNode); if ((UINT8 *) DevicePathNode > End - sizeof(EFI_DEVICE_PATH)) { goto Done; } } Data = ((UINT8 *) DevicePathNode) + sizeof(EFI_DEVICE_PATH); DataSize = End - Data; /* * Next is the load options */ if (DataSize) { Option->LoadOptions = Data; Option->LoadOptionsSize = DataSize; } /* * Expand the FilePath to a string */ Option->FilePathStr = DevicePathToStr(Option->FilePath); Valid = TRUE; Done: if (!Valid && Option) { BCfgFreeLoadOption (Option); Option = NULL; } return Option; } VOID BCfgFreeLoadOption ( BCFG_LOAD_OPTION *Option ) { if (Option->Description) { FreePool (Option->Description); } if (Option->FilePath) { FreePool (Option->FilePath); } if (Option->FilePathStr) { FreePool (Option->FilePathStr); } FreePool (Option); }