/** Write to IO space Argv[0] - "iowrite"[.#] # is optional width 1, 2, or 4. Default 1 Argv[1] - Hex IO address Argv[2] - Hex data to write iow.4 0x3f8 af ;Do a 32-bit IO write of af to 0x3f8 iow 0x3f8 af ;Do an 8-bit IO write of af to 0x3f8 @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblIoWriteCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Width; UINTN Port; UINTN Data; if (Argc < 3) { return EFI_INVALID_PARAMETER; } Port = AsciiStrHexToUintn (Argv[1]); Data = AsciiStrHexToUintn (Argv[2]); Width = WidthFromCommandName (Argv[0], 1); if (Width == 1) { IoWrite8 (Port, (UINT8)Data); } else if (Width == 2) { IoWrite16 (Port, (UINT16)Data); } else if (Width == 4) { IoWrite32 (Port, (UINT32)Data); } else { return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; }
/** Simple arm disassembler via a library Argv[0] - disasm Argv[1] - Address to start disassembling from ARgv[2] - Number of instructions to disassembly (optional) @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the comamnd name @return EFI_SUCCESS **/ EFI_STATUS EblDisassembler ( IN UINTN Argc, IN CHAR8 **Argv ) { UINT8 *Ptr, *CurrentAddress; UINT32 Address; UINT32 Count; CHAR8 Buffer[80]; UINT32 ItBlock; if (Argc < 2) { return EFI_INVALID_PARAMETER; } Address = AsciiStrHexToUintn (Argv[1]); Count = (Argc > 2) ? (UINT32)AsciiStrHexToUintn (Argv[2]) : 20; Ptr = (UINT8 *)(UINTN)Address; ItBlock = 0; do { CurrentAddress = Ptr; DisassembleInstruction (&Ptr, TRUE, TRUE, &ItBlock, Buffer, sizeof (Buffer)); AsciiPrint ("0x%08x: %a\n", CurrentAddress, Buffer); } while (Count-- > 0); return EFI_SUCCESS; }
/** ‘P n...=r...’ Writes the new value of n-th register received into the input buffer to the n-th register @param SystemContext Register content at time of the exception @param InBuffer Ponter to the input buffer received from gdb server **/ VOID EFIAPI WriteNthRegister ( IN EFI_SYSTEM_CONTEXT SystemContext, IN CHAR8 *InBuffer ) { UINTN RegNumber; CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array CHAR8 *RegNumBufPtr; CHAR8 *InBufPtr; // pointer to the input buffer // find the register number to write InBufPtr = &InBuffer[1]; RegNumBufPtr = RegNumBuffer; while (*InBufPtr != '=') { *RegNumBufPtr++ = *InBufPtr++; } *RegNumBufPtr = '\0'; RegNumber = AsciiStrHexToUintn (RegNumBuffer); // check if this is a valid Register Number if ((RegNumber < 0) || (RegNumber >= sizeof (gRegisterOffsets)/sizeof (UINTN))) { SendError (GDB_EINVALIDREGNUM); return; } InBufPtr++; // skips the '=' character BasicWriteRegister (SystemContext, RegNumber, InBufPtr); SendSuccess(); }
/** Read from IO space Argv[0] - "ioread"[.#] # is optional width 1, 2, or 4. Default 1 Argv[1] - Hex IO address ior.4 0x3f8 ;Do a 32-bit IO Read from 0x3f8 ior 0x3f8 ;Do a 8-bit IO Read from 0x3f8 @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblIoReadCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { UINTN Width; UINTN Port; UINTN Data; if (Argc < 2) { return EFI_INVALID_PARAMETER; } Port = AsciiStrHexToUintn (Argv[1]); Width = WidthFromCommandName (Argv[0], 1); if (Width == 1) { Data = IoRead8 (Port); } else if (Width == 2) { Data = IoRead16 (Port); } else if (Width == 4) { Data = IoRead32 (Port); } else { return EFI_INVALID_PARAMETER; } AsciiPrint ("0x%04x = 0x%x", Port, Data); return EFI_SUCCESS; }
/** Get section entry heximal UINTN value. @param[in] Context INI Config file context. @param[in] SectionName Section name. @param[in] EntryName Section entry name. @param[out] Data Point to the got heximal UINTN value. @retval EFI_SUCCESS Section entry heximal UINTN value is got. @retval EFI_NOT_FOUND Section is not found. **/ EFI_STATUS EFIAPI GetHexUintnFromDataFile ( IN VOID *Context, IN CHAR8 *SectionName, IN CHAR8 *EntryName, OUT UINTN *Data ) { CHAR8 *Value; EFI_STATUS Status; if (Context == NULL || SectionName == NULL || EntryName == NULL || Data == NULL) { return EFI_INVALID_PARAMETER; } Status = GetStringFromDataFile( Context, SectionName, EntryName, &Value ); if (EFI_ERROR(Status)) { return EFI_NOT_FOUND; } ASSERT (Value != NULL); if (!IsValidHexString(Value, AsciiStrLen(Value))) { return EFI_NOT_FOUND; } *Data = AsciiStrHexToUintn(Value); return EFI_SUCCESS; }
/** Receive a GDB Remote Serial Protocol Packet $PacketData#checksum PacketData is passed in and this function adds the packet prefix '$', the packet teminating character '#' and the two digit checksum. If host re-starts sending a packet without ending the previous packet, only the last valid packet is processed. (In other words, if received packet is '$12345$12345$123456#checksum', only '$123456#checksum' will be processed.) If an ack '+' is not sent resend the packet @param PacketData Payload data for the packet @retval Number of bytes of packet data received. **/ UINTN ReceivePacket ( OUT CHAR8 *PacketData, IN UINTN PacketDataSize ) { UINT8 CheckSum; UINTN Index; CHAR8 Char; CHAR8 SumString[3]; CHAR8 TestChar; ZeroMem (PacketData, PacketDataSize); for (;;) { // wait for the start of a packet TestChar = GdbGetChar (); while (TestChar != '$') { TestChar = GdbGetChar (); }; retry: for (Index = 0, CheckSum = 0; Index < (PacketDataSize - 1); Index++) { Char = GdbGetChar (); if (Char == '$') { goto retry; } if (Char == '#') { break; } PacketData[Index] = Char; CheckSum = CheckSum + Char; } PacketData[Index] = '\0'; if (Index == PacketDataSize) { continue; } SumString[0] = GdbGetChar (); SumString[1] = GdbGetChar (); SumString[2] = '\0'; if (AsciiStrHexToUintn (SumString) == CheckSum) { // Ack: Success GdbPutChar ('+'); // Null terminate the callers string PacketData[Index] = '\0'; return Index; } else { // Ack: Failure GdbPutChar ('-'); } } //return 0; }
/** ‘c [addr ]’ Continue. addr is Address to resume. If addr is omitted, resume at current Address. @param SystemContext Register content at time of the exception **/ VOID EFIAPI ContinueAtAddress ( IN EFI_SYSTEM_CONTEXT SystemContext, IN CHAR8 *PacketData ) { if (PacketData[1] != '\0') { SystemContext.SystemContextIa32->Eip = AsciiStrHexToUintn (&PacketData[1]); } }
/** ‘s [addr ]’ Single step. addr is the Address at which to resume. If addr is omitted, resume at same Address. @param SystemContext Register content at time of the exception **/ VOID EFIAPI SingleStep ( IN EFI_SYSTEM_CONTEXT SystemContext, IN CHAR8 *PacketData ) { if (PacketData[1] != '\0') { SystemContext.SystemContextX64->Rip = AsciiStrHexToUintn (&PacketData[1]); } AddSingleStep (SystemContext); }
/** Parse the F reply packet and extract the return value and an ErrNo if it exists. @param Packet Packet to parse like an F reply packet @param ErrNo Buffer to hold Count bytes that were read @retval -1 Error, not a valid F reply packet @retval other Return the return code from the F reply packet **/ INTN GdbParseFReplyPacket ( IN CHAR8 *Packet, OUT UINTN *ErrNo ) { INTN RetCode; if (Packet[0] != 'F') { // A valid responce would be an F packet return -1; } RetCode = AsciiStrHexToUintn (&Packet[1]); // Find 1st comma for (;*Packet != '\0' && *Packet != ','; Packet++); if (*Packet == '\0') { *ErrNo = 0; return RetCode; } *ErrNo = AsciiStrHexToUintn (++Packet); // Find 2nd comma for (;*Packet != '\0' && *Packet != ','; Packet++); if (*Packet == '\0') { return RetCode; } if (*(++Packet) == 'C') { GdbCtrlCBreakMessage (*ErrNo); } return RetCode; }
/** Convert the dec or hex ascii string to value. @param Str ascii string to be converted. @return the converted value. **/ UINTN UpdateAtoi ( IN UINT8 *Str ) { UINTN Number; Number = 0; // // Skip preceeding while spaces // while (*Str != '\0') { if (*Str != 0x20) { break; } Str++; } if (*Str == '\0') { return Number; } // // Find whether the string is prefixed by 0x. // That is, it should be xtoi or atoi. // if (*Str == '0') { if ((*(Str+1) == 'x' ) || ( *(Str+1) == 'X')) { return AsciiStrHexToUintn ((CONST CHAR8 *) Str); } } while (*Str != '\0') { if ((*Str >= '0') && (*Str <= '9')) { Number = Number * 10 + *Str - '0'; } else { break; } Str++; } return Number; }
/** Decode a percent-encoded URI component to the ASCII character. Decode the input component in Buffer according to RFC 3986. The caller is responsible to make sure ResultBuffer points to a buffer with size equal or greater than ((AsciiStrSize (Buffer)) in bytes. @param[in] Buffer The pointer to a percent-encoded URI component. @param[in] BufferLength Length of Buffer in bytes. @param[out] ResultBuffer Point to the buffer to store the decode result. @param[out] ResultLength Length of decoded string in ResultBuffer in bytes. @retval EFI_SUCCESS Successfully decoded the URI. @retval EFI_INVALID_PARAMETER Buffer is not a valid percent-encoded string. **/ EFI_STATUS EFIAPI UriPercentDecode ( IN CHAR8 *Buffer, IN UINT32 BufferLength, OUT CHAR8 *ResultBuffer, OUT UINT32 *ResultLength ) { UINTN Index; UINTN Offset; CHAR8 HexStr[3]; if (Buffer == NULL || BufferLength == 0 || ResultBuffer == NULL) { return EFI_INVALID_PARAMETER; } Index = 0; Offset = 0; HexStr[2] = '\0'; while (Index < BufferLength) { if (Buffer[Index] == '%') { if (!NET_IS_HEX_CHAR (Buffer[Index+1]) || !NET_IS_HEX_CHAR (Buffer[Index+2])) { return EFI_INVALID_PARAMETER; } HexStr[0] = Buffer[Index+1]; HexStr[1] = Buffer[Index+2]; ResultBuffer[Offset] = (CHAR8) AsciiStrHexToUintn (HexStr); Index += 3; } else { ResultBuffer[Offset] = Buffer[Index]; Index++; } Offset++; } *ResultLength = (UINT32) Offset; return EFI_SUCCESS; }
/** ‘p n’ Reads the n-th register's value into an output buffer and sends it as a packet @param SystemContext Register content at time of the exception @param InBuffer Pointer to the input buffer received from gdb server **/ VOID ReadNthRegister ( IN EFI_SYSTEM_CONTEXT SystemContext, IN CHAR8 *InBuffer ) { UINTN RegNumber; CHAR8 OutBuffer[17]; // 1 reg=16 hex chars, and the end '\0' (escape seq) CHAR8 *OutBufPtr; // pointer to the output buffer RegNumber = AsciiStrHexToUintn (&InBuffer[1]); if ((RegNumber < 0) || (RegNumber >= MaxRegisterCount())) { SendError (GDB_EINVALIDREGNUM); return; } OutBufPtr = OutBuffer; OutBufPtr = BasicReadRegister(SystemContext, RegNumber, OutBufPtr); *OutBufPtr = '\0'; // the end of the buffer SendPacket (OutBuffer); }
/** Load a file into memory and optionally jump to it. A load address can be specified or automatically allocated. A quoted command line can optionally be passed into the image. Argv[0] - "go" Argv[1] - Device Name:path for the file to load Argv[2] - Address to load to or '*' if the load address will be allocated Argv[3] - Optional Entry point to the image. Image will be called if present Argv[4] - "" string that will be passed as Argc & Argv to EntryPoint. Needs to include the command name go fv1:\EblCmdX 0x10000 0x10010 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX from FV1 to location 0x10000 and call the entry point at 0x10010 passing in "EblCmdX Arg2 Arg3 Arg4" as the arguments. go fv0:\EblCmdX * 0x10 "EblCmdX Arg2 Arg3 Arg4"; - load EblCmdX from FS0 to location allocated by this command and call the entry point at offset 0x10 passing in "EblCmdX Arg2 Arg3 Arg4" as the arguments. go fv1:\EblCmdX 0x10000; Load EblCmdX to address 0x10000 and return @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EblGoCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_STATUS Status; EFI_OPEN_FILE *File; VOID *Address; UINTN Size; EBL_COMMMAND EntryPoint; UINTN EntryPointArgc; CHAR8 *EntryPointArgv[MAX_ARGS]; if (Argc <= 2) { // device name and laod address are required return EFI_SUCCESS; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { AsciiPrint (" %a is not a valid path\n", Argv[1]); return EFI_SUCCESS; } EntryPoint = (EBL_COMMMAND)((Argc > 3) ? (UINTN)AsciiStrHexToUintn (Argv[3]) : (UINTN)NULL); if (Argv[2][0] == '*') { // * Means allocate the buffer Status = EfiReadAllocatePool (File, &Address, &Size); // EntryPoint is relative to the start of the image EntryPoint = (EBL_COMMMAND)((UINTN)EntryPoint + (UINTN)Address); } else { Address = (VOID *)AsciiStrHexToUintn (Argv[2]); Size = File->Size; // File->Size for LoadFile is lazy so we need to use the tell to figure it out EfiTell (File, NULL); Status = EfiRead (File, Address, &Size); } if (!EFI_ERROR (Status)) { AsciiPrint ("Loaded %,d bytes to 0x%08x\n", Size, Address); if (Argc > 3) { if (Argc > 4) { ParseArguments (Argv[4], &EntryPointArgc, EntryPointArgv); } else { EntryPointArgc = 1; EntryPointArgv[0] = File->FileName; } Status = EntryPoint (EntryPointArgc, EntryPointArgv); } } EfiClose (File); return Status; }
/** Convert the input ascii string into GUID value. @param Str Ascii GUID string to be converted. @param Guid Pointer to the converted GUID value. @retval EFI_OUT_OF_RESOURCES No enough memory is allocated. @retval EFI_NOT_FOUND The input ascii string is not a valid GUID format string. @retval EFI_SUCCESS GUID value is got. **/ EFI_STATUS UpdateStringToGuid ( IN UINT8 *Str, IN OUT EFI_GUID *Guid ) { UINT8 *PtrBuffer; UINT8 *PtrPosition; UINT8 *Buffer; UINTN Data; UINTN StrLen; UINTN Index; UINT8 Digits[3]; StrLen = AsciiStrLen ((CONST CHAR8 *) Str); Buffer = AllocateCopyPool (StrLen + 1, Str); if (Buffer == NULL) { return EFI_OUT_OF_RESOURCES; } // // Data1 // PtrBuffer = Buffer; PtrPosition = PtrBuffer; while (*PtrBuffer != '\0') { if (*PtrBuffer == '-') { break; } PtrBuffer++; } if (*PtrBuffer == '\0') { FreePool (Buffer); return EFI_NOT_FOUND; } *PtrBuffer = '\0'; Data = AsciiStrHexToUintn ((CONST CHAR8 *) PtrPosition); Guid->Data1 = (UINT32)Data; // // Data2 // PtrBuffer++; PtrPosition = PtrBuffer; while (*PtrBuffer != '\0') { if (*PtrBuffer == '-') { break; } PtrBuffer++; } if (*PtrBuffer == '\0') { FreePool (Buffer); return EFI_NOT_FOUND; } *PtrBuffer = '\0'; Data = AsciiStrHexToUintn ((CONST CHAR8 *) PtrPosition); Guid->Data2 = (UINT16)Data; // // Data3 // PtrBuffer++; PtrPosition = PtrBuffer; while (*PtrBuffer != '\0') { if (*PtrBuffer == '-') { break; } PtrBuffer++; } if (*PtrBuffer == '\0') { FreePool (Buffer); return EFI_NOT_FOUND; } *PtrBuffer = '\0'; Data = AsciiStrHexToUintn ((CONST CHAR8 *) PtrPosition); Guid->Data3 = (UINT16)Data; // // Data4[0..1] // for ( Index = 0 ; Index < 2 ; Index++) { PtrBuffer++; if ((*PtrBuffer == '\0') || ( *(PtrBuffer + 1) == '\0')) { FreePool (Buffer); return EFI_NOT_FOUND; } Digits[0] = *PtrBuffer; PtrBuffer++; Digits[1] = *PtrBuffer; Digits[2] = '\0'; Data = AsciiStrHexToUintn ((CONST CHAR8 *) Digits); Guid->Data4[Index] = (UINT8)Data; } // // skip the '-' // PtrBuffer++; if ((*PtrBuffer != '-' ) || ( *PtrBuffer == '\0')) { return EFI_NOT_FOUND; } // // Data4[2..7] // for ( ; Index < 8; Index++) { PtrBuffer++; if ((*PtrBuffer == '\0') || ( *(PtrBuffer + 1) == '\0')) { FreePool (Buffer); return EFI_NOT_FOUND; } Digits[0] = *PtrBuffer; PtrBuffer++; Digits[1] = *PtrBuffer; Digits[2] = '\0'; Data = AsciiStrHexToUintn ((CONST CHAR8 *) Digits); Guid->Data4[Index] = (UINT8)Data; } FreePool (Buffer); return EFI_SUCCESS; }
/** Parse Config data file to get the updated data array. @param DataBuffer Config raw file buffer. @param BufferSize Size of raw buffer. @param NumOfUpdates Pointer to the number of update data. @param UpdateArray Pointer to the config of update data. @retval EFI_NOT_FOUND No config data is found. @retval EFI_OUT_OF_RESOURCES No enough memory is allocated. @retval EFI_SUCCESS Parse the config file successfully. **/ EFI_STATUS ParseUpdateDataFile ( IN UINT8 *DataBuffer, IN UINTN BufferSize, IN OUT UINTN *NumOfUpdates, IN OUT UPDATE_CONFIG_DATA **UpdateArray ) { EFI_STATUS Status; CHAR8 *Value; CHAR8 *SectionName; CHAR8 Entry[MAX_LINE_LENGTH]; SECTION_ITEM *SectionHead; COMMENT_LINE *CommentHead; UINTN Num; UINTN Index; EFI_GUID FileGuid; SectionHead = NULL; CommentHead = NULL; // // First process the data buffer and get all sections and entries // Status = PreProcessDataFile ( DataBuffer, BufferSize, &SectionHead, &CommentHead ); if (EFI_ERROR (Status)) { FreeAllList (SectionHead, CommentHead); return Status; } // // Now get NumOfUpdate // Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) "Head", (UINT8 *) "NumOfUpdate", (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } Num = UpdateAtoi((UINT8 *) Value); if (Num <= 0) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } *NumOfUpdates = Num; *UpdateArray = AllocatePool ((sizeof (UPDATE_CONFIG_DATA) * Num)); if (*UpdateArray == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_OUT_OF_RESOURCES; } for ( Index = 0 ; Index < *NumOfUpdates ; Index++) { // // Get the section name of each update // AsciiStrCpyS (Entry, MAX_LINE_LENGTH, "Update"); UpdateStrCatNumber ((UINT8 *) Entry, Index); Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) "Head", (UINT8 *) Entry, (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } // // The section name of this update has been found. // Now looks for all the config data of this update // SectionName = Value; // // UpdateType // Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) SectionName, (UINT8 *) "UpdateType", (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } Num = UpdateAtoi((UINT8 *) Value); if (( Num >= (UINTN) UpdateOperationMaximum)) { FreeAllList (SectionHead, CommentHead); return Status; } (*UpdateArray)[Index].Index = Index; (*UpdateArray)[Index].UpdateType = (UPDATE_OPERATION_TYPE) Num; // // FvBaseAddress // Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) SectionName, (UINT8 *) "FvBaseAddress", (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } Num = AsciiStrHexToUintn ((CONST CHAR8 *) Value); (*UpdateArray)[Index].BaseAddress = (EFI_PHYSICAL_ADDRESS) Num; // // FileBuid // Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) SectionName, (UINT8 *) "FileGuid", (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } Status = UpdateStringToGuid ((UINT8 *) Value, &FileGuid); if (EFI_ERROR (Status)) { FreeAllList (SectionHead, CommentHead); return Status; } CopyMem (&((*UpdateArray)[Index].FileGuid), &FileGuid, sizeof(EFI_GUID)); // // FaultTolerant // Default value is FALSE // Value = NULL; (*UpdateArray)[Index].FaultTolerant = FALSE; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) SectionName, (UINT8 *) "FaultTolerant", (UINT8 **) &Value ); if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) { FreeAllList (SectionHead, CommentHead); return Status; } else if (Value != NULL) { if (AsciiStriCmp ((CONST CHAR8 *) Value, (CONST CHAR8 *) "TRUE") == 0) { (*UpdateArray)[Index].FaultTolerant = TRUE; } else if (AsciiStriCmp ((CONST CHAR8 *) Value, (CONST CHAR8 *) "FALSE") == 0) { (*UpdateArray)[Index].FaultTolerant = FALSE; } } if ((*UpdateArray)[Index].UpdateType == UpdateFvRange) { // // Length // Value = NULL; Status = UpdateGetProfileString ( SectionHead, (UINT8 *) SectionName, (UINT8 *) "Length", (UINT8 **) &Value ); if (Value == NULL) { FreeAllList (SectionHead, CommentHead); return EFI_NOT_FOUND; } Num = AsciiStrHexToUintn ((CONST CHAR8 *) Value); (*UpdateArray)[Index].Length = (UINTN) Num; } } // // Now all configuration data got. Free those temporary buffers // FreeAllList (SectionHead, CommentHead); return EFI_SUCCESS; }
/** Open a device named by PathName. The PathName includes a device name and path separated by a :. See file header for more details on the PathName syntax. There is no checking to prevent a file from being opened more than one type. SectionType is only used to open an FV. Each file in an FV contains multiple sections and only the SectionType section is opened. For any file that is opened with EfiOpen() must be closed with EfiClose(). @param PathName Path to parse to open @param OpenMode Same as EFI_FILE.Open() @param SectionType Section in FV to open. @return NULL Open failed @return Valid EFI_OPEN_FILE handle **/ EFI_OPEN_FILE * EfiOpen ( IN CHAR8 *PathName, IN CONST UINT64 OpenMode, IN CONST EFI_SECTION_TYPE SectionType ) { EFI_STATUS Status; EFI_OPEN_FILE *File; EFI_OPEN_FILE FileData; UINTN StrLen; UINTN FileStart; UINTN DevNumber = 0; EFI_OPEN_FILE_GUARD *GuardFile; BOOLEAN VolumeNameMatch; EFI_DEVICE_PATH_PROTOCOL *DevicePath; UINTN Size; EFI_IP_ADDRESS Ip; CHAR8 *CwdPlusPathName; UINTN Index; EFI_SECTION_TYPE ModifiedSectionType; EblUpdateDeviceLists (); File = &FileData; ZeroMem (File, sizeof (EFI_OPEN_FILE)); StrLen = AsciiStrSize (PathName); if (StrLen <= 1) { // Smallest valid path is 1 char and a null return NULL; } for (FileStart = 0; FileStart < StrLen; FileStart++) { if (PathName[FileStart] == ':') { FileStart++; break; } } // // Matching volume name has precedence over handle based names // VolumeNameMatch = EblMatchVolumeName (PathName, FileStart, &DevNumber); if (!VolumeNameMatch) { if (FileStart == StrLen) { // No Volume name or device name, so try Current Working Directory if (gCwd == NULL) { // No CWD return NULL; } // We could add a current working directory concept CwdPlusPathName = AllocatePool (AsciiStrSize (gCwd) + AsciiStrSize (PathName)); if (CwdPlusPathName == NULL) { return NULL; } if ((PathName[0] == '/') || (PathName[0] == '\\')) { // PathName starts in / so this means we go to the root of the device in the CWD. CwdPlusPathName[0] = '\0'; for (FileStart = 0; gCwd[FileStart] != '\0'; FileStart++) { CwdPlusPathName[FileStart] = gCwd[FileStart]; if (gCwd[FileStart] == ':') { FileStart++; CwdPlusPathName[FileStart] = '\0'; break; } } } else { AsciiStrCpy (CwdPlusPathName, gCwd); StrLen = AsciiStrLen (gCwd); if ((*PathName != '/') && (*PathName != '\\') && (gCwd[StrLen-1] != '/') && (gCwd[StrLen-1] != '\\')) { AsciiStrCat (CwdPlusPathName, "\\"); } } AsciiStrCat (CwdPlusPathName, PathName); if (AsciiStrStr (CwdPlusPathName, ":") == NULL) { // Extra error check to make sure we don't recurse and blow stack return NULL; } File = EfiOpen (CwdPlusPathName, OpenMode, SectionType); FreePool (CwdPlusPathName); return File; } DevNumber = EblConvertDevStringToNumber ((CHAR8 *)PathName); } File->DeviceName = AllocatePool (StrLen); AsciiStrCpy (File->DeviceName, PathName); File->DeviceName[FileStart - 1] = '\0'; File->FileName = &File->DeviceName[FileStart]; if (File->FileName[0] == '\0') { // if it is just a file name use / as root File->FileName = "\\"; } // // Use best match algorithm on the dev names so we only need to look at the // first few charters to match the full device name. Short name forms are // legal from the caller. // Status = EFI_SUCCESS; if (*PathName == 'f' || *PathName == 'F' || VolumeNameMatch) { if (PathName[1] == 's' || PathName[1] == 'S' || VolumeNameMatch) { if (DevNumber >= mFsCount) { goto ErrorExit; } File->Type = EfiOpenFileSystem; File->EfiHandle = mFs[DevNumber]; Status = EblFileDevicePath (File, &PathName[FileStart], OpenMode); } else if (PathName[1] == 'v' || PathName[1] == 'V') { if (DevNumber >= mFvCount) { goto ErrorExit; } File->Type = EfiOpenFirmwareVolume; File->EfiHandle = mFv[DevNumber]; if ((PathName[FileStart] == '/') || (PathName[FileStart] == '\\')) { // Skip leading / as its not really needed for the FV since no directories are supported FileStart++; } // Check for 2nd : ModifiedSectionType = SectionType; for (Index = FileStart; PathName[Index] != '\0'; Index++) { if (PathName[Index] == ':') { // Support fv0:\DxeCore:0x10 // This means open the PE32 Section of the file ModifiedSectionType = (EFI_SECTION_TYPE)AsciiStrHexToUintn (&PathName[Index + 1]); PathName[Index] = '\0'; } } File->FvSectionType = ModifiedSectionType; Status = EblFvFileDevicePath (File, &PathName[FileStart], ModifiedSectionType); } } else if ((*PathName == 'A') || (*PathName == 'a')) { // Handle a:0x10000000:0x1234 address form a:ADDRESS:SIZE File->Type = EfiOpenMemoryBuffer; // 1st colon is at PathName[FileStart - 1] File->Buffer = (VOID *)AsciiStrHexToUintn (&PathName[FileStart]); // Find 2nd colon while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { FileStart++; } // If we ran out of string, there's no extra data if (PathName[FileStart] == '\0') { File->Size = 0; } else { File->Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); } // if there's no number after the second colon, default // the end of memory if (File->Size == 0) { File->Size = (UINTN)(0 - (UINTN)File->Buffer); } File->MaxPosition = File->Size; File->BaseOffset = (UINTN)File->Buffer; } else if (*PathName== 'l' || *PathName == 'L') { if (DevNumber >= mLoadFileCount) { goto ErrorExit; } File->Type = EfiOpenLoadFile; File->EfiHandle = mLoadFile[DevNumber]; Status = gBS->HandleProtocol (File->EfiHandle, &gEfiLoadFileProtocolGuid, (VOID **)&File->LoadFile); if (EFI_ERROR (Status)) { goto ErrorExit; } Status = gBS->HandleProtocol (File->EfiHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); if (EFI_ERROR (Status)) { goto ErrorExit; } File->DevicePath = DuplicateDevicePath (DevicePath); } else if (*PathName == 'b' || *PathName == 'B') { // Handle b#:0x10000000:0x1234 address form b#:ADDRESS:SIZE if (DevNumber >= mBlkIoCount) { goto ErrorExit; } File->Type = EfiOpenBlockIo; File->EfiHandle = mBlkIo[DevNumber]; EblFileDevicePath (File, "", OpenMode); // 1st colon is at PathName[FileStart - 1] File->DiskOffset = AsciiStrHexToUintn (&PathName[FileStart]); // Find 2nd colon while ((PathName[FileStart] != ':') && (PathName[FileStart] != '\0')) { FileStart++; } // If we ran out of string, there's no extra data if (PathName[FileStart] == '\0') { Size = 0; } else { Size = AsciiStrHexToUintn (&PathName[FileStart + 1]); } // if a zero size is passed in (or the size is left out entirely), // go to the end of the device. if (Size == 0) { File->Size = File->Size - File->DiskOffset; } else { File->Size = Size; } File->MaxPosition = File->Size; File->BaseOffset = File->DiskOffset; } else if ((*PathName) >= '0' && (*PathName <= '9')) { // Get current IP address Status = EblGetCurrentIpAddress (&Ip); if (EFI_ERROR(Status)) { AsciiPrint("Device IP Address is not configured.\n"); goto ErrorExit; } // Parse X.X.X.X:Filename, only support IPv4 TFTP for now... File->Type = EfiOpenTftp; File->IsDirty = FALSE; File->IsBufferValid = FALSE; Status = ConvertIpStringToEfiIp (PathName, &File->ServerIp); } if (EFI_ERROR (Status)) { goto ErrorExit; } GuardFile = (EFI_OPEN_FILE_GUARD *)AllocateZeroPool (sizeof (EFI_OPEN_FILE_GUARD)); if (GuardFile == NULL) { goto ErrorExit; } GuardFile->Header = EFI_OPEN_FILE_GUARD_HEADER; CopyMem (&(GuardFile->File), &FileData, sizeof (EFI_OPEN_FILE)); GuardFile->Footer = EFI_OPEN_FILE_GUARD_FOOTER; return &(GuardFile->File); ErrorExit: FreePool (File->DeviceName); return NULL; }
/** Toggle page break global. This turns on and off prompting to Quit or hit any key to continue when a command is about to scroll the screen with its output Argv[0] - "hexdump"[.#] # is optional 1,2, or 4 for width Argv[1] - Device or File to dump. Argv[2] - Optional offset to start dumping Argv[3] - Optional number of bytes to dump @param Argc Number of command arguments in Argv @param Argv Array of strings that represent the parsed command line. Argv[0] is the command name @return EFI_SUCCESS **/ EFI_STATUS EFIAPI EblHexdumpCmd ( IN UINTN Argc, IN CHAR8 **Argv ) { EFI_OPEN_FILE *File; VOID *Location; UINTN Size; UINTN Width; UINTN Offset = 0; EFI_STATUS Status; UINTN Chunk = HEXDUMP_CHUNK; if ((Argc < 2) || (Argc > 4)) { return EFI_INVALID_PARAMETER; } Width = WidthFromCommandName (Argv[0], 1); if ((Width != 1) && (Width != 2) && (Width != 4)) { return EFI_INVALID_PARAMETER; } File = EfiOpen (Argv[1], EFI_FILE_MODE_READ, 0); if (File == NULL) { return EFI_NOT_FOUND; } Location = AllocatePool (Chunk); Size = (Argc > 3) ? AsciiStrHexToUintn (Argv[3]) : EfiTell (File, NULL); Offset = 0; if (Argc > 2) { Offset = AsciiStrHexToUintn (Argv[2]); if (Offset > 0) { // Make sure size includes the part of the file we have skipped Size += Offset; } } Status = EfiSeek (File, Offset, EfiSeekStart); if (EFI_ERROR (Status)) { goto Exit; } for (; Offset + HEXDUMP_CHUNK <= Size; Offset += Chunk) { Chunk = HEXDUMP_CHUNK; Status = EfiRead (File, Location, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint ("Error reading file content\n"); goto Exit; } Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset); if (EFI_ERROR(Status)) { if (Status == EFI_END_OF_FILE) { Status = EFI_SUCCESS; } goto Exit; } } // Any left over? if (Offset < Size) { Chunk = Size - Offset; Status = EfiRead (File, Location, &Chunk); if (EFI_ERROR(Status)) { AsciiPrint ("Error reading file content\n"); goto Exit; } Status = OutputData (Location, Chunk, Width, File->BaseOffset + Offset); if (EFI_ERROR(Status)) { if (Status == EFI_END_OF_FILE) { Status = EFI_SUCCESS; } goto Exit; } } Exit: EfiClose (File); FreePool (Location); return EFI_SUCCESS; }