1/** @file
2  Section Extraction Protocol implementation.
3
4  Stream database is implemented as a linked list of section streams,
5  where each stream contains a linked list of children, which may be leaves or
6  encapsulations.
7
8  Children that are encapsulations generate new stream entries
9  when they are created.  Streams can also be created by calls to
10  SEP->OpenSectionStream().
11
12  The database is only created far enough to return the requested data from
13  any given stream, or to determine that the requested data is not found.
14
15  If a GUIDed encapsulation is encountered, there are three possiblilites.
16
17  1) A support protocol is found, in which the stream is simply processed with
18     the support protocol.
19
20  2) A support protocol is not found, but the data is available to be read
21     without processing.  In this case, the database is built up through the
22     recursions to return the data, and a RPN event is set that will enable
23     the stream in question to be refreshed if and when the required section
24     extraction protocol is published.This insures the AuthenticationStatus
25     does not become stale in the cache.
26
27  3) A support protocol is not found, and the data is not available to be read
28     without it.  This results in EFI_PROTOCOL_ERROR.
29
30Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
31This program and the accompanying materials
32are licensed and made available under the terms and conditions of the BSD License
33which accompanies this distribution.  The full text of the license may be found at
34http://opensource.org/licenses/bsd-license.php
35
36THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
37WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
38
39**/
40
41#include <FrameworkDxe.h>
42
43#include <Library/BaseLib.h>
44#include <Library/DebugLib.h>
45#include <Library/UefiBootServicesTableLib.h>
46#include <Library/MemoryAllocationLib.h>
47#include <Library/BaseMemoryLib.h>
48#include <Library/UefiLib.h>
49#include <Protocol/Decompress.h>
50#include <Protocol/GuidedSectionExtraction.h>
51#include <Protocol/SectionExtraction.h>
52
53//
54// Local defines and typedefs
55//
56#define FRAMEWORK_SECTION_CHILD_SIGNATURE  SIGNATURE_32('S','X','F','S')
57#define CHILD_SECTION_NODE_FROM_LINK(Node) \
58  CR (Node, FRAMEWORK_SECTION_CHILD_NODE, Link, FRAMEWORK_SECTION_CHILD_SIGNATURE)
59
60typedef struct {
61  UINT32                      Signature;
62  LIST_ENTRY                  Link;
63  UINT32                      Type;
64  UINT32                      Size;
65  //
66  // StreamBase + OffsetInStream == pointer to section header in stream.  The
67  // stream base is always known when walking the sections within.
68  //
69  UINT32                      OffsetInStream;
70  //
71  // Then EncapsulatedStreamHandle below is always 0 if the section is NOT an
72  // encapsulating section.  Otherwise, it contains the stream handle
73  // of the encapsulated stream.  This handle is ALWAYS produced any time an
74  // encapsulating child is encountered, irrespective of whether the
75  // encapsulated stream is processed further.
76  //
77  UINTN                       EncapsulatedStreamHandle;
78  EFI_GUID                    *EncapsulationGuid;
79  //
80  // If the section REQUIRES an extraction protocol, register for RPN
81  // when the required GUIDed extraction protocol becomes available.
82  //
83  EFI_EVENT                   Event;
84} FRAMEWORK_SECTION_CHILD_NODE;
85
86#define FRAMEWORK_SECTION_STREAM_SIGNATURE SIGNATURE_32('S','X','S','S')
87#define STREAM_NODE_FROM_LINK(Node) \
88  CR (Node, FRAMEWORK_SECTION_STREAM_NODE, Link, FRAMEWORK_SECTION_STREAM_SIGNATURE)
89
90typedef struct {
91  UINT32                      Signature;
92  LIST_ENTRY                  Link;
93  UINTN                       StreamHandle;
94  UINT8                       *StreamBuffer;
95  UINTN                       StreamLength;
96  LIST_ENTRY                  Children;
97  //
98  // Authentication status is from GUIDed encapsulations.
99  //
100  UINT32                      AuthenticationStatus;
101} FRAMEWORK_SECTION_STREAM_NODE;
102
103#define NULL_STREAM_HANDLE    0
104
105typedef struct {
106  FRAMEWORK_SECTION_CHILD_NODE     *ChildNode;
107  FRAMEWORK_SECTION_STREAM_NODE    *ParentStream;
108  VOID                             *Registration;
109} RPN_EVENT_CONTEXT;
110
111/**
112  SEP member function.  This function creates and returns a new section stream
113  handle to represent the new section stream.
114
115  @param This                 Indicates the calling context.
116  @param SectionStreamLength  Size in bytes of the section stream.
117  @param SectionStream        Buffer containing the new section stream.
118  @param SectionStreamHandle  A pointer to a caller allocated UINTN that on output
119                              contains the new section stream handle.
120
121  @retval EFI_SUCCESS           Section wase opened successfully.
122  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
123  @retval EFI_INVALID_PARAMETER Section stream does not end concident with end of
124                                last section.
125
126**/
127EFI_STATUS
128EFIAPI
129OpenSectionStream (
130  IN     EFI_SECTION_EXTRACTION_PROTOCOL           *This,
131  IN     UINTN                                     SectionStreamLength,
132  IN     VOID                                      *SectionStream,
133     OUT UINTN                                     *SectionStreamHandle
134  )
135;
136
137/**
138  SEP member function.  Retrieves requested section from section stream.
139
140  @param This                  Pointer to SEP instance.
141  @param SectionStreamHandle   The section stream from which to extract the requested
142                               section.
143  @param SectionType           A pointer to the type of section to search for.
144  @param SectionDefinitionGuid If the section type is EFI_SECTION_GUID_DEFINED, then
145                               SectionDefinitionGuid indicates which of these types
146                               of sections to search for.
147  @param SectionInstance       Indicates which instance of the requested section to
148                               return.
149  @param Buffer                Double indirection to buffer.  If *Buffer is non-null on
150                               input, then the buffer is caller allocated.  If
151                               *Buffer is NULL, then the buffer is callee allocated.
152                               In either case, the required buffer size is returned
153                               in *BufferSize.
154  @param BufferSize            On input, indicates the size of *Buffer if *Buffer is
155                               non-null on input.  On output, indicates the required
156                               size (allocated size if callee allocated) of *Buffer.
157  @param AuthenticationStatus  Indicates the authentication status of the retrieved
158                               section.
159
160
161  @retval EFI_SUCCESS           Section was retrieved successfully
162  @retval EFI_PROTOCOL_ERROR    A GUID defined section was encountered in the section
163                                stream with its EFI_GUIDED_SECTION_PROCESSING_REQUIRED
164                                bit set, but there was no corresponding GUIDed Section
165                                Extraction Protocol in the handle database.  *Buffer is
166                                unmodified.
167  @retval EFI_NOT_FOUND         An error was encountered when parsing the SectionStream.
168                                This indicates the SectionStream  is not correctly
169                                formatted.
170  @retval EFI_NOT_FOUND         The requested section does not exist.
171  @retval EFI_OUT_OF_RESOURCES  The system has insufficient resources to process the
172                                request.
173  @retval EFI_INVALID_PARAMETER The SectionStreamHandle does not exist.
174  @retval EFI_WARN_TOO_SMALL    The size of the caller allocated input buffer is
175                                insufficient to contain the requested section.  The
176                                input buffer is filled and contents are section contents
177                                are truncated.
178
179**/
180EFI_STATUS
181EFIAPI
182GetSection (
183  IN EFI_SECTION_EXTRACTION_PROTOCOL                    *This,
184  IN UINTN                                              SectionStreamHandle,
185  IN EFI_SECTION_TYPE                                   *SectionType,
186  IN EFI_GUID                                           *SectionDefinitionGuid,
187  IN UINTN                                              SectionInstance,
188  IN VOID                                               **Buffer,
189  IN OUT UINTN                                          *BufferSize,
190  OUT UINT32                                            *AuthenticationStatus
191  )
192;
193
194/**
195  SEP member function.  Deletes an existing section stream
196
197  @param This                   Indicates the calling context.
198  @param StreamHandleToClose    Indicates the stream to close
199
200  @retval EFI_SUCCESS           Section stream was closed successfully.
201  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
202  @retval EFI_INVALID_PARAMETER Section stream does not end concident with end of
203                                last section.
204
205**/
206EFI_STATUS
207EFIAPI
208CloseSectionStream (
209  IN  EFI_SECTION_EXTRACTION_PROTOCOL           *This,
210  IN  UINTN                                     StreamHandleToClose
211  )
212;
213
214//
215// Module globals
216//
217LIST_ENTRY mStreamRoot = INITIALIZE_LIST_HEAD_VARIABLE (mStreamRoot);
218
219EFI_HANDLE mSectionExtractionHandle = NULL;
220
221EFI_SECTION_EXTRACTION_PROTOCOL mSectionExtraction = {
222  OpenSectionStream,
223  GetSection,
224  CloseSectionStream
225};
226
227/**
228  Entry point of the section extraction code. Initializes an instance of the
229  section extraction interface and installs it on a new handle.
230
231  @param ImageHandle             A handle for the image that is initializing this driver
232  @param SystemTable             A pointer to the EFI system table
233
234  @retval EFI_SUCCESS            Driver initialized successfully
235  @retval EFI_OUT_OF_RESOURCES   Could not allocate needed resources
236
237**/
238EFI_STATUS
239EFIAPI
240SectionExtractionEntryPoint (
241  IN EFI_HANDLE                   ImageHandle,
242  IN EFI_SYSTEM_TABLE             *SystemTable
243  )
244{
245  EFI_STATUS                         Status;
246
247  //
248  // Install SEP to a new handle
249  //
250  Status = gBS->InstallProtocolInterface (
251                  &mSectionExtractionHandle,
252                  &gEfiSectionExtractionProtocolGuid,
253                  EFI_NATIVE_INTERFACE,
254                  &mSectionExtraction
255                  );
256  ASSERT_EFI_ERROR (Status);
257
258  return Status;
259}
260
261/**
262
263  Check if a stream is valid.
264
265  @param SectionStream         The section stream to be checked
266  @param SectionStreamLength   The length of section stream
267
268  @return A boolean value indicating the validness of the section stream.
269
270**/
271BOOLEAN
272IsValidSectionStream (
273  IN  VOID              *SectionStream,
274  IN  UINTN             SectionStreamLength
275  )
276{
277  UINTN                       TotalLength;
278  UINTN                       SectionLength;
279  EFI_COMMON_SECTION_HEADER   *SectionHeader;
280  EFI_COMMON_SECTION_HEADER   *NextSectionHeader;
281
282  TotalLength = 0;
283  SectionHeader = (EFI_COMMON_SECTION_HEADER *)SectionStream;
284
285  while (TotalLength < SectionStreamLength) {
286    if (IS_SECTION2 (SectionHeader)) {
287      SectionLength = SECTION2_SIZE (SectionHeader);
288    } else {
289      SectionLength = SECTION_SIZE (SectionHeader);
290    }
291    TotalLength += SectionLength;
292
293    if (TotalLength == SectionStreamLength) {
294      return TRUE;
295    }
296
297    //
298    // Move to the next byte following the section...
299    //
300    SectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINT8 *) SectionHeader + SectionLength);
301
302    //
303    // Figure out where the next section begins
304    //
305    NextSectionHeader = ALIGN_POINTER(SectionHeader, 4);
306    TotalLength += (UINTN) NextSectionHeader - (UINTN) SectionHeader;
307    SectionHeader = NextSectionHeader;
308  }
309
310  ASSERT (FALSE);
311  return FALSE;
312}
313
314/**
315  Worker function.  Constructor for section streams.
316
317  @param SectionStreamLength   Size in bytes of the section stream.
318  @param SectionStream         Buffer containing the new section stream.
319  @param AllocateBuffer        Indicates whether the stream buffer is to be copied
320                               or the input buffer is to be used in place.
321  @param AuthenticationStatus  Indicates the default authentication status for the
322                               new stream.
323  @param SectionStreamHandle   A pointer to a caller allocated section stream handle.
324
325  @retval EFI_SUCCESS           Stream was added to stream database.
326  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
327
328**/
329EFI_STATUS
330OpenSectionStreamEx (
331  IN     UINTN                                     SectionStreamLength,
332  IN     VOID                                      *SectionStream,
333  IN     BOOLEAN                                   AllocateBuffer,
334  IN     UINT32                                    AuthenticationStatus,
335     OUT UINTN                                     *SectionStreamHandle
336  )
337{
338  FRAMEWORK_SECTION_STREAM_NODE    *NewStream;
339  EFI_TPL                          OldTpl;
340
341  //
342  // Allocate a new stream
343  //
344  NewStream = AllocatePool (sizeof (FRAMEWORK_SECTION_STREAM_NODE));
345  if (NewStream == NULL) {
346    return EFI_OUT_OF_RESOURCES;
347  }
348
349  if (AllocateBuffer) {
350    //
351    // if we're here, we're double buffering, allocate the buffer and copy the
352    // data in
353    //
354    if (SectionStreamLength > 0) {
355      NewStream->StreamBuffer = AllocatePool (SectionStreamLength);
356      if (NewStream->StreamBuffer == NULL) {
357        FreePool (NewStream);
358        return EFI_OUT_OF_RESOURCES;
359      }
360      //
361      // Copy in stream data
362      //
363      CopyMem (NewStream->StreamBuffer, SectionStream, SectionStreamLength);
364    } else {
365      //
366      // It's possible to have a zero length section stream.
367      //
368      NewStream->StreamBuffer = NULL;
369    }
370  } else {
371    //
372    // If were here, the caller has supplied the buffer (it's an internal call)
373    // so just assign the buffer.  This happens when we open section streams
374    // as a result of expanding an encapsulating section.
375    //
376    NewStream->StreamBuffer = SectionStream;
377  }
378
379  //
380  // Initialize the rest of the section stream
381  //
382  NewStream->Signature = FRAMEWORK_SECTION_STREAM_SIGNATURE;
383  NewStream->StreamHandle = (UINTN) NewStream;
384  NewStream->StreamLength = SectionStreamLength;
385  InitializeListHead (&NewStream->Children);
386  NewStream->AuthenticationStatus = AuthenticationStatus;
387
388  //
389  // Add new stream to stream list
390  //
391  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
392  InsertTailList (&mStreamRoot, &NewStream->Link);
393  gBS->RestoreTPL (OldTpl);
394
395  *SectionStreamHandle = NewStream->StreamHandle;
396
397  return EFI_SUCCESS;
398}
399
400/**
401  SEP member function.  This function creates and returns a new section stream
402  handle to represent the new section stream.
403
404  @param This                 Indicates the calling context.
405  @param SectionStreamLength  Size in bytes of the section stream.
406  @param SectionStream        Buffer containing the new section stream.
407  @param SectionStreamHandle  A pointer to a caller allocated UINTN that on output
408                              contains the new section stream handle.
409
410  @retval EFI_SUCCESS           Section wase opened successfully.
411  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
412  @retval EFI_INVALID_PARAMETER Section stream does not end concident with end of
413                                last section.
414
415**/
416EFI_STATUS
417EFIAPI
418OpenSectionStream (
419  IN     EFI_SECTION_EXTRACTION_PROTOCOL           *This,
420  IN     UINTN                                     SectionStreamLength,
421  IN     VOID                                      *SectionStream,
422     OUT UINTN                                     *SectionStreamHandle
423  )
424{
425  //
426  // Check to see section stream looks good...
427  //
428  if (!IsValidSectionStream (SectionStream, SectionStreamLength)) {
429    return EFI_INVALID_PARAMETER;
430  }
431
432  return OpenSectionStreamEx (
433          SectionStreamLength,
434          SectionStream,
435          TRUE,
436          0,
437          SectionStreamHandle
438          );
439}
440
441/**
442  Worker function.  Determine if the input stream:child matches the input type.
443
444  @param Stream                 Indicates the section stream associated with the child
445  @param Child                  Indicates the child to check
446  @param SearchType             Indicates the type of section to check against for
447  @param SectionDefinitionGuid  Indicates the GUID to check against if the type is
448                                EFI_SECTION_GUID_DEFINED
449
450  @retval TRUE                  The child matches
451  @retval FALSE                 The child doesn't match
452
453**/
454BOOLEAN
455ChildIsType (
456  IN FRAMEWORK_SECTION_STREAM_NODE *Stream,
457  IN FRAMEWORK_SECTION_CHILD_NODE  *Child,
458  IN EFI_SECTION_TYPE              SearchType,
459  IN EFI_GUID                      *SectionDefinitionGuid
460  )
461{
462  EFI_GUID_DEFINED_SECTION    *GuidedSection;
463
464  if (SearchType == EFI_SECTION_ALL) {
465    return TRUE;
466  }
467  if (Child->Type != SearchType) {
468    return FALSE;
469  }
470  if ((SearchType != EFI_SECTION_GUID_DEFINED) || (SectionDefinitionGuid == NULL)) {
471    return TRUE;
472  }
473  GuidedSection = (EFI_GUID_DEFINED_SECTION * )(Stream->StreamBuffer + Child->OffsetInStream);
474  if (IS_SECTION2 (GuidedSection)) {
475    return CompareGuid (&(((EFI_GUID_DEFINED_SECTION2 *) GuidedSection)->SectionDefinitionGuid), SectionDefinitionGuid);
476  } else {
477    return CompareGuid (&GuidedSection->SectionDefinitionGuid, SectionDefinitionGuid);
478  }
479}
480
481/**
482  Create a protocol notification event and return it.
483
484  @param ProtocolGuid    Protocol to register notification event on.
485  @param NotifyTpl       Maximum TPL to signal the NotifyFunction.
486  @param NotifyFunction  EFI notification routine.
487  @param NotifyContext   Context passed into Event when it is created.
488  @param Registration    Registration key returned from RegisterProtocolNotify().
489  @param SignalFlag      Boolean value to decide whether kick the event after register or not.
490
491  @return The EFI_EVENT that has been registered to be signaled when a ProtocolGuid
492           is added to the system.
493
494**/
495EFI_EVENT
496CreateProtocolNotifyEvent (
497  IN EFI_GUID             *ProtocolGuid,
498  IN EFI_TPL              NotifyTpl,
499  IN EFI_EVENT_NOTIFY     NotifyFunction,
500  IN VOID                 *NotifyContext,
501  OUT VOID                **Registration,
502  IN  BOOLEAN             SignalFlag
503  )
504{
505  EFI_STATUS              Status;
506  EFI_EVENT               Event;
507
508  //
509  // Create the event
510  //
511
512  Status = gBS->CreateEvent (
513            EVT_NOTIFY_SIGNAL,
514            NotifyTpl,
515            NotifyFunction,
516            NotifyContext,
517           &Event
518            );
519  ASSERT_EFI_ERROR (Status);
520
521  //
522  // Register for protocol notifactions on this event
523  //
524
525  Status = gBS->RegisterProtocolNotify (
526            ProtocolGuid,
527            Event,
528            Registration
529            );
530  ASSERT_EFI_ERROR (Status);
531
532  if (SignalFlag) {
533    //
534    // Kick the event so we will perform an initial pass of
535    // current installed drivers
536    //
537    gBS->SignalEvent (Event);
538  }
539
540  return Event;
541}
542
543/**
544  Verify the Guided Section GUID by checking if there is the Guided Section GUID configuration table recorded the GUID itself.
545
546  @param GuidedSectionGuid          The Guided Section GUID.
547  @param GuidedSectionExtraction    A pointer to the pointer to the supported Guided Section Extraction Protocol
548                                    for the Guided Section.
549
550  @return TRUE      The GuidedSectionGuid could be identified, and the pointer to
551                    the Guided Section Extraction Protocol will be returned to *GuidedSectionExtraction.
552  @return FALSE     The GuidedSectionGuid could not be identified, or
553                    the Guided Section Extraction Protocol has not been installed yet.
554
555**/
556BOOLEAN
557VerifyGuidedSectionGuid (
558  IN  EFI_GUID                                  *GuidedSectionGuid,
559  OUT EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL    **GuidedSectionExtraction
560  )
561{
562  EFI_GUID              *GuidRecorded;
563  VOID                  *Interface;
564  EFI_STATUS            Status;
565
566  //
567  // Check if there is the Guided Section GUID configuration table recorded the GUID itself.
568  //
569  Status = EfiGetSystemConfigurationTable (GuidedSectionGuid, (VOID **) &GuidRecorded);
570  if (Status == EFI_SUCCESS) {
571    if (CompareGuid (GuidRecorded, GuidedSectionGuid)) {
572      //
573      // Found the recorded GuidedSectionGuid.
574      //
575      Status = gBS->LocateProtocol (GuidedSectionGuid, NULL, (VOID **) &Interface);
576      if (!EFI_ERROR (Status) && Interface != NULL) {
577        //
578        // Found the supported Guided Section Extraction Porotocol for the Guided Section.
579        //
580        *GuidedSectionExtraction = (EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *) Interface;
581        return TRUE;
582      }
583      return FALSE;
584    }
585  }
586
587  return FALSE;
588}
589
590/**
591  RPN callback function.
592  1. Initialize the section stream when the GUIDED_SECTION_EXTRACTION_PROTOCOL is installed.
593  2. Removes a stale section stream and re-initializes it with an updated AuthenticationStatus.
594
595  @param Event               The event that fired
596  @param RpnContext          A pointer to the context that allows us to identify
597                             the relevent encapsulation.
598
599**/
600VOID
601EFIAPI
602NotifyGuidedExtraction (
603  IN   EFI_EVENT   Event,
604  IN   VOID        *RpnContext
605  )
606{
607  EFI_STATUS                              Status;
608  EFI_GUID_DEFINED_SECTION                *GuidedHeader;
609  EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL  *GuidedExtraction;
610  VOID                                    *NewStreamBuffer;
611  UINTN                                   NewStreamBufferSize;
612  UINT32                                  AuthenticationStatus;
613  RPN_EVENT_CONTEXT                       *Context;
614
615  Context = RpnContext;
616  Status = EFI_SUCCESS;
617  if (Context->ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
618    Status = CloseSectionStream (&mSectionExtraction, Context->ChildNode->EncapsulatedStreamHandle);
619  }
620  if (!EFI_ERROR (Status)) {
621    //
622    // The stream is not initialized, open it.
623    // Or the stream closed successfully, so re-open the stream with correct AuthenticationStatus.
624    //
625
626    GuidedHeader = (EFI_GUID_DEFINED_SECTION *)
627      (Context->ParentStream->StreamBuffer + Context->ChildNode->OffsetInStream);
628    ASSERT (GuidedHeader->CommonHeader.Type == EFI_SECTION_GUID_DEFINED);
629
630    if (!VerifyGuidedSectionGuid (Context->ChildNode->EncapsulationGuid, &GuidedExtraction)) {
631      return;
632    }
633
634    Status = GuidedExtraction->ExtractSection (
635                                 GuidedExtraction,
636                                 GuidedHeader,
637                                 &NewStreamBuffer,
638                                 &NewStreamBufferSize,
639                                 &AuthenticationStatus
640                                 );
641    ASSERT_EFI_ERROR (Status);
642    //
643    // OR in the parent stream's aggregate status.
644    //
645    AuthenticationStatus |= Context->ParentStream->AuthenticationStatus & EFI_AGGREGATE_AUTH_STATUS_ALL;
646    Status = OpenSectionStreamEx (
647               NewStreamBufferSize,
648               NewStreamBuffer,
649               FALSE,
650               AuthenticationStatus,
651               &Context->ChildNode->EncapsulatedStreamHandle
652               );
653    ASSERT_EFI_ERROR (Status);
654  }
655
656  //
657  //  If above, the stream did not close successfully, it indicates it's
658  //  already been closed by someone, so just destroy the event and be done with
659  //  it.
660  //
661
662  gBS->CloseEvent (Event);
663  Context->ChildNode->Event = NULL;
664  FreePool (Context);
665}
666
667/**
668  Worker function.  Constructor for RPN event if needed to keep AuthenticationStatus
669  cache correct when a missing GUIDED_SECTION_EXTRACTION_PROTOCOL appears...
670
671  @param ParentStream        Indicates the parent of the ecnapsulation section (child)
672  @param ChildNode           Indicates the child node that is the encapsulation section.
673
674**/
675VOID
676CreateGuidedExtractionRpnEvent (
677  IN FRAMEWORK_SECTION_STREAM_NODE       *ParentStream,
678  IN FRAMEWORK_SECTION_CHILD_NODE        *ChildNode
679  )
680{
681  RPN_EVENT_CONTEXT *Context;
682
683  //
684  // Allocate new event structure and context
685  //
686  Context = AllocatePool (sizeof (RPN_EVENT_CONTEXT));
687  ASSERT (Context != NULL);
688
689  Context->ChildNode = ChildNode;
690  Context->ParentStream = ParentStream;
691
692  Context->ChildNode->Event = CreateProtocolNotifyEvent (
693                                Context->ChildNode->EncapsulationGuid,
694                                TPL_NOTIFY,
695                                NotifyGuidedExtraction,
696                                Context,
697                                &Context->Registration,
698                                FALSE
699                                );
700}
701
702/**
703  Worker function.  Constructor for new child nodes.
704
705  @param Stream                Indicates the section stream in which to add the child.
706  @param ChildOffset           Indicates the offset in Stream that is the beginning
707                               of the child section.
708  @param ChildNode             Indicates the Callee allocated and initialized child.
709
710  @retval EFI_SUCCESS          Child node was found and returned.
711  @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
712  @retval EFI_PROTOCOL_ERROR   Encapsulation sections produce new stream handles when
713                               the child node is created.  If the section type is GUID
714                               defined, and the extraction GUID does not exist, and
715                               producing the stream requires the GUID, then a protocol
716                               error is generated and no child is produced.
717                               Values returned by OpenSectionStreamEx.
718
719**/
720EFI_STATUS
721CreateChildNode (
722  IN     FRAMEWORK_SECTION_STREAM_NODE              *Stream,
723  IN     UINT32                                     ChildOffset,
724  OUT    FRAMEWORK_SECTION_CHILD_NODE               **ChildNode
725  )
726{
727  EFI_STATUS                                   Status;
728  EFI_COMMON_SECTION_HEADER                    *SectionHeader;
729  EFI_COMPRESSION_SECTION                      *CompressionHeader;
730  EFI_GUID_DEFINED_SECTION                     *GuidedHeader;
731  EFI_DECOMPRESS_PROTOCOL                      *Decompress;
732  EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL       *GuidedExtraction;
733  VOID                                         *NewStreamBuffer;
734  VOID                                         *ScratchBuffer;
735  UINT32                                       ScratchSize;
736  UINTN                                        NewStreamBufferSize;
737  UINT32                                       AuthenticationStatus;
738  VOID                                         *CompressionSource;
739  UINT32                                       CompressionSourceSize;
740  UINT32                                       UncompressedLength;
741  UINT8                                        CompressionType;
742  UINT16                                       GuidedSectionAttributes;
743
744  FRAMEWORK_SECTION_CHILD_NODE                      *Node;
745
746  SectionHeader = (EFI_COMMON_SECTION_HEADER *) (Stream->StreamBuffer + ChildOffset);
747
748  //
749  // Allocate a new node
750  //
751  *ChildNode = AllocateZeroPool (sizeof (FRAMEWORK_SECTION_CHILD_NODE));
752  Node = *ChildNode;
753  if (Node == NULL) {
754    return EFI_OUT_OF_RESOURCES;
755  }
756
757  //
758  // Now initialize it
759  //
760  Node->Signature = FRAMEWORK_SECTION_CHILD_SIGNATURE;
761  Node->Type = SectionHeader->Type;
762  if (IS_SECTION2 (SectionHeader)) {
763    Node->Size = SECTION2_SIZE (SectionHeader);
764  } else {
765    Node->Size = SECTION_SIZE (SectionHeader);
766  }
767  Node->OffsetInStream = ChildOffset;
768  Node->EncapsulatedStreamHandle = NULL_STREAM_HANDLE;
769  Node->EncapsulationGuid = NULL;
770
771  //
772  // If it's an encapsulating section, then create the new section stream also
773  //
774  switch (Node->Type) {
775    case EFI_SECTION_COMPRESSION:
776      //
777      // Get the CompressionSectionHeader
778      //
779      if (Node->Size < sizeof (EFI_COMPRESSION_SECTION)) {
780        FreePool (Node);
781        return EFI_NOT_FOUND;
782      }
783
784      CompressionHeader = (EFI_COMPRESSION_SECTION *) SectionHeader;
785
786      if (IS_SECTION2 (CompressionHeader)) {
787        CompressionSource = (VOID *) ((UINT8 *) CompressionHeader + sizeof (EFI_COMPRESSION_SECTION2));
788        CompressionSourceSize = (UINT32) (SECTION2_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION2));
789        UncompressedLength = ((EFI_COMPRESSION_SECTION2 *) CompressionHeader)->UncompressedLength;
790        CompressionType = ((EFI_COMPRESSION_SECTION2 *) CompressionHeader)->CompressionType;
791      } else {
792        CompressionSource = (VOID *) ((UINT8 *) CompressionHeader + sizeof (EFI_COMPRESSION_SECTION));
793        CompressionSourceSize = (UINT32) (SECTION_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION));
794        UncompressedLength = CompressionHeader->UncompressedLength;
795        CompressionType = CompressionHeader->CompressionType;
796      }
797
798      //
799      // Allocate space for the new stream
800      //
801      if (UncompressedLength > 0) {
802        NewStreamBufferSize = UncompressedLength;
803        NewStreamBuffer = AllocatePool (NewStreamBufferSize);
804        if (NewStreamBuffer == NULL) {
805          FreePool (Node);
806          return EFI_OUT_OF_RESOURCES;
807        }
808
809        if (CompressionType == EFI_NOT_COMPRESSED) {
810          //
811          // stream is not actually compressed, just encapsulated.  So just copy it.
812          //
813          CopyMem (NewStreamBuffer, CompressionSource, NewStreamBufferSize);
814        } else if (CompressionType == EFI_STANDARD_COMPRESSION) {
815          //
816          // Only support the EFI_SATNDARD_COMPRESSION algorithm.
817          //
818
819          //
820          // Decompress the stream
821          //
822          Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **)&Decompress);
823
824          ASSERT_EFI_ERROR (Status);
825
826          Status = Decompress->GetInfo (
827                                 Decompress,
828                                 CompressionSource,
829                                 CompressionSourceSize,
830                                 (UINT32 *)&NewStreamBufferSize,
831                                 &ScratchSize
832                                 );
833          if (EFI_ERROR (Status) || (NewStreamBufferSize != UncompressedLength)) {
834            FreePool (Node);
835            FreePool (NewStreamBuffer);
836            if (!EFI_ERROR (Status)) {
837              Status = EFI_BAD_BUFFER_SIZE;
838            }
839            return Status;
840          }
841
842          ScratchBuffer = AllocatePool (ScratchSize);
843          if (ScratchBuffer == NULL) {
844            FreePool (Node);
845            FreePool (NewStreamBuffer);
846            return EFI_OUT_OF_RESOURCES;
847          }
848
849          Status = Decompress->Decompress (
850                                 Decompress,
851                                 CompressionSource,
852                                 CompressionSourceSize,
853                                 NewStreamBuffer,
854                                 (UINT32)NewStreamBufferSize,
855                                 ScratchBuffer,
856                                 ScratchSize
857                                 );
858          FreePool (ScratchBuffer);
859          if (EFI_ERROR (Status)) {
860            FreePool (Node);
861            FreePool (NewStreamBuffer);
862            return Status;
863          }
864        }
865      } else {
866        NewStreamBuffer = NULL;
867        NewStreamBufferSize = 0;
868      }
869
870      Status = OpenSectionStreamEx (
871                 NewStreamBufferSize,
872                 NewStreamBuffer,
873                 FALSE,
874                 Stream->AuthenticationStatus,
875                 &Node->EncapsulatedStreamHandle
876                 );
877      if (EFI_ERROR (Status)) {
878        FreePool (Node);
879        FreePool (NewStreamBuffer);
880        return Status;
881      }
882      break;
883
884    case EFI_SECTION_GUID_DEFINED:
885      GuidedHeader = (EFI_GUID_DEFINED_SECTION *) SectionHeader;
886      if (IS_SECTION2 (GuidedHeader)) {
887        Node->EncapsulationGuid = &(((EFI_GUID_DEFINED_SECTION2 *) GuidedHeader)->SectionDefinitionGuid);
888        GuidedSectionAttributes = ((EFI_GUID_DEFINED_SECTION2 *) GuidedHeader)->Attributes;
889      } else {
890        Node->EncapsulationGuid = &GuidedHeader->SectionDefinitionGuid;
891        GuidedSectionAttributes = GuidedHeader->Attributes;
892      }
893      if (VerifyGuidedSectionGuid (Node->EncapsulationGuid, &GuidedExtraction)) {
894        //
895        // NewStreamBuffer is always allocated by ExtractSection... No caller
896        // allocation here.
897        //
898        Status = GuidedExtraction->ExtractSection (
899                                     GuidedExtraction,
900                                     GuidedHeader,
901                                     &NewStreamBuffer,
902                                     &NewStreamBufferSize,
903                                     &AuthenticationStatus
904                                     );
905        if (EFI_ERROR (Status)) {
906          FreePool (*ChildNode);
907          return EFI_PROTOCOL_ERROR;
908        }
909
910        //
911        // Make sure we initialize the new stream with the correct
912        // authentication status for both aggregate and local status fields.
913        //
914        if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) == EFI_GUIDED_SECTION_AUTH_STATUS_VALID) {
915          //
916          // OR in the parent stream's aggregate status.
917          //
918          AuthenticationStatus |= Stream->AuthenticationStatus & EFI_AGGREGATE_AUTH_STATUS_ALL;
919        } else {
920          //
921          // since there's no authentication data contributed by the section,
922          // just inherit the full value from our immediate parent.
923          //
924          AuthenticationStatus = Stream->AuthenticationStatus;
925        }
926
927        Status = OpenSectionStreamEx (
928                   NewStreamBufferSize,
929                   NewStreamBuffer,
930                   FALSE,
931                   AuthenticationStatus,
932                   &Node->EncapsulatedStreamHandle
933                   );
934        if (EFI_ERROR (Status)) {
935          FreePool (*ChildNode);
936          FreePool (NewStreamBuffer);
937          return Status;
938        }
939      } else {
940        //
941        // There's no GUIDed section extraction protocol available.
942        //
943        if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == EFI_GUIDED_SECTION_PROCESSING_REQUIRED) {
944          //
945          // If the section REQUIRES an extraction protocol, register for RPN
946          // when the required GUIDed extraction protocol becomes available.
947          //
948          AuthenticationStatus = 0;
949          CreateGuidedExtractionRpnEvent (Stream, Node);
950        } else {
951          //
952          // Figure out the proper authentication status
953          //
954          AuthenticationStatus = Stream->AuthenticationStatus;
955          if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) == EFI_GUIDED_SECTION_AUTH_STATUS_VALID) {
956            //
957            //  The local status of the new stream is contained in
958            //  AuthenticaionStatus.  This value needs to be ORed into the
959            //  Aggregate bits also...
960            //
961
962            //
963            // Clear out and initialize the local status
964            //
965            AuthenticationStatus &= ~EFI_LOCAL_AUTH_STATUS_ALL;
966            AuthenticationStatus |= EFI_LOCAL_AUTH_STATUS_IMAGE_SIGNED | EFI_LOCAL_AUTH_STATUS_NOT_TESTED;
967            //
968            // OR local status into aggregate status
969            //
970            AuthenticationStatus |= AuthenticationStatus >> 16;
971          }
972
973          if (IS_SECTION2 (GuidedHeader)) {
974            Status = OpenSectionStreamEx (
975                       SECTION2_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION2 *) GuidedHeader)->DataOffset,
976                       (UINT8 *) GuidedHeader + ((EFI_GUID_DEFINED_SECTION2 *) GuidedHeader)->DataOffset,
977                       TRUE,
978                       AuthenticationStatus,
979                       &Node->EncapsulatedStreamHandle
980                       );
981          } else {
982            Status = OpenSectionStreamEx (
983                       SECTION_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION *) GuidedHeader)->DataOffset,
984                       (UINT8 *) GuidedHeader + ((EFI_GUID_DEFINED_SECTION *) GuidedHeader)->DataOffset,
985                       TRUE,
986                       AuthenticationStatus,
987                       &Node->EncapsulatedStreamHandle
988                       );
989          }
990          if (EFI_ERROR (Status)) {
991            FreePool (Node);
992            return Status;
993          }
994        }
995      }
996
997      if ((AuthenticationStatus & EFI_LOCAL_AUTH_STATUS_ALL) ==
998            (EFI_LOCAL_AUTH_STATUS_IMAGE_SIGNED | EFI_LOCAL_AUTH_STATUS_NOT_TESTED)) {
999        //
1000        // Need to register for RPN for when the required GUIDed extraction
1001        // protocol becomes available.  This will enable us to refresh the
1002        // AuthenticationStatus cached in the Stream if it's ever requested
1003        // again.
1004        //
1005        CreateGuidedExtractionRpnEvent (Stream, Node);
1006      }
1007
1008      break;
1009
1010    default:
1011
1012      //
1013      // Nothing to do if it's a leaf
1014      //
1015      break;
1016  }
1017
1018  //
1019  // Last, add the new child node to the stream
1020  //
1021  InsertTailList (&Stream->Children, &Node->Link);
1022
1023  return EFI_SUCCESS;
1024}
1025
1026/**
1027  Worker function  Recursively searches / builds section stream database
1028  looking for requested section.
1029
1030
1031  @param SourceStream          Indicates the section stream in which to do the search.
1032  @param SearchType            Indicates the type of section to search for.
1033  @param SectionInstance       Indicates which instance of section to find.  This is
1034                               an in/out parameter to deal with recursions.
1035  @param SectionDefinitionGuid Guid of section definition
1036  @param FoundChild            Output indicating the child node that is found.
1037  @param FoundStream           Output indicating which section stream the child was
1038                               found in.  If this stream was generated as a result of
1039                               an encapsulation section, the streamhandle is visible
1040                               within the SEP driver only.
1041  @param AuthenticationStatus  Indicates the authentication status of the found section.
1042
1043  @retval EFI_SUCCESS          Child node was found and returned.
1044  @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1045  @retval EFI_NOT_FOUND        Requested child node does not exist.
1046  @retval EFI_PROTOCOL_ERROR   A required GUIDED section extraction protocol does not
1047                               exist
1048
1049**/
1050EFI_STATUS
1051FindChildNode (
1052  IN     FRAMEWORK_SECTION_STREAM_NODE                   *SourceStream,
1053  IN     EFI_SECTION_TYPE                           SearchType,
1054  IN OUT UINTN                                      *SectionInstance,
1055  IN     EFI_GUID                                   *SectionDefinitionGuid,
1056  OUT    FRAMEWORK_SECTION_CHILD_NODE                    **FoundChild,
1057  OUT    FRAMEWORK_SECTION_STREAM_NODE                   **FoundStream,
1058  OUT    UINT32                                     *AuthenticationStatus
1059  )
1060{
1061  FRAMEWORK_SECTION_CHILD_NODE                       *CurrentChildNode;
1062  FRAMEWORK_SECTION_CHILD_NODE                       *RecursedChildNode;
1063  FRAMEWORK_SECTION_STREAM_NODE                      *RecursedFoundStream;
1064  UINT32                                        NextChildOffset;
1065  EFI_STATUS                                    ErrorStatus;
1066  EFI_STATUS                                    Status;
1067
1068  CurrentChildNode = NULL;
1069  ErrorStatus = EFI_NOT_FOUND;
1070
1071  if (SourceStream->StreamLength == 0) {
1072    return EFI_NOT_FOUND;
1073  }
1074
1075  if (IsListEmpty (&SourceStream->Children) &&
1076                   SourceStream->StreamLength >= sizeof (EFI_COMMON_SECTION_HEADER)) {
1077    //
1078    // This occurs when a section stream exists, but no child sections
1079    // have been parsed out yet.  Therefore, extract the first child and add it
1080    // to the list of children so we can get started.
1081    // Section stream may contain an array of zero or more bytes.
1082    // So, its size should be >= the size of commen section header.
1083    //
1084    Status = CreateChildNode (SourceStream, 0, &CurrentChildNode);
1085    if (EFI_ERROR (Status)) {
1086      return Status;
1087    }
1088  }
1089
1090  //
1091  // At least one child has been parsed out of the section stream.  So, walk
1092  // through the sections that have already been parsed out looking for the
1093  // requested section, if necessary, continue parsing section stream and
1094  // adding children until either the requested section is found, or we run
1095  // out of data
1096  //
1097  CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetFirstNode(&SourceStream->Children));
1098
1099  for (;;) {
1100    ASSERT (CurrentChildNode != NULL);
1101    if (ChildIsType (SourceStream, CurrentChildNode, SearchType, SectionDefinitionGuid)) {
1102      //
1103      // The type matches, so check the instance count to see if it's the one we want
1104      //
1105      (*SectionInstance)--;
1106      if (*SectionInstance == 0) {
1107        //
1108        // Got it!
1109        //
1110        *FoundChild = CurrentChildNode;
1111        *FoundStream = SourceStream;
1112        *AuthenticationStatus = SourceStream->AuthenticationStatus;
1113        return EFI_SUCCESS;
1114      }
1115    }
1116
1117    if (CurrentChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
1118      //
1119      // If the current node is an encapsulating node, recurse into it...
1120      //
1121      Status = FindChildNode (
1122                (FRAMEWORK_SECTION_STREAM_NODE *)CurrentChildNode->EncapsulatedStreamHandle,
1123                SearchType,
1124                SectionInstance,
1125                SectionDefinitionGuid,
1126                &RecursedChildNode,
1127                &RecursedFoundStream,
1128                AuthenticationStatus
1129                );
1130      //
1131      // If the status is not EFI_SUCCESS, just save the error code and continue
1132      // to find the request child node in the rest stream.
1133      //
1134      if (*SectionInstance == 0) {
1135        ASSERT_EFI_ERROR (Status);
1136        *FoundChild = RecursedChildNode;
1137        *FoundStream = RecursedFoundStream;
1138        return EFI_SUCCESS;
1139      } else {
1140        ErrorStatus = Status;
1141      }
1142    } else if ((CurrentChildNode->Type == EFI_SECTION_GUID_DEFINED) && (SearchType != EFI_SECTION_GUID_DEFINED)) {
1143      //
1144      // When Node Type is GUIDED section, but Node has no encapsulated data, Node data should not be parsed
1145      // because a required GUIDED section extraction protocol does not exist.
1146      // If SearchType is not GUIDED section, EFI_PROTOCOL_ERROR should return.
1147      //
1148      ErrorStatus = EFI_PROTOCOL_ERROR;
1149    }
1150
1151    if (!IsNodeAtEnd (&SourceStream->Children, &CurrentChildNode->Link)) {
1152      //
1153      // We haven't found the child node we're interested in yet, but there's
1154      // still more nodes that have already been parsed so get the next one
1155      // and continue searching..
1156      //
1157      CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetNextNode (&SourceStream->Children, &CurrentChildNode->Link));
1158    } else {
1159      //
1160      // We've exhausted children that have already been parsed, so see if
1161      // there's any more data and continue parsing out more children if there
1162      // is.
1163      //
1164      NextChildOffset = CurrentChildNode->OffsetInStream + CurrentChildNode->Size;
1165      //
1166      // Round up to 4 byte boundary
1167      //
1168      NextChildOffset += 3;
1169      NextChildOffset &= ~(UINTN)3;
1170      if (NextChildOffset <= SourceStream->StreamLength - sizeof (EFI_COMMON_SECTION_HEADER)) {
1171        //
1172        // There's an unparsed child remaining in the stream, so create a new child node
1173        //
1174        Status = CreateChildNode (SourceStream, NextChildOffset, &CurrentChildNode);
1175        if (EFI_ERROR (Status)) {
1176          return Status;
1177        }
1178      } else {
1179        ASSERT (EFI_ERROR (ErrorStatus));
1180        return ErrorStatus;
1181      }
1182    }
1183  }
1184}
1185
1186/**
1187  Worker function.  Search stream database for requested stream handle.
1188
1189  @param SearchHandle        Indicates which stream to look for.
1190  @param FoundStream         Output pointer to the found stream.
1191
1192  @retval EFI_SUCCESS        StreamHandle was found and *FoundStream contains
1193                             the stream node.
1194  @retval EFI_NOT_FOUND      SearchHandle was not found in the stream database.
1195
1196**/
1197EFI_STATUS
1198FindStreamNode (
1199  IN  UINTN                                     SearchHandle,
1200  OUT FRAMEWORK_SECTION_STREAM_NODE                  **FoundStream
1201  )
1202{
1203  FRAMEWORK_SECTION_STREAM_NODE                      *StreamNode;
1204
1205  if (!IsListEmpty (&mStreamRoot)) {
1206    StreamNode = STREAM_NODE_FROM_LINK (GetFirstNode (&mStreamRoot));
1207    for (;;) {
1208      if (StreamNode->StreamHandle == SearchHandle) {
1209        *FoundStream = StreamNode;
1210        return EFI_SUCCESS;
1211      } else if (IsNodeAtEnd (&mStreamRoot, &StreamNode->Link)) {
1212        break;
1213      } else {
1214        StreamNode = STREAM_NODE_FROM_LINK (GetNextNode (&mStreamRoot, &StreamNode->Link));
1215      }
1216    }
1217  }
1218
1219  return EFI_NOT_FOUND;
1220}
1221
1222/**
1223  SEP member function.  Retrieves requested section from section stream.
1224
1225  @param This                  Pointer to SEP instance.
1226  @param SectionStreamHandle   The section stream from which to extract the requested
1227                               section.
1228  @param SectionType           A pointer to the type of section to search for.
1229  @param SectionDefinitionGuid If the section type is EFI_SECTION_GUID_DEFINED, then
1230                               SectionDefinitionGuid indicates which of these types
1231                               of sections to search for.
1232  @param SectionInstance       Indicates which instance of the requested section to
1233                               return.
1234  @param Buffer                Double indirection to buffer.  If *Buffer is non-null on
1235                               input, then the buffer is caller allocated.  If
1236                               *Buffer is NULL, then the buffer is callee allocated.
1237                               In either case, the required buffer size is returned
1238                               in *BufferSize.
1239  @param BufferSize            On input, indicates the size of *Buffer if *Buffer is
1240                               non-null on input.  On output, indicates the required
1241                               size (allocated size if callee allocated) of *Buffer.
1242  @param AuthenticationStatus  Indicates the authentication status of the retrieved
1243                               section.
1244
1245
1246  @retval EFI_SUCCESS           Section was retrieved successfully
1247  @retval EFI_PROTOCOL_ERROR    A GUID defined section was encountered in the section
1248                                stream with its EFI_GUIDED_SECTION_PROCESSING_REQUIRED
1249                                bit set, but there was no corresponding GUIDed Section
1250                                Extraction Protocol in the handle database.  *Buffer is
1251                                unmodified.
1252  @retval EFI_NOT_FOUND         An error was encountered when parsing the SectionStream.
1253                                This indicates the SectionStream  is not correctly
1254                                formatted.
1255  @retval EFI_NOT_FOUND         The requested section does not exist.
1256  @retval EFI_OUT_OF_RESOURCES  The system has insufficient resources to process the
1257                                request.
1258  @retval EFI_INVALID_PARAMETER The SectionStreamHandle does not exist.
1259  @retval EFI_WARN_TOO_SMALL    The size of the caller allocated input buffer is
1260                                insufficient to contain the requested section.  The
1261                                input buffer is filled and contents are section contents
1262                                are truncated.
1263
1264**/
1265EFI_STATUS
1266EFIAPI
1267GetSection (
1268  IN EFI_SECTION_EXTRACTION_PROTOCOL                    *This,
1269  IN UINTN                                              SectionStreamHandle,
1270  IN EFI_SECTION_TYPE                                   *SectionType,
1271  IN EFI_GUID                                           *SectionDefinitionGuid,
1272  IN UINTN                                              SectionInstance,
1273  IN VOID                                               **Buffer,
1274  IN OUT UINTN                                          *BufferSize,
1275  OUT UINT32                                            *AuthenticationStatus
1276  )
1277{
1278  FRAMEWORK_SECTION_STREAM_NODE                         *StreamNode;
1279  EFI_TPL                                               OldTpl;
1280  EFI_STATUS                                            Status;
1281  FRAMEWORK_SECTION_CHILD_NODE                          *ChildNode;
1282  FRAMEWORK_SECTION_STREAM_NODE                         *ChildStreamNode;
1283  UINTN                                                 CopySize;
1284  UINT32                                                ExtractedAuthenticationStatus;
1285  UINTN                                                 Instance;
1286  UINT8                                                 *CopyBuffer;
1287  UINTN                                                 SectionSize;
1288  EFI_COMMON_SECTION_HEADER                             *Section;
1289
1290
1291  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
1292  Instance = SectionInstance + 1;
1293  ChildStreamNode = NULL;
1294
1295  //
1296  // Locate target stream
1297  //
1298  Status = FindStreamNode (SectionStreamHandle, &StreamNode);
1299  if (EFI_ERROR (Status)) {
1300    Status = EFI_INVALID_PARAMETER;
1301    goto GetSection_Done;
1302  }
1303
1304  //
1305  // Found the stream, now locate and return the appropriate section
1306  //
1307  if (SectionType == NULL) {
1308    //
1309    // SectionType == NULL means return the WHOLE section stream...
1310    //
1311    CopySize = StreamNode->StreamLength;
1312    CopyBuffer = StreamNode->StreamBuffer;
1313    *AuthenticationStatus = StreamNode->AuthenticationStatus;
1314  } else {
1315    //
1316    // There's a requested section type, so go find it and return it...
1317    //
1318    Status = FindChildNode (
1319                      StreamNode,
1320                      *SectionType,
1321                      &Instance,
1322                      SectionDefinitionGuid,
1323                      &ChildNode,
1324                      &ChildStreamNode,
1325                      &ExtractedAuthenticationStatus
1326                      );
1327    if (EFI_ERROR (Status)) {
1328      goto GetSection_Done;
1329    }
1330    ASSERT (ChildNode != NULL);
1331    ASSERT (ChildStreamNode != NULL);
1332    Section = (EFI_COMMON_SECTION_HEADER *) (ChildStreamNode->StreamBuffer + ChildNode->OffsetInStream);
1333
1334    if (IS_SECTION2 (Section)) {
1335      CopySize = SECTION2_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER2);
1336      CopyBuffer = (UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2);
1337    } else {
1338      CopySize = SECTION_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER);
1339      CopyBuffer = (UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER);
1340    }
1341    *AuthenticationStatus = ExtractedAuthenticationStatus;
1342  }
1343
1344  SectionSize = CopySize;
1345  if (*Buffer != NULL) {
1346    //
1347    // Caller allocated buffer.  Fill to size and return required size...
1348    //
1349    if (*BufferSize < CopySize) {
1350      Status = EFI_WARN_BUFFER_TOO_SMALL;
1351      CopySize = *BufferSize;
1352    }
1353  } else {
1354    //
1355    // Callee allocated buffer.  Allocate buffer and return size.
1356    //
1357    *Buffer = AllocatePool (CopySize);
1358    if (*Buffer == NULL) {
1359      Status = EFI_OUT_OF_RESOURCES;
1360      goto GetSection_Done;
1361    }
1362  }
1363  CopyMem (*Buffer, CopyBuffer, CopySize);
1364  *BufferSize = SectionSize;
1365
1366GetSection_Done:
1367  gBS->RestoreTPL (OldTpl);
1368  return Status;
1369}
1370
1371/**
1372  Worker function.  Destructor for child nodes.
1373
1374  @param ChildNode           Indicates the node to destroy
1375
1376**/
1377VOID
1378FreeChildNode (
1379  IN  FRAMEWORK_SECTION_CHILD_NODE                   *ChildNode
1380  )
1381{
1382  ASSERT (ChildNode->Signature == FRAMEWORK_SECTION_CHILD_SIGNATURE);
1383  //
1384  // Remove the child from it's list
1385  //
1386  RemoveEntryList (&ChildNode->Link);
1387
1388  if (ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
1389    //
1390    // If it's an encapsulating section, we close the resulting section stream.
1391    // CloseSectionStream will free all memory associated with the stream.
1392    //
1393    CloseSectionStream (&mSectionExtraction, ChildNode->EncapsulatedStreamHandle);
1394  }
1395
1396  if (ChildNode->Event != NULL) {
1397    gBS->CloseEvent (ChildNode->Event);
1398  }
1399
1400  //
1401  // Last, free the child node itself
1402  //
1403  FreePool (ChildNode);
1404}
1405
1406/**
1407  SEP member function.  Deletes an existing section stream
1408
1409  @param This                   Indicates the calling context.
1410  @param StreamHandleToClose    Indicates the stream to close
1411
1412  @retval EFI_SUCCESS           Section stream was closed successfully.
1413  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
1414  @retval EFI_INVALID_PARAMETER Section stream does not end concident with end of
1415                                last section.
1416
1417**/
1418EFI_STATUS
1419EFIAPI
1420CloseSectionStream (
1421  IN  EFI_SECTION_EXTRACTION_PROTOCOL           *This,
1422  IN  UINTN                                     StreamHandleToClose
1423  )
1424{
1425  FRAMEWORK_SECTION_STREAM_NODE                      *StreamNode;
1426  EFI_TPL                                       OldTpl;
1427  EFI_STATUS                                    Status;
1428  LIST_ENTRY                                    *Link;
1429  FRAMEWORK_SECTION_CHILD_NODE                       *ChildNode;
1430
1431  OldTpl = gBS->RaiseTPL (TPL_NOTIFY);
1432
1433  //
1434  // Locate target stream
1435  //
1436  Status = FindStreamNode (StreamHandleToClose, &StreamNode);
1437  if (!EFI_ERROR (Status)) {
1438    //
1439    // Found the stream, so close it
1440    //
1441    RemoveEntryList (&StreamNode->Link);
1442    while (!IsListEmpty (&StreamNode->Children)) {
1443      Link = GetFirstNode (&StreamNode->Children);
1444      ChildNode = CHILD_SECTION_NODE_FROM_LINK (Link);
1445      FreeChildNode (ChildNode);
1446    }
1447    FreePool (StreamNode->StreamBuffer);
1448    FreePool (StreamNode);
1449    Status = EFI_SUCCESS;
1450  } else {
1451    Status = EFI_INVALID_PARAMETER;
1452  }
1453
1454  gBS->RestoreTPL (OldTpl);
1455  return Status;
1456}
1457