From 5223c1213506f3a8f3c120a6620258d2b071db84 Mon Sep 17 00:00:00 2001 From: Brendan Jackman Date: Fri, 24 Jan 2014 22:27:11 +0000 Subject: [PATCH] ShellPkg/Shell: Fix reporting of exit status in ShellProtocol.Execute When the exit status of the command run by the shell is other than SHELL_SUCCESS, the shell image will now exit with EFI_ABORTED, placing the commands exit status (which is a SHELL_STATUS) in ExitData. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Brendan Jackman Reviewed-by: Olivier Martin Reviewed-by: Jaben Carsey git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15180 6f19259b-4bc3-4df7-8a09-765794883524 --- ShellPkg/Application/Shell/Shell.c | 202 ++++++++++++++++----- ShellPkg/Application/Shell/Shell.h | 34 ++-- ShellPkg/Application/Shell/ShellProtocol.c | 58 +++++- ShellPkg/Application/Shell/ShellProtocol.h | 6 +- 4 files changed, 236 insertions(+), 64 deletions(-) diff --git a/ShellPkg/Application/Shell/Shell.c b/ShellPkg/Application/Shell/Shell.c index 134ec587d5..e56b3f8363 100644 --- a/ShellPkg/Application/Shell/Shell.c +++ b/ShellPkg/Application/Shell/Shell.c @@ -244,6 +244,9 @@ UefiMain ( UINTN Size; EFI_HANDLE ConInHandle; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *OldConIn; + UINTN ExitDataSize; + CHAR16 *ExitData; + SHELL_STATUS ExitStatus; if (PcdGet8(PcdShellSupportLevel) > 3) { return (EFI_UNSUPPORTED); @@ -406,7 +409,7 @@ UefiMain ( // Display the mapping // if (PcdGet8(PcdShellSupportLevel) >= 2 && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoMap) { - Status = RunCommand(L"map"); + Status = RunCommand(L"map", NULL); ASSERT_EFI_ERROR(Status); } @@ -472,7 +475,11 @@ UefiMain ( // // process the startup script or launch the called app. // - Status = DoStartupScript(ShellInfoObject.ImageDevPath, ShellInfoObject.FileDevPath); + Status = DoStartupScript( + ShellInfoObject.ImageDevPath, + ShellInfoObject.FileDevPath, + &ExitStatus + ); } if (!ShellInfoObject.ShellInitSettings.BitUnion.Bits.Exit && !ShellCommandGetExit() && (PcdGet8(PcdShellSupportLevel) >= 3 || PcdGetBool(PcdShellForceConsole)) && !EFI_ERROR(Status) && !ShellInfoObject.ShellInitSettings.BitUnion.Bits.NoConsoleIn) { @@ -505,6 +512,7 @@ UefiMain ( // Status = DoShellPrompt(); } while (!ShellCommandGetExit()); + ExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); } if (OldConIn != NULL && ConInHandle != NULL) { CloseSimpleTextInOnFile (gST->ConIn); @@ -575,10 +583,29 @@ UefiMain ( DEBUG_CODE(ShellInfoObject.ConsoleInfo = NULL;); } - if (ShellCommandGetExit()) { - return ((EFI_STATUS)ShellCommandGetExitCode()); + // If the command exited with an error, we pass this error out in the ExitData + // so that it can be retrieved by the EfiShellExecute function (which may + // start the shell with gBS->StartImage) + if (ExitStatus != SHELL_SUCCESS) { + // Allocate a buffer for exit data to pass to gBS->Exit(). + // This buffer will contain the empty string immediately followed by + // the shell's exit status. (The empty string is required by the UEFI spec) + ExitDataSize = (sizeof (CHAR16) + sizeof (SHELL_STATUS)); + ExitData = AllocatePool (ExitDataSize); + if (ExitData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + ExitData[0] = '\0'; + // Use CopyMem to avoid alignment faults + CopyMem ((ExitData + 1), &ExitStatus, sizeof (ExitStatus)); + + gBS->Exit (ImageHandle, EFI_ABORTED, ExitDataSize, ExitData); + } else { + return EFI_SUCCESS; } - return (Status); + + ASSERT (FALSE); + return EFI_SUCCESS; } /** @@ -872,13 +899,16 @@ ProcessCommandLine( @param ImagePath the path to the image for shell. first place to look for the startup script @param FilePath the path to the file for shell. second place to look for the startup script. + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + @retval EFI_SUCCESS the variable is initialized. **/ EFI_STATUS EFIAPI DoStartupScript( - EFI_DEVICE_PATH_PROTOCOL *ImagePath, - EFI_DEVICE_PATH_PROTOCOL *FilePath + IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -913,7 +943,7 @@ DoStartupScript( StrCat(FileStringPath, L" "); StrCat(FileStringPath, ShellInfoObject.ShellInitSettings.FileOptions); } - Status = RunCommand(FileStringPath); + Status = RunCommand(FileStringPath, ExitStatus); FreePool(FileStringPath); return (Status); @@ -990,7 +1020,13 @@ DoStartupScript( // If we got a file, run it // if (!EFI_ERROR(Status) && FileHandle != NULL) { - Status = RunScriptFile (mStartupScript, FileHandle, L"", ShellInfoObject.NewShellParametersProtocol); + Status = RunScriptFile ( + mStartupScript, + FileHandle, + L"", + ShellInfoObject.NewShellParametersProtocol, + ExitStatus + ); ShellInfoObject.NewEfiShellProtocol->CloseFile(FileHandle); } else { FileStringPath = ShellFindFilePath(mStartupScript); @@ -1001,7 +1037,13 @@ DoStartupScript( Status = EFI_SUCCESS; ASSERT(FileHandle == NULL); } else { - Status = RunScriptFile(FileStringPath, NULL, L"", ShellInfoObject.NewShellParametersProtocol); + Status = RunScriptFile( + FileStringPath, + NULL, + L"", + ShellInfoObject.NewShellParametersProtocol, + ExitStatus + ); FreePool(FileStringPath); } } @@ -1066,7 +1108,7 @@ DoShellPrompt ( // if (!EFI_ERROR (Status)) { CmdLine[BufferSize / sizeof (CHAR16)] = CHAR_NULL; - Status = RunCommand(CmdLine); + Status = RunCommand(CmdLine, NULL); } // @@ -1326,6 +1368,9 @@ ShellConvertVariables ( @param[in] StdIn The pointer to the Standard input. @param[in] StdOut The pointer to the Standard output. + @param[out] ExitStatus The exit code of the last command in the pipeline. + Ignored if NULL. + @retval EFI_SUCCESS The split command is executed successfully. @retval other Some error occurs when executing the split command. **/ @@ -1334,7 +1379,8 @@ EFIAPI RunSplitCommand( IN CONST CHAR16 *CmdLine, IN SHELL_FILE_HANDLE *StdIn, - IN SHELL_FILE_HANDLE *StdOut + IN SHELL_FILE_HANDLE *StdOut, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -1388,7 +1434,7 @@ RunSplitCommand( ASSERT(Split->SplitStdOut != NULL); InsertHeadList(&ShellInfoObject.SplitList.Link, &Split->Link); - Status = RunCommand(OurCommandLine); + Status = RunCommand(OurCommandLine, NULL); // // move the output from the first to the in to the second. @@ -1403,7 +1449,7 @@ RunSplitCommand( ShellInfoObject.NewEfiShellProtocol->SetFilePosition(ConvertShellHandleToEfiFileProtocol(Split->SplitStdIn), 0); if (!EFI_ERROR(Status)) { - Status = RunCommand(NextCommandLine); + Status = RunCommand(NextCommandLine, ExitStatus); } // @@ -1695,7 +1741,9 @@ VerifySplit( /** Process a split based operation. - @param[in] CmdLine pointer to the command line to process + @param[in] CmdLine Pointer to the command line to process + @param[out] ExitStatus The exit status of the command. Ignored if NULL. + Invalid if this function returns an error. @retval EFI_SUCCESS The operation was successful @return an error occured. @@ -1703,7 +1751,8 @@ VerifySplit( EFI_STATUS EFIAPI ProcessNewSplitCommandLine( - IN CONST CHAR16 *CmdLine + IN CONST CHAR16 *CmdLine, + OUT SHELL_STATUS *ExitStatus ) { SPLIT_LIST *Split; @@ -1724,9 +1773,14 @@ ProcessNewSplitCommandLine( } if (Split == NULL) { - Status = RunSplitCommand(CmdLine, NULL, NULL); + Status = RunSplitCommand(CmdLine, NULL, NULL, ExitStatus); } else { - Status = RunSplitCommand(CmdLine, Split->SplitStdIn, Split->SplitStdOut); + Status = RunSplitCommand( + CmdLine, + Split->SplitStdIn, + Split->SplitStdOut, + ExitStatus + ); } if (EFI_ERROR(Status)) { ShellPrintHiiEx(-1, -1, NULL, STRING_TOKEN (STR_SHELL_INVALID_SPLIT), ShellInfoObject.HiiHandle, CmdLine); @@ -1901,6 +1955,8 @@ ProcessCommandLineToFinal( @param[in] FirstParameter the first parameter on the command line @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] ExitStatus The exit code of the command. Ignored if NULL. + @retval EFI_SUCCESS The command was completed. @retval EFI_ABORTED The command's operation was aborted. **/ @@ -1909,7 +1965,8 @@ EFIAPI RunInternalCommand( IN CONST CHAR16 *CmdLine, IN CHAR16 *FirstParameter, - IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT SHELL_STATUS *ExitStatus OPTIONAL ) { EFI_STATUS Status; @@ -1936,6 +1993,9 @@ RunInternalCommand( if (LastError) { SetLastError(CommandReturnedStatus); } + if (ExitStatus != NULL) { + *ExitStatus = CommandReturnedStatus; + } // // Pass thru the exitcode from the app. @@ -1990,6 +2050,9 @@ RunInternalCommand( @param[in] FirstParameter the first parameter on the command line @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] ExitStatus The exit code of the command or file. + Ignored if NULL. + @retval EFI_SUCCESS The command was completed. @retval EFI_ABORTED The command's operation was aborted. **/ @@ -1999,13 +2062,14 @@ RunCommandOrFile( IN SHELL_OPERATION_TYPES Type, IN CONST CHAR16 *CmdLine, IN CHAR16 *FirstParameter, - IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; - EFI_STATUS StatusCode; CHAR16 *CommandWithPath; EFI_DEVICE_PATH_PROTOCOL *DevPath; + SHELL_STATUS CalleeExitStatus; Status = EFI_SUCCESS; CommandWithPath = NULL; @@ -2013,7 +2077,12 @@ RunCommandOrFile( switch (Type) { case Internal_Command: - Status = RunInternalCommand(CmdLine, FirstParameter, ParamProtocol); + Status = RunInternalCommand( + CmdLine, + FirstParameter, + ParamProtocol, + &CalleeExitStatus + ); break; case Script_File_Name: case Efi_Application: @@ -2048,7 +2117,13 @@ RunCommandOrFile( } switch (Type) { case Script_File_Name: - Status = RunScriptFile (CommandWithPath, NULL, CmdLine, ParamProtocol); + Status = RunScriptFile ( + CommandWithPath, + NULL, + CmdLine, + ParamProtocol, + &CalleeExitStatus + ); break; case Efi_Application: // @@ -2068,7 +2143,8 @@ RunCommandOrFile( DevPath, CmdLine, NULL, - &StatusCode + NULL, + NULL ); SHELL_FREE_NON_NULL(DevPath); @@ -2076,7 +2152,8 @@ RunCommandOrFile( // // Update last error status. // - SetLastError((SHELL_STATUS) StatusCode); + // Status is an EFI_STATUS. Clear top bit to convert to SHELL_STATUS + SetLastError((SHELL_STATUS) (Status & (~MAX_BIT))); break; default: // @@ -2094,6 +2171,10 @@ RunCommandOrFile( SHELL_FREE_NON_NULL(CommandWithPath); + if (ExitStatus != NULL) { + *ExitStatus = CalleeExitStatus; + } + return (Status); } @@ -2105,16 +2186,20 @@ RunCommandOrFile( @param[in] FirstParameter the first parameter on the command line. @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] ExitStatus The exit code of the command or file. + Ignored if NULL. + @retval EFI_SUCCESS The command was completed. @retval EFI_ABORTED The command's operation was aborted. **/ EFI_STATUS EFIAPI SetupAndRunCommandOrFile( - IN SHELL_OPERATION_TYPES Type, - IN CHAR16 *CmdLine, - IN CHAR16 *FirstParameter, - IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + IN SHELL_OPERATION_TYPES Type, + IN CHAR16 *CmdLine, + IN CHAR16 *FirstParameter, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -2133,7 +2218,13 @@ SetupAndRunCommandOrFile( // Now run the command, script, or application // if (!EFI_ERROR(Status)) { - Status = RunCommandOrFile(Type, CmdLine, FirstParameter, ParamProtocol); + Status = RunCommandOrFile( + Type, + CmdLine, + FirstParameter, + ParamProtocol, + ExitStatus + ); } // @@ -2158,6 +2249,7 @@ SetupAndRunCommandOrFile( command or dispatch an external application. @param[in] CmdLine The command line to parse. + @param[out] ExitStatus The exit code of the command. Ignored if NULL. @retval EFI_SUCCESS The command was completed. @retval EFI_ABORTED The command's operation was aborted. @@ -2165,7 +2257,8 @@ SetupAndRunCommandOrFile( EFI_STATUS EFIAPI RunCommand( - IN CONST CHAR16 *CmdLine + IN CONST CHAR16 *CmdLine, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -2207,7 +2300,7 @@ RunCommand( // We dont do normal processing with a split command line (output from one command input to another) // if (ContainsSplit(CleanOriginal)) { - Status = ProcessNewSplitCommandLine(CleanOriginal); + Status = ProcessNewSplitCommandLine(CleanOriginal, ExitStatus); SHELL_FREE_NON_NULL(CleanOriginal); return (Status); } @@ -2233,7 +2326,13 @@ RunCommand( case Internal_Command: case Script_File_Name: case Efi_Application: - Status = SetupAndRunCommandOrFile(Type, CleanOriginal, FirstParameter, ShellInfoObject.NewShellParametersProtocol); + Status = SetupAndRunCommandOrFile( + Type, + CleanOriginal, + FirstParameter, + ShellInfoObject.NewShellParametersProtocol, + ExitStatus + ); break; default: // @@ -2288,13 +2387,16 @@ IsValidCommandName( @param[in] Handle The handle to the already opened file. @param[in] Name The name of the script file. + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + @retval EFI_SUCCESS the script completed sucessfully **/ EFI_STATUS EFIAPI RunScriptFileHandle ( - IN SHELL_FILE_HANDLE Handle, - IN CONST CHAR16 *Name + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Name, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -2310,6 +2412,7 @@ RunScriptFileHandle ( CONST CHAR16 *CurDir; UINTN LineCount; CHAR16 LeString[50]; + SHELL_STATUS CalleeExitStatus = SHELL_SUCCESS; ASSERT(!ShellCommandGetScriptExit()); @@ -2495,7 +2598,7 @@ RunScriptFileHandle ( // PreCommandEchoState = ShellCommandGetEchoState(); ShellCommandSetEchoState(FALSE); - Status = RunCommand(CommandLine3+1); + Status = RunCommand(CommandLine3+1, NULL); // // If command was "@echo -off" or "@echo -on" then don't restore echo state @@ -2517,7 +2620,7 @@ RunScriptFileHandle ( } ShellPrintEx(-1, -1, L"%s\r\n", CommandLine2); } - Status = RunCommand(CommandLine3); + Status = RunCommand(CommandLine3, NULL); } } @@ -2525,7 +2628,8 @@ RunScriptFileHandle ( // // ShellCommandGetExitCode() always returns a UINT64 // - UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", ShellCommandGetExitCode()); + CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); + UnicodeSPrint(LeString, sizeof(LeString), L"0x%Lx", CalleeExitStatus); DEBUG_CODE(InternalEfiShellSetEnv(L"debuglasterror", LeString, TRUE);); InternalEfiShellSetEnv(L"lasterror", LeString, TRUE); @@ -2537,9 +2641,11 @@ RunScriptFileHandle ( break; } if (EFI_ERROR(Status)) { + CalleeExitStatus = (SHELL_STATUS) Status; break; } if (ShellCommandGetExit()) { + CalleeExitStatus = (SHELL_STATUS) ShellCommandGetExitCode(); break; } } @@ -2571,6 +2677,11 @@ RunScriptFileHandle ( if (ShellCommandGetCurrentScriptFile()==NULL) { ShellCommandSetEchoState(PreScriptEchoState); } + + if (ExitStatus != NULL) { + *ExitStatus = CalleeExitStatus; + } + return (EFI_SUCCESS); } @@ -2582,15 +2693,18 @@ RunScriptFileHandle ( @param[in] CmdLine the command line to run. @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + @retval EFI_SUCCESS the script completed sucessfully **/ EFI_STATUS EFIAPI RunScriptFile ( - IN CONST CHAR16 *ScriptPath, - IN SHELL_FILE_HANDLE Handle OPTIONAL, - IN CONST CHAR16 *CmdLine, - IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + IN CONST CHAR16 *ScriptPath, + IN SHELL_FILE_HANDLE Handle OPTIONAL, + IN CONST CHAR16 *CmdLine, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT SHELL_STATUS *ExitStatus ) { EFI_STATUS Status; @@ -2617,7 +2731,7 @@ RunScriptFile ( // // run it // - Status = RunScriptFileHandle(FileHandle, ScriptPath); + Status = RunScriptFileHandle(FileHandle, ScriptPath, ExitStatus); // // now close the file @@ -2625,7 +2739,7 @@ RunScriptFile ( ShellCloseFile(&FileHandle); } } else { - Status = RunScriptFileHandle(Handle, ScriptPath); + Status = RunScriptFileHandle(Handle, ScriptPath, ExitStatus); } } diff --git a/ShellPkg/Application/Shell/Shell.h b/ShellPkg/Application/Shell/Shell.h index 1962dd6392..72dde69645 100644 --- a/ShellPkg/Application/Shell/Shell.h +++ b/ShellPkg/Application/Shell/Shell.h @@ -230,14 +230,17 @@ ProcessCommandLine( @param[in] ImagePath The path to the image for shell. The first place to look for the startup script. @param[in] FilePath The path to the file for shell. The second place to look for the startup script. + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + Invalid when this function returns an error. @retval EFI_SUCCESS The variable is initialized. **/ EFI_STATUS EFIAPI DoStartupScript( - IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, - IN EFI_DEVICE_PATH_PROTOCOL *FilePath + IN EFI_DEVICE_PATH_PROTOCOL *ImagePath, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + OUT SHELL_STATUS *ExitStatus ); /** @@ -282,7 +285,8 @@ AddLineToCommandHistory( This will determine if the command line represents an internal shell command or dispatch an external application. - @param[in] CmdLine the command line to parse + @param[in] CmdLine the command line to parse + @param[out] ExitStatus The exit status of the command. Ignored if NULL. @retval EFI_SUCCESS the command was completed @retval EFI_ABORTED the command's operation was aborted @@ -290,7 +294,8 @@ AddLineToCommandHistory( EFI_STATUS EFIAPI RunCommand( - IN CONST CHAR16 *CmdLine + IN CONST CHAR16 *CmdLine, + OUT SHELL_STATUS *ExitStatus ); /** @@ -314,13 +319,17 @@ IsValidCommandName( @param[in] Handle The handle to the already opened file. @param[in] Name The name of the script file. + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + Invalid when this function returns an error. + @retval EFI_SUCCESS the script completed sucessfully **/ EFI_STATUS EFIAPI RunScriptFileHandle ( - IN SHELL_FILE_HANDLE Handle, - IN CONST CHAR16 *Name + IN SHELL_FILE_HANDLE Handle, + IN CONST CHAR16 *Name, + OUT SHELL_STATUS *ExitStatus ); /** @@ -331,17 +340,20 @@ RunScriptFileHandle ( @param[in] CmdLine the command line to run. @param[in] ParamProtocol the shell parameters protocol pointer + @param[out] ExitStatus The exit code of the script. Ignored if NULL. + Invalid when this function returns an error. + @retval EFI_SUCCESS the script completed sucessfully **/ EFI_STATUS EFIAPI RunScriptFile ( - IN CONST CHAR16 *ScriptPath, - IN SHELL_FILE_HANDLE Handle OPTIONAL, - IN CONST CHAR16 *CmdLine, - IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol + IN CONST CHAR16 *ScriptPath, + IN SHELL_FILE_HANDLE Handle OPTIONAL, + IN CONST CHAR16 *CmdLine, + IN EFI_SHELL_PARAMETERS_PROTOCOL *ParamProtocol, + OUT SHELL_STATUS *ExitStatus ); - #endif //_SHELL_INTERNAL_HEADER_ diff --git a/ShellPkg/Application/Shell/ShellProtocol.c b/ShellPkg/Application/Shell/ShellProtocol.c index c619ba15a1..7f5884f90b 100644 --- a/ShellPkg/Application/Shell/ShellProtocol.c +++ b/ShellPkg/Application/Shell/ShellProtocol.c @@ -1358,6 +1358,9 @@ EfiShellEnablePageBreak ( is NULL, then the current shell environment is used. @param StatusCode Points to the status code returned by the command. + @param[out] ExitDataSize ExitDataSize as returned from gBS->StartImage + @param[out] ExitData ExitData as returned from gBS->StartImage + @retval EFI_SUCCESS The command executed successfully. The status code returned by the command is pointed to by StatusCode. @retval EFI_INVALID_PARAMETER The parameters are invalid. @@ -1371,7 +1374,8 @@ InternalShellExecuteDevicePath( IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN CONST CHAR16 *CommandLine OPTIONAL, IN CONST CHAR16 **Environment OPTIONAL, - OUT EFI_STATUS *StatusCode OPTIONAL + OUT UINTN *ExitDataSize OPTIONAL, + OUT CHAR16 **ExitData OPTIONAL ) { EFI_STATUS Status; @@ -1379,6 +1383,16 @@ InternalShellExecuteDevicePath( EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; LIST_ENTRY OrigEnvs; EFI_SHELL_PARAMETERS_PROTOCOL ShellParamsProtocol; + UINTN InternalExitDataSize; + UINTN *ExitDataSizePtr; + + // ExitDataSize is not OPTIONAL for gBS->BootServices, provide somewhere for + // it to be dumped if the caller doesn't want it. + if (ExitData == NULL) { + ExitDataSizePtr = &InternalExitDataSize; + } else { + ExitDataSizePtr = ExitDataSize; + } if (ParentImageHandle == NULL) { return (EFI_INVALID_PARAMETER); @@ -1445,14 +1459,14 @@ InternalShellExecuteDevicePath( ///@todo initialize and install ShellInterface protocol on the new image for compatibility if - PcdGetBool(PcdShellSupportOldProtocols) // - // now start the image and if the caller wanted the return code pass it to them... + // now start the image, passing up exit data if the caller requested it // if (!EFI_ERROR(Status)) { - if (StatusCode != NULL) { - *StatusCode = gBS->StartImage(NewHandle, NULL, NULL); - } else { - Status = gBS->StartImage(NewHandle, NULL, NULL); - } + Status = gBS->StartImage( + NewHandle, + ExitDataSizePtr, + ExitData + ); } // @@ -1523,6 +1537,8 @@ EfiShellExecute( CHAR16 *Temp; EFI_DEVICE_PATH_PROTOCOL *DevPath; UINTN Size; + UINTN ExitDataSize; + CHAR16 *ExitData; if ((PcdGet8(PcdShellSupportLevel) < 1)) { return (EFI_UNSUPPORTED); @@ -1550,7 +1566,33 @@ EfiShellExecute( DevPath, Temp, (CONST CHAR16**)Environment, - StatusCode); + &ExitDataSize, + &ExitData); + + if (Status == EFI_ABORTED) { + // If the command exited with an error, the shell should put the exit + // status in ExitData, preceded by a null-terminated string. + ASSERT (ExitDataSize == StrSize (ExitData) + sizeof (SHELL_STATUS)); + + if (StatusCode != NULL) { + // Skip the null-terminated string + ExitData += StrLen (ExitData) + 1; + + // Use CopyMem to avoid alignment faults + CopyMem (StatusCode, ExitData, sizeof (SHELL_STATUS)); + + // Convert from SHELL_STATUS to EFI_STATUS + // EFI_STATUSes have top bit set when they are errors. + // (See UEFI Spec Appendix D) + if (*StatusCode != SHELL_SUCCESS) { + *StatusCode = (EFI_STATUS) *StatusCode | MAX_BIT; + } + } + FreePool (ExitData); + Status = EFI_SUCCESS; + } else if ((StatusCode != NULL) && !EFI_ERROR(Status)) { + *StatusCode = EFI_SUCCESS; + } // // de-allocate and return diff --git a/ShellPkg/Application/Shell/ShellProtocol.h b/ShellPkg/Application/Shell/ShellProtocol.h index 7493c9f49f..4920d82830 100644 --- a/ShellPkg/Application/Shell/ShellProtocol.h +++ b/ShellPkg/Application/Shell/ShellProtocol.h @@ -443,6 +443,9 @@ EfiShellEnablePageBreak ( variables with the format 'x=y', where x is the environment variable name and y is the value. If this is NULL, then the current shell environment is used. + @param[out] ExitDataSize ExitDataSize as returned from gBS->StartImage + @param[out] ExitData ExitData as returned from gBS->StartImage + @param StatusCode Points to the status code returned by the command. @retval EFI_SUCCESS The command executed successfully. The status code @@ -458,7 +461,8 @@ InternalShellExecuteDevicePath( IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, IN CONST CHAR16 *CommandLine OPTIONAL, IN CONST CHAR16 **Environment OPTIONAL, - OUT EFI_STATUS *StatusCode OPTIONAL + OUT UINTN *ExitDataSize OPTIONAL, + OUT CHAR16 **ExitData OPTIONAL ); /** -- 2.39.2