1 /** @file
2   AML Stream.
3 
4   Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
5 
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 **/
8 
9 #include <Stream/AmlStream.h>
10 
11 /** Initialize a stream.
12 
13   @param  [in, out] Stream          Pointer to the stream to initialize.
14   @param  [in]      Buffer          Buffer to initialize Stream with.
15                                     Point to the beginning of the Buffer.
16   @param  [in]      MaxBufferSize   Maximum size of Buffer.
17   @param  [in]      Direction       Direction Stream is progressing
18                                     (forward, backward).
19 
20   @retval EFI_SUCCESS             The function completed successfully.
21   @retval EFI_INVALID_PARAMETER   Invalid parameter.
22 **/
23 EFI_STATUS
24 EFIAPI
AmlStreamInit(IN OUT AML_STREAM * Stream,IN UINT8 * Buffer,IN UINT32 MaxBufferSize,IN EAML_STREAM_DIRECTION Direction)25 AmlStreamInit (
26   IN  OUT AML_STREAM              * Stream,
27   IN      UINT8                   * Buffer,
28   IN      UINT32                    MaxBufferSize,
29   IN      EAML_STREAM_DIRECTION     Direction
30   )
31 {
32   if ((Stream == NULL)                            ||
33       (Buffer == NULL)                            ||
34       (MaxBufferSize == 0)                        ||
35       ((Direction != EAmlStreamDirectionForward)  &&
36        (Direction != EAmlStreamDirectionBackward))) {
37     ASSERT (0);
38     return EFI_INVALID_PARAMETER;
39   }
40 
41   Stream->Buffer = Buffer;
42   Stream->MaxBufferSize = MaxBufferSize;
43   Stream->Index = 0;
44   Stream->Direction = Direction;
45 
46   return EFI_SUCCESS;
47 }
48 
49 /** Clone a stream.
50 
51   Cloning a stream means copying all the values of the input Stream
52   in the ClonedStream.
53 
54   @param  [in]  Stream          Pointer to the stream to clone.
55   @param  [out] ClonedStream    Pointer to the stream to initialize.
56 
57   @retval EFI_SUCCESS             The function completed successfully.
58   @retval EFI_INVALID_PARAMETER   Invalid parameter.
59 **/
60 EFI_STATUS
61 EFIAPI
AmlStreamClone(IN CONST AML_STREAM * Stream,OUT AML_STREAM * ClonedStream)62 AmlStreamClone (
63   IN  CONST AML_STREAM    * Stream,
64   OUT        AML_STREAM   * ClonedStream
65   )
66 {
67   if (!IS_STREAM (Stream)   ||
68       (ClonedStream == NULL)) {
69     ASSERT (0);
70     return EFI_INVALID_PARAMETER;
71   }
72 
73   ClonedStream->Buffer = Stream->Buffer;
74   ClonedStream->MaxBufferSize = Stream->MaxBufferSize;
75   ClonedStream->Index = Stream->Index;
76   ClonedStream->Direction = Stream->Direction;
77 
78   return EFI_SUCCESS;
79 }
80 
81 /** Initialize a sub-stream from a stream.
82 
83   A sub-stream is a stream initialized at the current position of the input
84   stream:
85     - the Buffer field points to the current position of the input stream;
86     - the Index field is set to 0;
87     - the MaxBufferSize field is set to the remaining size of the input stream;
88     - the direction is conserved;
89 
90   E.g.: For a forward stream:
91                    +----------------+----------------+
92                    |ABCD.........XYZ|   Free Space   |
93                    +----------------+----------------+
94                    ^                ^                ^
95   Stream:        Buffer          CurrPos         EndOfBuff
96   Sub-stream:                Buffer/CurrPos      EndOfBuff
97 
98   @param  [in]  Stream      Pointer to the stream from which a sub-stream is
99                             created.
100   @param  [out] SubStream   Pointer to the stream to initialize.
101 
102   @retval EFI_SUCCESS             The function completed successfully.
103   @retval EFI_INVALID_PARAMETER   Invalid parameter.
104 **/
105 EFI_STATUS
106 EFIAPI
AmlStreamInitSubStream(IN CONST AML_STREAM * Stream,OUT AML_STREAM * SubStream)107 AmlStreamInitSubStream (
108   IN  CONST AML_STREAM  * Stream,
109   OUT       AML_STREAM  * SubStream
110   )
111 {
112   if (!IS_STREAM (Stream) ||
113       (SubStream == NULL)) {
114     ASSERT (0);
115     return EFI_INVALID_PARAMETER;
116   }
117 
118   if (IS_STREAM_FORWARD (Stream)) {
119     SubStream->Buffer = AmlStreamGetCurrPos (Stream);
120   } else if (IS_STREAM_BACKWARD (Stream)) {
121     SubStream->Buffer = Stream->Buffer;
122   } else {
123     ASSERT (0);
124     return EFI_INVALID_PARAMETER;
125   }
126 
127   SubStream->MaxBufferSize = AmlStreamGetFreeSpace (Stream);
128   SubStream->Index = 0;
129   SubStream->Direction = Stream->Direction;
130 
131   return EFI_SUCCESS;
132 }
133 
134 /** Get the buffer of a stream.
135 
136   @param  [in]  Stream    Pointer to a stream.
137 
138   @return The stream's Buffer.
139           NULL otherwise.
140 **/
141 UINT8 *
142 EFIAPI
AmlStreamGetBuffer(IN CONST AML_STREAM * Stream)143 AmlStreamGetBuffer (
144   IN  CONST AML_STREAM  * Stream
145   )
146 {
147   if (!IS_STREAM (Stream)) {
148     ASSERT (0);
149     return NULL;
150   }
151   return Stream->Buffer;
152 }
153 
154 /** Get the size of Stream's Buffer.
155 
156   @param  [in]  Stream    Pointer to a stream.
157 
158   @return The Size of Stream's Buffer.
159           Return 0 if Stream is invalid.
160 **/
161 UINT32
162 EFIAPI
AmlStreamGetMaxBufferSize(IN CONST AML_STREAM * Stream)163 AmlStreamGetMaxBufferSize (
164   IN  CONST AML_STREAM  * Stream
165   )
166 {
167   if (!IS_STREAM (Stream)) {
168     ASSERT (0);
169     return 0;
170   }
171   return Stream->MaxBufferSize;
172 }
173 
174 /** Reduce the maximal size of Stream's Buffer (MaxBufferSize field).
175 
176   @param  [in]  Stream    Pointer to a stream.
177   @param  [in]  Diff      Value to subtract to the Stream's MaxBufferSize.
178                           0 < x < MaxBufferSize - Index.
179 
180   @retval EFI_SUCCESS             The function completed successfully.
181   @retval EFI_INVALID_PARAMETER   Invalid parameter.
182 **/
183 EFI_STATUS
184 EFIAPI
AmlStreamReduceMaxBufferSize(IN AML_STREAM * Stream,IN UINT32 Diff)185 AmlStreamReduceMaxBufferSize (
186   IN  AML_STREAM  * Stream,
187   IN  UINT32        Diff
188   )
189 {
190   if (!IS_STREAM (Stream)       ||
191       (Diff == 0)               ||
192       ((Stream->MaxBufferSize - Diff) <= Stream->Index)) {
193     ASSERT (0);
194     return EFI_INVALID_PARAMETER;
195   }
196 
197   Stream->MaxBufferSize -= Diff;
198   return EFI_SUCCESS;
199 }
200 
201 /** Get Stream's Index.
202 
203   Stream's Index is incremented when writing data, reading data,
204   or moving the position in the Stream.
205   It can be seen as an index:
206    - starting at the beginning of Stream's Buffer if the stream goes forward;
207    - starting at the end of Stream's Buffer if the stream goes backward.
208 
209   @param  [in]  Stream    Pointer to a stream.
210 
211   @return Stream's Index.
212           Return 0 if Stream is invalid.
213 **/
214 UINT32
215 EFIAPI
AmlStreamGetIndex(IN CONST AML_STREAM * Stream)216 AmlStreamGetIndex (
217   IN  CONST AML_STREAM  * Stream
218   )
219 {
220   if (!IS_STREAM (Stream)) {
221     ASSERT (0);
222     return 0;
223   }
224   return Stream->Index;
225 }
226 
227 /** Get Stream's Direction.
228 
229   @param  [in]  Stream    Pointer to a stream.
230 
231   @return Stream's Direction.
232           Return EAmlStreamDirectionUnknown if Stream is invalid.
233 **/
234 EAML_STREAM_DIRECTION
235 EFIAPI
AmlStreamGetDirection(IN CONST AML_STREAM * Stream)236 AmlStreamGetDirection (
237   IN  CONST AML_STREAM  * Stream
238   )
239 {
240   if (!IS_STREAM (Stream)) {
241     ASSERT (0);
242     return EAmlStreamDirectionInvalid;
243   }
244   return Stream->Direction;
245 }
246 
247 /** Return a pointer to the current position in the stream.
248 
249   @param  [in]  Stream    Pointer to a stream.
250 
251   @return The current position in the stream.
252           Return NULL if error.
253 **/
254 UINT8 *
255 EFIAPI
AmlStreamGetCurrPos(IN CONST AML_STREAM * Stream)256 AmlStreamGetCurrPos (
257   IN  CONST AML_STREAM  * Stream
258   )
259 {
260   if (!IS_STREAM (Stream)) {
261     ASSERT (0);
262     return NULL;
263   }
264 
265   if (IS_STREAM_FORWARD (Stream)) {
266     return Stream->Buffer + Stream->Index;
267   } else if (IS_STREAM_BACKWARD (Stream)) {
268     return Stream->Buffer + (Stream->MaxBufferSize - 1) - Stream->Index;
269   } else {
270     ASSERT (0);
271     return NULL;
272   }
273 }
274 
275 /** Get the space available in the stream.
276 
277   @param  [in]  Stream    Pointer to a stream.
278 
279   @return Remaining space available in the stream.
280           Zero in case of error or if the stream is at its end.
281 **/
282 UINT32
283 EFIAPI
AmlStreamGetFreeSpace(IN CONST AML_STREAM * Stream)284 AmlStreamGetFreeSpace (
285   IN  CONST AML_STREAM  * Stream
286   )
287 {
288   if (!IS_STREAM (Stream)) {
289     ASSERT (0);
290     return 0;
291   }
292 
293   if (Stream->Index > Stream->MaxBufferSize) {
294     ASSERT (0);
295     return 0;
296   }
297 
298   return Stream->MaxBufferSize - Stream->Index;
299 }
300 
301 /** Move Stream by Offset bytes.
302 
303   The stream current position is moved according to the stream direction
304   (forward, backward).
305 
306   @param  [in]  Stream  Pointer to a stream.
307                         The stream must not be at its end.
308   @param  [in]  Offset  Offset to move the stream of.
309 
310   @retval EFI_SUCCESS             The function completed successfully.
311   @retval EFI_INVALID_PARAMETER   Invalid parameter.
312   @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
313 **/
314 EFI_STATUS
315 EFIAPI
AmlStreamProgress(IN AML_STREAM * Stream,IN UINT32 Offset)316 AmlStreamProgress (
317   IN  AML_STREAM  * Stream,
318   IN  UINT32        Offset
319   )
320 {
321   if (!IS_STREAM (Stream)         ||
322       IS_END_OF_STREAM  (Stream)  ||
323       (Offset == 0)) {
324     ASSERT (0);
325     return EFI_INVALID_PARAMETER;
326   }
327 
328   if (AmlStreamGetFreeSpace (Stream) < Offset) {
329     ASSERT (0);
330     return EFI_BUFFER_TOO_SMALL;
331   }
332 
333   Stream->Index += Offset;
334 
335   return EFI_SUCCESS;
336 }
337 
338 /** Rewind Stream of Offset bytes.
339 
340   The stream current position is rewound according to the stream direction
341   (forward, backward). A stream going forward will be rewound backward.
342 
343   @param  [in]  Stream  Pointer to a stream.
344   @param  [in]  Offset  Offset to rewind the stream of.
345 
346   @retval EFI_SUCCESS             The function completed successfully.
347   @retval EFI_INVALID_PARAMETER   Invalid parameter.
348   @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
349 **/
350 EFI_STATUS
351 EFIAPI
AmlStreamRewind(IN AML_STREAM * Stream,IN UINT32 Offset)352 AmlStreamRewind (
353   IN  AML_STREAM  * Stream,
354   IN  UINT32        Offset
355   )
356 {
357   if (!IS_STREAM (Stream) ||
358       (Offset == 0)) {
359     ASSERT (0);
360     return EFI_INVALID_PARAMETER;
361   }
362 
363   if (AmlStreamGetIndex (Stream) < Offset) {
364     ASSERT (0);
365     return EFI_BUFFER_TOO_SMALL;
366   }
367 
368   Stream->Index -= Offset;
369 
370   return EFI_SUCCESS;
371 }
372 
373 /** Reset the Stream (move the current position to the initial position).
374 
375   @param  [in]  Stream  Pointer to a stream.
376 
377   @retval EFI_SUCCESS             The function completed successfully.
378   @retval EFI_INVALID_PARAMETER   Invalid parameter.
379 **/
380 EFI_STATUS
381 EFIAPI
AmlStreamReset(IN AML_STREAM * Stream)382 AmlStreamReset (
383   IN  AML_STREAM  * Stream
384   )
385 {
386   if (!IS_STREAM (Stream)) {
387     ASSERT (0);
388     return EFI_INVALID_PARAMETER;
389   }
390 
391   Stream->Index = 0;
392 
393   return EFI_SUCCESS;
394 }
395 
396 /** Peek one byte at Stream's current position.
397 
398   Stream's position is not moved when peeking.
399 
400   @param  [in]  Stream    Pointer to a stream.
401                           The stream must not be at its end.
402   @param  [out] OutByte   Pointer holding the byte value of
403                           the stream current position.
404 
405   @retval EFI_SUCCESS           The function completed successfully.
406   @retval EFI_INVALID_PARAMETER Invalid parameter.
407   @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
408 **/
409 EFI_STATUS
410 EFIAPI
AmlStreamPeekByte(IN AML_STREAM * Stream,OUT UINT8 * OutByte)411 AmlStreamPeekByte (
412   IN  AML_STREAM  * Stream,
413   OUT UINT8       * OutByte
414   )
415 {
416   UINT8   * CurPos;
417 
418   if (!IS_STREAM (Stream)       ||
419       IS_END_OF_STREAM (Stream) ||
420       (OutByte == NULL)) {
421     ASSERT (0);
422     return EFI_INVALID_PARAMETER;
423   }
424 
425   CurPos = AmlStreamGetCurrPos (Stream);
426   if (CurPos == NULL) {
427     ASSERT (0);
428     return EFI_INVALID_PARAMETER;
429   }
430 
431   *OutByte = *CurPos;
432   return EFI_SUCCESS;
433 }
434 
435 /** Read one byte at Stream's current position.
436 
437   The stream current position is moved when reading.
438 
439   @param  [in]  Stream    Pointer to a stream.
440                           The stream must not be at its end.
441   @param  [out] OutByte   Pointer holding the byte value of
442                           the stream current position.
443 
444   @retval EFI_SUCCESS           The function completed successfully.
445   @retval EFI_INVALID_PARAMETER Invalid parameter.
446   @retval EFI_BUFFER_TOO_SMALL  No space left in the buffer.
447 **/
448 EFI_STATUS
449 EFIAPI
AmlStreamReadByte(IN AML_STREAM * Stream,OUT UINT8 * OutByte)450 AmlStreamReadByte (
451   IN  AML_STREAM  * Stream,
452   OUT UINT8       * OutByte
453   )
454 {
455   EFI_STATUS    Status;
456 
457   if (!IS_STREAM (Stream)       ||
458       IS_END_OF_STREAM (Stream) ||
459       (OutByte == NULL)) {
460     ASSERT (0);
461     return EFI_INVALID_PARAMETER;
462   }
463 
464   // Stream is checked in the function call.
465   Status = AmlStreamPeekByte (Stream, OutByte);
466   if (EFI_ERROR (Status)) {
467     ASSERT (0);
468     return Status;
469   }
470 
471   Status = AmlStreamProgress (Stream, 1);
472   ASSERT_EFI_ERROR (Status);
473   return Status;
474 }
475 
476 /** Write Size bytes in the stream.
477 
478   If the stream goes backward (toward lower addresses), the bytes written
479   to the stream are not reverted.
480   In the example below, writing "Hello" to the stream will not revert
481   the string. The end of the stream buffer will contain "Hello world!".
482   Stream buffer:
483      +---------------+-----+-----+-----+-----+-----+-----+---- +------+
484      |         ..... | ' ' | 'w' | 'o' | 'r' | 'l' | 'd' | '!' | '\0' |
485      +---------------+-----+-----+-----+-----+-----+-----+---- +------+
486                         ^
487                  Current position.
488 
489   @param  [in]  Stream  Pointer to a stream.
490                         The stream must not be at its end.
491   @param  [in]  Buffer  Pointer to the data to write.
492   @param  [in]  Size    Number of bytes to write.
493 
494   @retval EFI_SUCCESS             The function completed successfully.
495   @retval EFI_INVALID_PARAMETER   Invalid parameter.
496   @retval EFI_BUFFER_TOO_SMALL    No space left in the buffer.
497 **/
498 EFI_STATUS
499 EFIAPI
AmlStreamWrite(IN AML_STREAM * Stream,IN CONST UINT8 * Buffer,IN UINT32 Size)500 AmlStreamWrite (
501   IN        AML_STREAM  * Stream,
502   IN  CONST UINT8       * Buffer,
503   IN        UINT32        Size
504   )
505 {
506   UINT8   * CurrPos;
507 
508   if (!IS_STREAM (Stream)       ||
509       IS_END_OF_STREAM (Stream) ||
510       (Buffer == NULL)          ||
511       (Size == 0)) {
512     ASSERT (0);
513     return EFI_INVALID_PARAMETER;
514   }
515 
516   if (AmlStreamGetFreeSpace (Stream) < Size) {
517     ASSERT (0);
518     return EFI_BUFFER_TOO_SMALL;
519   }
520 
521   CurrPos = AmlStreamGetCurrPos (Stream);
522 
523   // If the Stream goes backward, prepare some space to copy the data.
524   if (IS_STREAM_BACKWARD (Stream)) {
525     CurrPos -= Size;
526   }
527 
528   CopyMem (CurrPos, Buffer, Size);
529   Stream->Index += Size;
530 
531   return EFI_SUCCESS;
532 }
533 
534 /** Compare Size bytes between Stream1 and Stream2 from their
535     respective current position.
536 
537   Stream1 and Stream2 must go in the same direction.
538   Stream1 and Stream2 are left unchanged.
539 
540   @param  [in]  Stream1   First stream to compare.
541                           The stream must not be at its end.
542   @param  [in]  Stream2   Second stream to compare.
543                           The stream must not be at its end.
544   @param  [in]  Size      Number of bytes to compare.
545                           Must be lower than the minimum remaining space of
546                           Stream1 and Stream2.
547                           Must be non-zero.
548 
549   @retval TRUE  If Stream1 and Stream2 have Size bytes equal,
550                 from their respective current position.
551                 The function completed successfully.
552   @retval FALSE Otherwise.
553 **/
554 BOOLEAN
555 EFIAPI
AmlStreamCmp(IN CONST AML_STREAM * Stream1,IN CONST AML_STREAM * Stream2,IN UINT32 Size)556 AmlStreamCmp (
557   IN  CONST AML_STREAM    * Stream1,
558   IN  CONST AML_STREAM    * Stream2,
559   IN        UINT32          Size
560   )
561 {
562   UINT32          MinSize;
563   UINT8         * CurrPosStream1;
564   UINT8         * CurrPosStream2;
565 
566   if (!IS_STREAM (Stream1)                        ||
567       IS_END_OF_STREAM (Stream1)                  ||
568       !IS_STREAM (Stream2)                        ||
569       IS_END_OF_STREAM (Stream2)                  ||
570       (Stream1->Direction != Stream2->Direction)  ||
571       (Size == 0)) {
572     ASSERT (0);
573     return FALSE;
574   }
575 
576   // Check the Size is not longer than the remaining size of
577   // Stream1 and Stream2.
578   MinSize = MIN (
579               AmlStreamGetFreeSpace (Stream1),
580               AmlStreamGetFreeSpace (Stream2)
581               );
582   if (MinSize < Size) {
583     ASSERT (0);
584     return FALSE;
585   }
586 
587   CurrPosStream1 = AmlStreamGetCurrPos (Stream1);
588   if (CurrPosStream1 == NULL) {
589     ASSERT (0);
590     return FALSE;
591   }
592   CurrPosStream2 = AmlStreamGetCurrPos (Stream2);
593   if (CurrPosStream2 == NULL) {
594     ASSERT (0);
595     return FALSE;
596   }
597 
598   if (Stream1->Direction == EAmlStreamDirectionForward) {
599     return (0 == CompareMem (CurrPosStream1, CurrPosStream2, MinSize));
600   }
601 
602   // The stream is already pointing on the last byte, thus the (-1).
603   //          +---------------------+
604   // BStream  | | | | | | | |M|E|T|0|
605   //          +---------------------+
606   //                               ^
607   //                             CurrPos
608   return (0 == CompareMem (
609                   CurrPosStream1 - (MinSize - 1),
610                   CurrPosStream2 - (MinSize - 1),
611                   MinSize
612                   ));
613 }
614 
615 /** Copy Size bytes of the stream's data to DstBuffer.
616 
617   For a backward stream, the bytes are copied starting from the
618   current stream position.
619 
620   @param  [out] DstBuffer         Destination Buffer to copy the data to.
621   @param  [in]  MaxDstBufferSize  Maximum size of DstBuffer.
622                                   Must be non-zero.
623   @param  [in]  Stream            Pointer to the stream to copy the data from.
624   @param  [in]  Size              Number of bytes to copy from the stream
625                                   buffer.
626                                   Must be lower than MaxDstBufferSize.
627                                   Must be lower than Stream's MaxBufferSize.
628                                   Return success if zero.
629 
630   @retval EFI_SUCCESS           The function completed successfully.
631   @retval EFI_INVALID_PARAMETER Invalid parameter.
632 **/
633 EFI_STATUS
634 EFIAPI
AmlStreamCpyS(OUT CHAR8 * DstBuffer,IN UINT32 MaxDstBufferSize,IN AML_STREAM * Stream,IN UINT32 Size)635 AmlStreamCpyS (
636   OUT CHAR8         * DstBuffer,
637   IN  UINT32          MaxDstBufferSize,
638   IN  AML_STREAM    * Stream,
639   IN  UINT32          Size
640   )
641 {
642   CHAR8   * StreamBufferStart;
643 
644   // Stream is checked in the function call.
645   if ((DstBuffer == NULL)       ||
646       (MaxDstBufferSize == 0)   ||
647       (Size > MaxDstBufferSize) ||
648       (Size > AmlStreamGetMaxBufferSize (Stream))) {
649     ASSERT (0);
650     return EFI_INVALID_PARAMETER;
651   }
652 
653   if (Size == 0) {
654     return EFI_SUCCESS;
655   }
656 
657   // Find the address at which the data is starting.
658   StreamBufferStart = (CHAR8*)(IS_STREAM_FORWARD (Stream) ?
659                                  Stream->Buffer :
660                                  AmlStreamGetCurrPos (Stream));
661 
662   CopyMem (DstBuffer, StreamBufferStart, Size);
663 
664   return EFI_SUCCESS;
665 }
666