+/** @file\r
+ AML Stream.\r
+\r
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Stream/AmlStream.h>\r
+\r
+/** Initialize a stream.\r
+\r
+ @param [in, out] Stream Pointer to the stream to initialize.\r
+ @param [in] Buffer Buffer to initialize Stream with.\r
+ Point to the beginning of the Buffer.\r
+ @param [in] MaxBufferSize Maximum size of Buffer.\r
+ @param [in] Direction Direction Stream is progressing\r
+ (forward, backward).\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamInit (\r
+ IN OUT AML_STREAM * Stream,\r
+ IN UINT8 * Buffer,\r
+ IN UINT32 MaxBufferSize,\r
+ IN EAML_STREAM_DIRECTION Direction\r
+ )\r
+{\r
+ if ((Stream == NULL) ||\r
+ (Buffer == NULL) ||\r
+ (MaxBufferSize == 0) ||\r
+ ((Direction != EAmlStreamDirectionForward) &&\r
+ (Direction != EAmlStreamDirectionBackward))) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Stream->Buffer = Buffer;\r
+ Stream->MaxBufferSize = MaxBufferSize;\r
+ Stream->Index = 0;\r
+ Stream->Direction = Direction;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Clone a stream.\r
+\r
+ Cloning a stream means copying all the values of the input Stream\r
+ in the ClonedStream.\r
+\r
+ @param [in] Stream Pointer to the stream to clone.\r
+ @param [in] ClonedStream Pointer to the stream to initialize.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamClone (\r
+ IN CONST AML_STREAM * Stream,\r
+ OUT AML_STREAM * ClonedStream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream) ||\r
+ (ClonedStream == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ ClonedStream->Buffer = Stream->Buffer;\r
+ ClonedStream->MaxBufferSize = Stream->MaxBufferSize;\r
+ ClonedStream->Index = Stream->Index;\r
+ ClonedStream->Direction = Stream->Direction;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Initialize a sub-stream from a stream.\r
+\r
+ A sub-stream is a stream initialized at the current position of the input\r
+ stream:\r
+ - the Buffer field points to the current position of the input stream;\r
+ - the Index field is set to 0;\r
+ - the MaxBufferSize field is set to the remaining size of the input stream;\r
+ - the direction is conserved;\r
+\r
+ E.g.: For a forward stream:\r
+ +----------------+----------------+\r
+ |ABCD.........XYZ| Free Space |\r
+ +----------------+----------------+\r
+ ^ ^ ^\r
+ Stream: Buffer CurrPos EndOfBuff\r
+ Sub-stream: Buffer/CurrPos EndOfBuff\r
+\r
+ @param [in] Stream Pointer to the stream from which a sub-stream is\r
+ created.\r
+ @param [in] SubStream Pointer to the stream to initialize.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamInitSubStream (\r
+ IN CONST AML_STREAM * Stream,\r
+ OUT AML_STREAM * SubStream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream) ||\r
+ (SubStream == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (IS_STREAM_FORWARD (Stream)) {\r
+ SubStream->Buffer = AmlStreamGetCurrPos (Stream);\r
+ } else if (IS_STREAM_BACKWARD (Stream)) {\r
+ SubStream->Buffer = Stream->Buffer;\r
+ } else {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ SubStream->MaxBufferSize = AmlStreamGetFreeSpace (Stream);\r
+ SubStream->Index = 0;\r
+ SubStream->Direction = Stream->Direction;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Get the buffer of a stream.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return The stream's Buffer.\r
+ NULL otherwise.\r
+**/\r
+UINT8 *\r
+EFIAPI\r
+AmlStreamGetBuffer (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return NULL;\r
+ }\r
+ return Stream->Buffer;\r
+}\r
+\r
+/** Get the size of Stream's Buffer.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return The Size of Stream's Buffer.\r
+ Return 0 if Stream is invalid.\r
+**/\r
+UINT32\r
+EFIAPI\r
+AmlStreamGetMaxBufferSize (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return 0;\r
+ }\r
+ return Stream->MaxBufferSize;\r
+}\r
+\r
+/** Reduce the maximal size of Stream's Buffer (MaxBufferSize field).\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ @param [in] Diff Value to subtract to the Stream's MaxBufferSize.\r
+ 0 < x < MaxBufferSize - Index.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamReduceMaxBufferSize (\r
+ IN AML_STREAM * Stream,\r
+ IN UINT32 Diff\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream) ||\r
+ (Diff == 0) ||\r
+ ((Stream->MaxBufferSize - Diff) <= Stream->Index)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Stream->MaxBufferSize -= Diff;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Get Stream's Index.\r
+\r
+ Stream's Index is incremented when writing data, reading data,\r
+ or moving the position in the Stream.\r
+ It can be seen as an index:\r
+ - starting at the beginning of Stream's Buffer if the stream goes forward;\r
+ - starting at the end of Stream's Buffer if the stream goes backward.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return Stream's Index.\r
+ Return 0 if Stream is invalid.\r
+**/\r
+UINT32\r
+EFIAPI\r
+AmlStreamGetIndex (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return 0;\r
+ }\r
+ return Stream->Index;\r
+}\r
+\r
+/** Get Stream's Direction.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return Stream's Direction.\r
+ Return EAmlStreamDirectionUnknown if Stream is invalid.\r
+**/\r
+EAML_STREAM_DIRECTION\r
+EFIAPI\r
+AmlStreamGetDirection (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return EAmlStreamDirectionInvalid;\r
+ }\r
+ return Stream->Direction;\r
+}\r
+\r
+/** Return a pointer to the current position in the stream.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return The current position in the stream.\r
+ Return NULL if error.\r
+**/\r
+UINT8 *\r
+EFIAPI\r
+AmlStreamGetCurrPos (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return NULL;\r
+ }\r
+\r
+ if (IS_STREAM_FORWARD (Stream)) {\r
+ return Stream->Buffer + Stream->Index;\r
+ } else if (IS_STREAM_BACKWARD (Stream)) {\r
+ return Stream->Buffer + (Stream->MaxBufferSize - 1) - Stream->Index;\r
+ } else {\r
+ ASSERT (0);\r
+ return NULL;\r
+ }\r
+}\r
+\r
+/** Get the space available in the stream.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @return Remaining space available in the stream.\r
+ Zero in case of error or if the stream is at its end.\r
+**/\r
+UINT32\r
+EFIAPI\r
+AmlStreamGetFreeSpace (\r
+ IN CONST AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return 0;\r
+ }\r
+\r
+ if (Stream->Index > Stream->MaxBufferSize) {\r
+ ASSERT (0);\r
+ return 0;\r
+ }\r
+\r
+ return Stream->MaxBufferSize - Stream->Index;\r
+}\r
+\r
+/** Move Stream by Offset bytes.\r
+\r
+ The stream current position is moved according to the stream direction\r
+ (forward, backward).\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ The stream must not be at its end.\r
+ @param [in] Offset Offset to move the stream of.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamProgress (\r
+ IN AML_STREAM * Stream,\r
+ IN UINT32 Offset\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream) ||\r
+ IS_END_OF_STREAM (Stream) ||\r
+ (Offset == 0)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AmlStreamGetFreeSpace (Stream) < Offset) {\r
+ ASSERT (0);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ Stream->Index += Offset;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Rewind Stream of Offset bytes.\r
+\r
+ The stream current position is rewound according to the stream direction\r
+ (forward, backward). A stream going forward will be rewound backward.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ @param [in] Offset Offset to rewind the stream of.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamRewind (\r
+ IN AML_STREAM * Stream,\r
+ IN UINT32 Offset\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream) ||\r
+ (Offset == 0)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AmlStreamGetIndex (Stream) < Offset) {\r
+ ASSERT (0);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ Stream->Index -= Offset;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Reset the Stream (move the current position to the initial position).\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamReset (\r
+ IN AML_STREAM * Stream\r
+ )\r
+{\r
+ if (!IS_STREAM (Stream)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Stream->Index = 0;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Peek one byte at Stream's current position.\r
+\r
+ Stream's position is not moved when peeking.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ The stream must not be at its end.\r
+ @param [out] OutByte Pointer holding the byte value of\r
+ the stream current position.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamPeekByte (\r
+ IN AML_STREAM * Stream,\r
+ OUT UINT8 * OutByte\r
+ )\r
+{\r
+ UINT8 * CurPos;\r
+\r
+ if (!IS_STREAM (Stream) ||\r
+ IS_END_OF_STREAM (Stream) ||\r
+ (OutByte == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ CurPos = AmlStreamGetCurrPos (Stream);\r
+ if (CurPos == NULL) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ *OutByte = *CurPos;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Read one byte at Stream's current position.\r
+\r
+ The stream current position is moved when reading.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ The stream must not be at its end.\r
+ @param [out] OutByte Pointer holding the byte value of\r
+ the stream current position.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamReadByte (\r
+ IN AML_STREAM * Stream,\r
+ OUT UINT8 * OutByte\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (!IS_STREAM (Stream) ||\r
+ IS_END_OF_STREAM (Stream) ||\r
+ (OutByte == NULL)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ // Stream is checked in the function call.\r
+ Status = AmlStreamPeekByte (Stream, OutByte);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT (0);\r
+ return Status;\r
+ }\r
+\r
+ Status = AmlStreamProgress (Stream, 1);\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r
+\r
+/** Write Size bytes in the stream.\r
+\r
+ If the stream goes backward (toward lower addresses), the bytes written\r
+ to the stream are not reverted.\r
+ In the example below, writing "Hello" to the stream will not revert\r
+ the string. The end of the stream buffer will contain "Hello world!".\r
+ Stream buffer:\r
+ +---------------+-----+-----+-----+-----+-----+-----+---- +------+\r
+ | ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' |\r
+ +---------------+-----+-----+-----+-----+-----+-----+---- +------+\r
+ ^\r
+ Current position.\r
+\r
+ @param [in] Stream Pointer to a stream.\r
+ The stream must not be at its end.\r
+ @param [in] Buffer Pointer to the data to write.\r
+ @param [in] Size Number of bytes to write.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+ @retval EFI_BUFFER_TOO_SMALL No space left in the buffer.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamWrite (\r
+ IN AML_STREAM * Stream,\r
+ IN CONST UINT8 * Buffer,\r
+ IN UINT32 Size\r
+ )\r
+{\r
+ UINT8 * CurrPos;\r
+\r
+ if (!IS_STREAM (Stream) ||\r
+ IS_END_OF_STREAM (Stream) ||\r
+ (Buffer == NULL) ||\r
+ (Size == 0)) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (AmlStreamGetFreeSpace (Stream) < Size) {\r
+ ASSERT (0);\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ CurrPos = AmlStreamGetCurrPos (Stream);\r
+\r
+ // If the Stream goes backward, prepare some space to copy the data.\r
+ if (IS_STREAM_BACKWARD (Stream)) {\r
+ CurrPos -= Size;\r
+ }\r
+\r
+ CopyMem (CurrPos, Buffer, Size);\r
+ Stream->Index += Size;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/** Compare Size bytes between Stream1 and Stream2 from their\r
+ respective current position.\r
+\r
+ Stream1 and Stream2 must go in the same direction.\r
+ Stream1 and Stream2 are left unchanged.\r
+\r
+ @param [in] Stream1 First stream to compare.\r
+ The stream must not be at its end.\r
+ @param [in] Stream2 Second stream to compare.\r
+ The stream must not be at its end.\r
+ @param [in] Size Number of bytes to compare.\r
+ Must be lower than the minimum remaining space of\r
+ Stream1 and Stream2.\r
+ Must be non-zero.\r
+\r
+ @retval TRUE If Stream1 and Stream2 have Size bytes equal,\r
+ from their respective current position.\r
+ The function completed successfully.\r
+ @retval FALSE Otherwise.\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+AmlStreamCmp (\r
+ IN CONST AML_STREAM * Stream1,\r
+ IN CONST AML_STREAM * Stream2,\r
+ IN UINT32 Size\r
+ )\r
+{\r
+ UINT32 MinSize;\r
+ UINT8 * CurrPosStream1;\r
+ UINT8 * CurrPosStream2;\r
+\r
+ if (!IS_STREAM (Stream1) ||\r
+ IS_END_OF_STREAM (Stream1) ||\r
+ !IS_STREAM (Stream2) ||\r
+ IS_END_OF_STREAM (Stream2) ||\r
+ (Stream1->Direction != Stream2->Direction) ||\r
+ (Size == 0)) {\r
+ ASSERT (0);\r
+ return FALSE;\r
+ }\r
+\r
+ // Check the Size is not longer than the remaining size of\r
+ // Stream1 and Stream2.\r
+ MinSize = MIN (\r
+ AmlStreamGetFreeSpace (Stream1),\r
+ AmlStreamGetFreeSpace (Stream2)\r
+ );\r
+ if (MinSize < Size) {\r
+ ASSERT (0);\r
+ return FALSE;\r
+ }\r
+\r
+ CurrPosStream1 = AmlStreamGetCurrPos (Stream1);\r
+ if (CurrPosStream1 == NULL) {\r
+ ASSERT (0);\r
+ return FALSE;\r
+ }\r
+ CurrPosStream2 = AmlStreamGetCurrPos (Stream2);\r
+ if (CurrPosStream2 == NULL) {\r
+ ASSERT (0);\r
+ return FALSE;\r
+ }\r
+\r
+ if (Stream1->Direction == EAmlStreamDirectionForward) {\r
+ return (0 == CompareMem (CurrPosStream1, CurrPosStream2, MinSize));\r
+ }\r
+\r
+ // The stream is already pointing on the last byte, thus the (-1).\r
+ // +---------------------+\r
+ // BStream | | | | | | | |M|E|T|0|\r
+ // +---------------------+\r
+ // ^\r
+ // CurrPos\r
+ return (0 == CompareMem (\r
+ CurrPosStream1 - (MinSize - 1),\r
+ CurrPosStream2 - (MinSize - 1),\r
+ MinSize\r
+ ));\r
+}\r
+\r
+/** Copy Size bytes of the stream's data to DstBuffer.\r
+\r
+ For a backward stream, the bytes are copied starting from the\r
+ current stream position.\r
+\r
+ @param [out] DstBuffer Destination Buffer to copy the data to.\r
+ @param [in] MaxDstBufferSize Maximum size of DstBuffer.\r
+ Must be non-zero.\r
+ @param [in] Stream Pointer to the stream to copy the data from.\r
+ @param [in] Size Number of bytes to copy from the stream\r
+ buffer.\r
+ Must be lower than MaxDstBufferSize.\r
+ Must be lower than Stream's MaxBufferSize.\r
+ Return success if zero.\r
+\r
+ @retval EFI_SUCCESS The function completed successfully.\r
+ @retval EFI_INVALID_PARAMETER Invalid parameter.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+AmlStreamCpyS (\r
+ OUT CHAR8 * DstBuffer,\r
+ IN UINT32 MaxDstBufferSize,\r
+ IN AML_STREAM * Stream,\r
+ IN UINT32 Size\r
+ )\r
+{\r
+ CHAR8 * StreamBufferStart;\r
+\r
+ // Stream is checked in the function call.\r
+ if ((DstBuffer == NULL) ||\r
+ (MaxDstBufferSize == 0) ||\r
+ (Size > MaxDstBufferSize) ||\r
+ (Size > AmlStreamGetMaxBufferSize (Stream))) {\r
+ ASSERT (0);\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Size == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ // Find the address at which the data is starting.\r
+ StreamBufferStart = (CHAR8*)(IS_STREAM_FORWARD (Stream) ?\r
+ Stream->Buffer :\r
+ AmlStreamGetCurrPos (Stream));\r
+\r
+ CopyMem (DstBuffer, StreamBufferStart, Size);\r
+\r
+ return EFI_SUCCESS;\r
+}\r