/*++ Copyright (c) 1998 Intel Corporation Module Name: init.c Abstract: Shell Revision History --*/ #include "nshell.h" /* * Globals */ CHAR16 *ShellEnvPathName[] = { L"shellenv.efi", L"efi\\shellenv.efi", L"efi\\tools\\shellenv.efi", NULL } ; /* * Prototypes */ EFI_STATUS InitializeShell ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ); EFI_STATUS ShellLoadEnvDriver ( IN EFI_HANDLE ImageHandle ); EFI_STATUS NShellPrompt ( IN EFI_HANDLE ImageHandle ); BOOLEAN ParseLoadOptions( EFI_HANDLE ImageHandle, OUT CHAR16 **CommandLine, OUT CHAR16 **CurrentDir ); /* * */ EFI_DRIVER_ENTRY_POINT(InitializeShell) EFI_STATUS InitializeShell ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) /*++ Routine Description: Arguments: ImageHandle - The handle for this driver SystemTable - The system table Returns: --*/ { EFI_STATUS Status; EFI_HANDLE Handle; UINTN BufferSize; VOID *Junk; BOOLEAN IsRootInstance; CHAR16 *CommandLine; CHAR16 *CurrentDir; /* * The shell may be started as either: * 1. the first time with no shell environment loaded yet * 2. not the first time, but with a shell environment loaded * 3. as a child of a parent shell image */ IsRootInstance = FALSE; InitializeLib (ImageHandle, SystemTable); /* * If the shell environment is not loaded, load it now */ BufferSize = sizeof(Handle); Status = BS->LocateHandle(ByProtocol, &ShellEnvProtocol, NULL, &BufferSize, &Handle); if (EFI_ERROR(Status)) { Status = ShellLoadEnvDriver (ImageHandle); if (EFI_ERROR(Status)) { Print(L"Shell environment driver not loaded\n"); BS->Exit (ImageHandle, Status, 0, NULL); } } /* * Check to see if we're a child of a previous shell */ Status = BS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID*)&Junk); if (EFI_ERROR(Status)) { /* * Special case were the shell is being started directly (e.g., not * as a child of another shell) */ BufferSize = sizeof(Handle); Status = BS->LocateHandle(ByProtocol, &ShellEnvProtocol, NULL, &BufferSize, &Handle); ASSERT (!EFI_ERROR(Status)); Status = BS->HandleProtocol(Handle, &ShellEnvProtocol, (VOID*)&SE); ASSERT (!EFI_ERROR(Status)); /* * Allocate a new shell interface structure, and assign it to our * image handle */ SI = SE->NewShell(ImageHandle); Status = LibInstallProtocolInterfaces (&ImageHandle, &ShellInterfaceProtocol, SI, NULL); ASSERT (!EFI_ERROR(Status)); IsRootInstance = TRUE; } /* * Now we can initialize like a normal shell app */ InitializeShellApplication (ImageHandle, SystemTable); /* * If there are load options, assume they contain a command line and * possible current working directory */ if (ParseLoadOptions (ImageHandle, &CommandLine, &CurrentDir)) { /* * Skip the 1st argument which should be us. */ while (*CommandLine != L' ' && *CommandLine != 0) { CommandLine++; } /* * Get to the beginning of the next argument. */ while (*CommandLine == L' ') { CommandLine++; } /* * If there was a current working directory, set it. */ if (CurrentDir) { CHAR16 CmdLine[256], *Tmp; /* * Set a mapping */ StrCpy (CmdLine, CurrentDir); for (Tmp = CmdLine; *Tmp && *Tmp != L':'; Tmp++) ; if ( *Tmp ) { *(++Tmp) = 0; ShellExecute (ImageHandle, CmdLine, TRUE); } /* * Now change to that directory */ StrCpy (CmdLine, L"cd "); if ((StrLen (CmdLine) + StrLen (CurrentDir) + sizeof(CHAR16)) < (sizeof(CmdLine) / sizeof(CHAR16))) { StrCat (CmdLine, CurrentDir); ShellExecute (ImageHandle, CmdLine, TRUE); } } /* * Have the shell execute the remaining command line. If there is * nothing remaining, run the shell main loop below. */ if ( *CommandLine != 0 ) return (ShellExecute (ImageHandle, CommandLine, TRUE)); } /* * If this is the root instance, execute the command to load the default values */ if (IsRootInstance) { Print (L"%EEFI Shell version %01d.%02d [%d.%d]\n%N", (ST->Hdr.Revision >> 16), (ST->Hdr.Revision & 0xffff), (ST->FirmwareRevision >> 16), (ST->FirmwareRevision & 0xffff)); ShellExecute (ImageHandle, L"_load_defaults", TRUE); /* dump device mappings, -r to sync with current hardware */ ShellExecute (ImageHandle, L"map -r", TRUE); /* run startup script (if any) */ /* * BugBug: I turned on echo so you can tell the startup.nsh is running * * ShellExecute (ImageHandle, L"echo -off", FALSE); */ ShellExecute (ImageHandle, L"startup.nsh", FALSE); /* ShellExecute (ImageHandle, L"echo -on", FALSE); */ } /* * EFI Shell main loop */ Status = EFI_SUCCESS; while (Status != -1) { Status = NShellPrompt (ImageHandle); } /* * Done - cleanup the shell */ Status = EFI_SUCCESS; Print (L"Shell exit - %r\n", Status); /* * If this was a root instance, we allocate a dumby shell interface for ourselves * free it now */ if (IsRootInstance) { BS->UninstallProtocolInterface (ImageHandle, &ShellInterfaceProtocol, SI); FreePool (SI); } return Status; } EFI_STATUS ShellLoadEnvDriverByPath ( IN EFI_HANDLE ParentImageHandle, IN EFI_HANDLE DeviceHandle ) { EFI_STATUS Status; EFI_DEVICE_PATH *FilePath; EFI_HANDLE NewImageHandle; UINTN Index; BOOLEAN SearchNext; /* * If there's no device to search forget it */ if (!DeviceHandle) { return EFI_NOT_FOUND; } /* * Try loading shellenv from each path */ SearchNext = TRUE; for (Index=0; ShellEnvPathName[Index] && SearchNext; Index++) { /* * Load it */ FilePath = FileDevicePath (DeviceHandle, ShellEnvPathName[Index]); ASSERT (FilePath); Status = BS->LoadImage(FALSE, ParentImageHandle, FilePath, NULL, 0, &NewImageHandle); FreePool (FilePath); /* * Only search the next path if it was not found on this path */ SearchNext = FALSE; if (Status == EFI_LOAD_ERROR || Status == EFI_NOT_FOUND) { SearchNext = TRUE; } /* * If there was no error, start the image */ if (!EFI_ERROR(Status)) { Status = BS->StartImage(NewImageHandle, NULL, 0); } } return Status; } EFI_STATUS ShellLoadEnvDriver ( IN EFI_HANDLE ImageHandle ) { EFI_STATUS Status; EFI_LOADED_IMAGE *Image; UINTN Index, NoHandles; EFI_HANDLE *Handles; /* * Get the file path for the current image */ Status = BS->HandleProtocol (ImageHandle, &LoadedImageProtocol, (VOID*)&Image); ASSERT (!EFI_ERROR(Status)); /* * Attempt to load shellenv */ Status = ShellLoadEnvDriverByPath (Image->ParentHandle, Image->DeviceHandle); if (EFI_ERROR(Status)) { /* * shellenv was not found. Search all filesystems for it */ Status = LibLocateHandle (ByProtocol, &FileSystemProtocol, NULL, &NoHandles, &Handles); for (Index=0; Index < NoHandles; Index++) { Status = ShellLoadEnvDriverByPath (Image->ParentHandle, Handles[Index]); if (!EFI_ERROR(Status)) { break; } } if (Handles) { FreePool (Handles); } } /* * Done */ return Status; } EFI_STATUS NShellPrompt ( IN EFI_HANDLE ImageHandle ) { CHAR16 CmdLine[256]; CHAR16 *CurDir; UINTN BufferSize; EFI_STATUS Status; /* * Prompt for input */ CurDir = ShellCurDir(NULL); if (CurDir) { Print (L"%E%s> ", CurDir); FreePool (CurDir); } else { Print (L"%EShell> "); } /* * Read a line from the console */ BufferSize = sizeof(CmdLine)-1; Status = SI->StdIn->Read (SI->StdIn, &BufferSize, CmdLine); /* * Null terminate the string and parse it */ if (!EFI_ERROR(Status)) { CmdLine[BufferSize/sizeof(CHAR16)] = 0; Status = ShellExecute (ImageHandle, CmdLine, TRUE); } /* * Done with this command */ return Status; } BOOLEAN ParseLoadOptions( EFI_HANDLE ImageHandle, OUT CHAR16 **CommandLine, OUT CHAR16 **CurrentDir ) { EFI_LOADED_IMAGE *Image; EFI_STATUS Status; /* * Set defaults. */ *CommandLine = NULL; *CurrentDir = NULL; Status = BS->HandleProtocol (ImageHandle, &LoadedImageProtocol, (VOID*)&Image); if (!EFI_ERROR(Status)) { CHAR16 *CmdLine = Image->LoadOptions; UINT32 CmdSize = Image->LoadOptionsSize & ~1; /* make sure it is power of 2 */ if (CmdLine && CmdSize) { /* * Set command line pointer for caller */ *CommandLine = CmdLine; /* * See if current working directory was passed. */ while ((*CmdLine != 0) && CmdSize) { CmdLine++; CmdSize -= sizeof(CHAR16); } /* * If a current working directory was passed, set it. */ if (CmdSize > sizeof(CHAR16)) { *CurrentDir = ++CmdLine; } return TRUE; } } return FALSE; }