478 lines
11 KiB
C
478 lines
11 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|