1 /** @file
2 
3   Implement the Firmware Volume Block (FVB) services based on SMM FVB
4   module and install FVB protocol.
5 
6 Copyright (c) 2010  - 2014, Intel Corporation. All rights reserved. <BR>
7 
8 
9   This program and the accompanying materials are licensed and made available under
10 
11   the terms and conditions of the BSD License that accompanies this distribution.
12 
13   The full text of the license may be found at
14 
15   http://opensource.org/licenses/bsd-license.php.
16 
17 
18 
19   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
20 
21   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 
23 
24 
25 
26 **/
27 
28 #include "FvbSmmDxe.h"
29 
30 EFI_HANDLE                       mHandle           = NULL;
31 EFI_SMM_COMMUNICATION_PROTOCOL  *mSmmCommunication = NULL;
32 
33 //
34 // Template structure used when installing FVB protocol.
35 //
36 EFI_FVB_DEVICE    mFvbDeviceTemplate = {
37   FVB_DEVICE_SIGNATURE,
38   NULL,
39   {
40     FvbGetAttributes,
41     FvbSetAttributes,
42     FvbGetPhysicalAddress,
43     FvbGetBlockSize,
44     FvbRead,
45     FvbWrite,
46     FvbEraseBlocks,
47     NULL
48   },
49   NULL
50 };
51 
52 FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = {
53   {
54     {
55       HARDWARE_DEVICE_PATH,
56       HW_MEMMAP_DP,
57       {
58         (UINT8)(sizeof (MEMMAP_DEVICE_PATH)),
59         (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8)
60       }
61     },
62     EfiMemoryMappedIO,
63     (EFI_PHYSICAL_ADDRESS) 0,
64     (EFI_PHYSICAL_ADDRESS) 0,
65   },
66   {
67     END_DEVICE_PATH_TYPE,
68     END_ENTIRE_DEVICE_PATH_SUBTYPE,
69     {
70       END_DEVICE_PATH_LENGTH,
71       0
72     }
73   }
74 };
75 
76 FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = {
77   {
78     {
79       MEDIA_DEVICE_PATH,
80       MEDIA_PIWG_FW_VOL_DP,
81       {
82         (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)),
83         (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8)
84       }
85     },
86     { 0 }
87   },
88   {
89     END_DEVICE_PATH_TYPE,
90     END_ENTIRE_DEVICE_PATH_SUBTYPE,
91     {
92       END_DEVICE_PATH_LENGTH,
93       0
94     }
95   }
96 };
97 
98 /**
99   Initialize the communicate buffer using DataSize and Function.
100 
101   The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
102   DataSize.
103 
104   @param[out]      CommunicateBuffer The communicate buffer. Caller should free it after use.
InitCommunicateBuffer(OUT VOID ** CommunicateBuffer,OUT VOID ** DataPtr,IN UINTN DataSize,IN UINTN Function)105   @param[out]      DataPtr           Points to the data in the communicate buffer. Caller should not free it.
106   @param[in]       DataSize          The payload size.
107   @param[in]       Function          The function number used to initialize the communicate header.
108 
109   @retval EFI_INVALID_PARAMETER      The data size is too big.
110   @retval EFI_SUCCESS                Find the specified variable.
111 
112 **/
113 EFI_STATUS
114 InitCommunicateBuffer (
115   OUT     VOID                              **CommunicateBuffer,
116   OUT     VOID                              **DataPtr,
117   IN      UINTN                             DataSize,
118   IN      UINTN                             Function
119   )
120 {
121   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
122   SMM_FVB_COMMUNICATE_FUNCTION_HEADER       *SmmFvbFunctionHeader;
123 
124   //
125   // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE + DataSize.
126   //
127   SmmCommunicateHeader = AllocatePool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE);
128   ASSERT (SmmCommunicateHeader != NULL);
129 
130   //
131   // Prepare data buffer.
132   //
133   CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFirmwareVolumeBlockProtocolGuid);
134   SmmCommunicateHeader->MessageLength = DataSize + SMM_FVB_COMMUNICATE_HEADER_SIZE;
135 
136   SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
137   SmmFvbFunctionHeader->Function = Function;
138 
139   *CommunicateBuffer = SmmCommunicateHeader;
140   *DataPtr = SmmFvbFunctionHeader->Data;
141 
142   return EFI_SUCCESS;
143 }
144 
SendCommunicateBuffer(IN EFI_SMM_COMMUNICATE_HEADER * SmmCommunicateHeader,IN UINTN DataSize)145 
146 /**
147   Send the data in communicate buffer to SMM.
148 
149   @param[out]      SmmCommunicateHeader    The communicate buffer.
150   @param[in]       DataSize                The payload size.
151 
152 **/
153 EFI_STATUS
154 SendCommunicateBuffer (
155   IN      EFI_SMM_COMMUNICATE_HEADER        *SmmCommunicateHeader,
156   IN      UINTN                             DataSize
157   )
158 {
159   EFI_STATUS                                Status;
160   UINTN                                     CommSize;
161   SMM_FVB_COMMUNICATE_FUNCTION_HEADER       *SmmFvbFunctionHeader;
162 
163   CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FVB_COMMUNICATE_HEADER_SIZE;
164   Status = mSmmCommunication->Communicate (
165                                 mSmmCommunication,
166                                 SmmCommunicateHeader,
167                                 &CommSize
168                                 );
169   ASSERT_EFI_ERROR (Status);
170 
171   SmmFvbFunctionHeader = (SMM_FVB_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
172   return  SmmFvbFunctionHeader->ReturnStatus;
173 }
174 
175 /**
176   This function retrieves the attributes and current settings of the block.
177 
178   @param[in]  This       Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
179 
FvbGetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,OUT EFI_FVB_ATTRIBUTES_2 * Attributes)180   @param[out] Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the attributes
181                          and current settings are returned. Type EFI_FVB_ATTRIBUTES_2
182                          is defined in EFI_FIRMWARE_VOLUME_HEADER.
183 
184   @retval EFI_SUCCESS              The firmware volume attributes were returned.
185   @retval EFI_INVALID_PARAMETER    Attributes is NULL.
186 **/
187 EFI_STATUS
188 EFIAPI
189 FvbGetAttributes (
190   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
191      OUT   EFI_FVB_ATTRIBUTES_2                 *Attributes
192   )
193 {
194   EFI_STATUS                                    Status;
195   UINTN                                         PayloadSize;
196   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
197   SMM_FVB_ATTRIBUTES_HEADER                     *SmmFvbAttributesHeader;
198   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
199   EFI_FVB_DEVICE                                *FvbDevice;
200 
201   if (Attributes == NULL) {
202     return EFI_INVALID_PARAMETER;
203   }
204 
205   FvbDevice = FVB_DEVICE_FROM_THIS (This);
206   SmmFvb    = FvbDevice->SmmFvbInstance;
207 
208   //
209   // Initialize the communicate buffer.
210   //
211   PayloadSize  = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
212   Status = InitCommunicateBuffer (
213              (VOID **)&SmmCommunicateHeader,
214              (VOID **)&SmmFvbAttributesHeader,
215              PayloadSize,
216              EFI_FUNCTION_GET_ATTRIBUTES
217              );
218   if (EFI_ERROR (Status)) {
219     return Status;
220   }
221 
222   SmmFvbAttributesHeader->SmmFvb     = SmmFvb;
223   SmmFvbAttributesHeader->Attributes = 0;
224 
225   //
226   // Send data to SMM.
227   //
228   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
229 
230   //
231   // Get data from SMM.
232   //
233   *Attributes = SmmFvbAttributesHeader->Attributes;
234   FreePool (SmmCommunicateHeader);
235 
236   return Status;
237 }
238 
239 
240 /**
241   Sets Volume attributes. No polarity translations are done.
242 
FvbSetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,IN OUT EFI_FVB_ATTRIBUTES_2 * Attributes)243   @param[in]  This        Calling context.
244   @param[out] Attributes  Output buffer which contains attributes.
245 
246   @retval     EFI_SUCCESS              Set the Attributes successfully.
247   @retval     EFI_INVALID_PARAMETER    Attributes is NULL.
248 
249 **/
250 EFI_STATUS
251 EFIAPI
252 FvbSetAttributes (
253   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
254   IN OUT   EFI_FVB_ATTRIBUTES_2                 *Attributes
255   )
256 {
257   EFI_STATUS                                    Status;
258   UINTN                                         PayloadSize;
259   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
260   SMM_FVB_ATTRIBUTES_HEADER                     *SmmFvbAttributesHeader;
261   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
262   EFI_FVB_DEVICE                                *FvbDevice;
263 
264   if (Attributes == NULL) {
265     return EFI_INVALID_PARAMETER;
266   }
267 
268   FvbDevice = FVB_DEVICE_FROM_THIS (This);
269   SmmFvb    = FvbDevice->SmmFvbInstance;
270 
271   //
272   // Initialize the communicate buffer.
273   //
274   PayloadSize  = sizeof (SMM_FVB_ATTRIBUTES_HEADER);
275   Status = InitCommunicateBuffer (
276              (VOID **)&SmmCommunicateHeader,
277              (VOID **)&SmmFvbAttributesHeader,
278              PayloadSize,
279              EFI_FUNCTION_SET_ATTRIBUTES
280              );
281   if (EFI_ERROR (Status)) {
282     return Status;
283   }
284 
285   SmmFvbAttributesHeader->SmmFvb     = SmmFvb;
286   SmmFvbAttributesHeader->Attributes = *Attributes;
287 
288   //
289   // Send data to SMM.
290   //
291   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
292 
293   //
294   // Get data from SMM.
295   //
296   *Attributes = SmmFvbAttributesHeader->Attributes;
297   FreePool (SmmCommunicateHeader);
298 
299   return Status;
300 }
301 
302 
303 /**
304   Retrieves the physical address of the FVB instance.
GetPhysicalAddress(IN EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL * SmmFvb,OUT EFI_PHYSICAL_ADDRESS * Address)305 
306   @param[in]  SmmFvb         A pointer to EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
307   @param[out] Address        Output buffer containing the address.
308 
309   @retval     EFI_SUCCESS    Get the address successfully.
310   @retval     Others         Failed to get address.
311 
312 **/
313 EFI_STATUS
314 GetPhysicalAddress (
315   IN   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *SmmFvb,
316   OUT  EFI_PHYSICAL_ADDRESS                    *Address
317   )
318 {
319   EFI_STATUS                                   Status;
320   UINTN                                        PayloadSize;
321   EFI_SMM_COMMUNICATE_HEADER                   *SmmCommunicateHeader;
322   SMM_FVB_PHYSICAL_ADDRESS_HEADER              *SmmFvbPhysicalAddressHeader;
323 
324   //
325   // Initialize the communicate buffer.
326   //
327   PayloadSize  = sizeof (SMM_FVB_PHYSICAL_ADDRESS_HEADER);
328   Status = InitCommunicateBuffer (
329              (VOID **)&SmmCommunicateHeader,
330              (VOID **)&SmmFvbPhysicalAddressHeader,
331              PayloadSize,
332              EFI_FUNCTION_GET_PHYSICAL_ADDRESS
333              );
334   if (EFI_ERROR (Status)) {
335     return Status;
336   }
337 
338   SmmFvbPhysicalAddressHeader->SmmFvb  = SmmFvb;
339   SmmFvbPhysicalAddressHeader->Address = 0;
340 
341   //
342   // Send data to SMM.
343   //
344   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
345 
346   //
347   // Get data from SMM.
348   //
349   *Address = SmmFvbPhysicalAddressHeader->Address;
350   FreePool (SmmCommunicateHeader);
351 
352   return Status;
353 }
354 
355 
356 /**
357   Retrieves the physical address of the FVB instance.
358 
FvbGetPhysicalAddress(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,OUT EFI_PHYSICAL_ADDRESS * Address)359   @param[in]  This                     A pointer to EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL.
360   @param[out] Address                  Output buffer containing the address.
361 
362   @retval     EFI_SUCCESS              Get the address successfully.
363   @retval     Others                   Failed to get the address.
364 
365 **/
366 EFI_STATUS
367 EFIAPI
368 FvbGetPhysicalAddress (
369   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
370      OUT   EFI_PHYSICAL_ADDRESS                *Address
371   )
372 {
373   EFI_STATUS                                   Status;
374   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL       *SmmFvb;
375   EFI_FVB_DEVICE                               *FvbDevice;
376 
377   if (Address == NULL) {
378     return EFI_INVALID_PARAMETER;
379   }
380 
381   FvbDevice = FVB_DEVICE_FROM_THIS (This);
382   SmmFvb    = FvbDevice->SmmFvbInstance;
383 
384   Status = GetPhysicalAddress (SmmFvb, Address);
385 
386   return Status;
387 }
388 
389 
390 /**
391   Retrieve the size of a logical block.
392 
393   @param[in]  This        Calling context.
394   @param[in]  Lba         Indicates which block to return the size for.
395   @param[out] BlockSize   A pointer to a caller allocated UINTN in which
396                           the size of the block is returned.
397   @param[out] NumOfBlocks A pointer to a caller allocated UINTN in which the
FvbGetBlockSize(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,IN EFI_LBA Lba,OUT UINTN * BlockSize,OUT UINTN * NumOfBlocks)398                           number of consecutive blocks starting with Lba is
399                           returned. All blocks in this range have a size of
400                           BlockSize.
401 
402   @retval     EFI_SUCCESS              Get BlockSize and NumOfBlocks successfully.
403   @retval     EFI_INVALID_PARAMETER    BlockSize or NumOfBlocks are NULL.
404 **/
405 EFI_STATUS
406 EFIAPI
407 FvbGetBlockSize (
408   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *This,
409   IN       EFI_LBA                             Lba,
410      OUT   UINTN                               *BlockSize,
411      OUT   UINTN                               *NumOfBlocks
412   )
413 {
414   EFI_STATUS                                   Status;
415   UINTN                                        PayloadSize;
416   EFI_SMM_COMMUNICATE_HEADER                   *SmmCommunicateHeader;
417   SMM_FVB_BLOCK_SIZE_HEADER                    *SmmFvbBlockSizeHeader;
418   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL       *SmmFvb;
419   EFI_FVB_DEVICE                               *FvbDevice;
420 
421   if ((BlockSize == NULL) || (NumOfBlocks == NULL)) {
422     return EFI_INVALID_PARAMETER;
423   }
424 
425   FvbDevice = FVB_DEVICE_FROM_THIS (This);
426   SmmFvb    = FvbDevice->SmmFvbInstance;
427 
428   //
429   // Initialize the communicate buffer.
430   //
431   PayloadSize  = sizeof (SMM_FVB_BLOCK_SIZE_HEADER);
432   Status = InitCommunicateBuffer (
433              (VOID **)&SmmCommunicateHeader,
434              (VOID **)&SmmFvbBlockSizeHeader,
435              PayloadSize,
436              EFI_FUNCTION_GET_BLOCK_SIZE
437              );
438   if (EFI_ERROR (Status)) {
439     return Status;
440   }
441 
442   SmmFvbBlockSizeHeader->SmmFvb = SmmFvb;
443   SmmFvbBlockSizeHeader->Lba    = Lba;
444 
445   //
446   // Send data to SMM.
447   //
448   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
449 
450   //
451   // Get data from SMM.
452   //
453   *BlockSize   = SmmFvbBlockSizeHeader->BlockSize;
454   *NumOfBlocks = SmmFvbBlockSizeHeader->NumOfBlocks;
455   FreePool (SmmCommunicateHeader);
456 
457   return Status;
458 }
459 
460 
461 /**
462   Reads data beginning at Lba:Offset from FV. The Read terminates either
463   when *NumBytes of data have been read, or when a block boundary is
464   reached.  *NumBytes is updated to reflect the actual number of bytes
465   written. The write opertion does not include erase. This routine will
466   attempt to write only the specified bytes. If the writes do not stick,
467   it will return an error.
468 
469   @param[in]      This           Calling context
470   @param[in]      Lba            Block in which to begin write
471   @param[in]      Offset         Offset in the block at which to begin write
472   @param[in,out]  NumBytes       On input, indicates the requested write size. On
473                                  output, indicates the actual number of bytes written
474   @param[in]      Buffer         Buffer containing source data for the write.
475 
476   @retval EFI_SUCCESS            The firmware volume was read successfully and
477                                  contents are in Buffer.
478   @retval EFI_BAD_BUFFER_SIZE    Read attempted across a LBA boundary. On output,
479                                  NumBytes contains the total number of bytes returned
FvbRead(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,OUT UINT8 * Buffer)480                                  in Buffer.
481   @retval EFI_ACCESS_DENIED      The firmware volume is in the ReadDisabled state
482   @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and
483                                  could not be read.
484   @retval EFI_INVALID_PARAMETER  NumBytes or Buffer are NULL.
485 
486 **/
487 EFI_STATUS
488 EFIAPI
489 FvbRead (
490   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
491   IN       EFI_LBA                              Lba,
492   IN       UINTN                                Offset,
493   IN OUT   UINTN                                *NumBytes,
494      OUT   UINT8                                *Buffer
495   )
496 {
497   EFI_STATUS                                    Status;
498   UINTN                                         PayloadSize;
499   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
500   SMM_FVB_READ_WRITE_HEADER                     *SmmFvbReadWriteHeader;
501   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
502   EFI_FVB_DEVICE                                *FvbDevice;
503 
504   if ((NumBytes == NULL) || (Buffer == NULL)) {
505     return EFI_INVALID_PARAMETER;
506   }
507 
508   FvbDevice = FVB_DEVICE_FROM_THIS (This);
509   SmmFvb    = FvbDevice->SmmFvbInstance;
510 
511   //
512   // Initialize the communicate buffer.
513   //
514   PayloadSize  = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
515   Status = InitCommunicateBuffer (
516              (VOID **)&SmmCommunicateHeader,
517              (VOID **)&SmmFvbReadWriteHeader,
518              PayloadSize, EFI_FUNCTION_READ
519              );
520   if (EFI_ERROR (Status)) {
521     return Status;
522   }
523 
524   SmmFvbReadWriteHeader->SmmFvb   = SmmFvb;
525   SmmFvbReadWriteHeader->Lba      = Lba;
526   SmmFvbReadWriteHeader->Offset   = Offset;
527   SmmFvbReadWriteHeader->NumBytes = *NumBytes;
528 
529   //
530   // Send data to SMM.
531   //
532   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
533 
534   //
535   // Get data from SMM.
536   //
537   *NumBytes = SmmFvbReadWriteHeader->NumBytes;
538   if (!EFI_ERROR (Status)) {
539     CopyMem (Buffer, (UINT8 *)(SmmFvbReadWriteHeader + 1), *NumBytes);
540   }
541   FreePool (SmmCommunicateHeader);
542 
543   return Status;
544 }
545 
546 
547 /**
548   Writes data beginning at Lba:Offset from FV. The write terminates either
549   when *NumBytes of data have been written, or when a block boundary is
550   reached.  *NumBytes is updated to reflect the actual number of bytes
551   written. The write opertion does not include erase. This routine will
552   attempt to write only the specified bytes. If the writes do not stick,
553   it will return an error.
554 
555   @param[in]      This           Calling context.
556   @param[in]      Lba            Block in which to begin write.
557   @param[in]      Offset         Offset in the block at which to begin write.
558   @param[in,out]  NumBytes       On input, indicates the requested write size. On
559                                  output, indicates the actual number of bytes written.
560   @param[in]      Buffer         Buffer containing source data for the write.
561 
562   @retval EFI_SUCCESS            The firmware volume was written successfully.
563   @retval EFI_BAD_BUFFER_SIZE    Write attempted across a LBA boundary. On output,
564                                  NumBytes contains the total number of bytes
FvbWrite(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)565                                  actually written.
566   @retval EFI_ACCESS_DENIED      The firmware volume is in the WriteDisabled state.
567   @retval EFI_DEVICE_ERROR       The block device is not functioning correctly and
568                                  could not be written.
569   @retval EFI_INVALID_PARAMETER  NumBytes or Buffer are NULL.
570 
571 **/
572 EFI_STATUS
573 EFIAPI
574 FvbWrite (
575   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL   *This,
576   IN       EFI_LBA                              Lba,
577   IN       UINTN                                Offset,
578   IN OUT   UINTN                                *NumBytes,
579   IN       UINT8                                *Buffer
580   )
581 {
582   EFI_STATUS                                    Status;
583   UINTN                                         PayloadSize;
584   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
585   SMM_FVB_READ_WRITE_HEADER                     *SmmFvbReadWriteHeader;
586   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
587   EFI_FVB_DEVICE                                *FvbDevice;
588 
589   if ((NumBytes == NULL) || (Buffer == NULL)) {
590     return EFI_INVALID_PARAMETER;
591   }
592 
593   FvbDevice = FVB_DEVICE_FROM_THIS (This);
594   SmmFvb    = FvbDevice->SmmFvbInstance;
595 
596   //
597   // Initialize the communicate buffer.
598   //
599   PayloadSize  = sizeof (SMM_FVB_READ_WRITE_HEADER) + *NumBytes;
600   Status = InitCommunicateBuffer (
601              (VOID **)&SmmCommunicateHeader,
602              (VOID **)&SmmFvbReadWriteHeader,
603              PayloadSize,
604              EFI_FUNCTION_WRITE
605              );
606   if (EFI_ERROR (Status)) {
607     return Status;
608   }
609 
610   SmmFvbReadWriteHeader->SmmFvb   = SmmFvb;
611   SmmFvbReadWriteHeader->Lba      = Lba;
612   SmmFvbReadWriteHeader->Offset   = Offset;
613   SmmFvbReadWriteHeader->NumBytes = *NumBytes;
614   CopyMem ((UINT8 *)(SmmFvbReadWriteHeader + 1), Buffer, *NumBytes);
615 
616   //
617   // Send data to SMM.
618   //
619   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
620 
621   //
622   // Get data from SMM.
623   //
624   *NumBytes = SmmFvbReadWriteHeader->NumBytes;
625   FreePool (SmmCommunicateHeader);
626 
627   return Status;
628 }
629 
630 
631 /**
632   The EraseBlock() function erases NumOfLba blocks started from StartingLba.
633 
634   @param[in] This            Calling context.
635   @param[in] StartingLba     Starting LBA followed to erase.
636   @param[in] NumOfLba        Number of block to erase.
EraseBlock(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,IN EFI_LBA StartingLba,IN UINTN NumOfLba)637 
638   @retval EFI_SUCCESS        The erase request was successfully completed.
639   @retval EFI_ACCESS_DENIED  The firmware volume is in the WriteDisabled state.
640   @retval EFI_DEVICE_ERROR   The block device is not functioning correctly and
641                              could not be written. Firmware device may have been
642                              partially erased.
643 
644 **/
645 EFI_STATUS
646 EraseBlock (
647   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
648   IN       EFI_LBA                               StartingLba,
649   IN       UINTN                                 NumOfLba
650   )
651 {
652   EFI_STATUS                                     Status;
653   UINTN                                          PayloadSize;
654   EFI_SMM_COMMUNICATE_HEADER                    *SmmCommunicateHeader;
655   SMM_FVB_BLOCKS_HEADER                         *SmmFvbBlocksHeader;
656   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
657   EFI_FVB_DEVICE                                *FvbDevice;
658 
659   FvbDevice = FVB_DEVICE_FROM_THIS (This);
660   SmmFvb    = FvbDevice->SmmFvbInstance;
661 
662   //
663   // Initialize the communicate buffer.
664   //
665   PayloadSize  = sizeof (SMM_FVB_BLOCKS_HEADER);
666   Status = InitCommunicateBuffer (
667              (VOID **)&SmmCommunicateHeader,
668              (VOID **)&SmmFvbBlocksHeader,
669              PayloadSize,
670              EFI_FUNCTION_ERASE_BLOCKS
671              );
672   if (EFI_ERROR (Status)) {
673     return Status;
674   }
675 
676   SmmFvbBlocksHeader->SmmFvb   = SmmFvb;
677   SmmFvbBlocksHeader->StartLba = StartingLba;
678   SmmFvbBlocksHeader->NumOfLba = NumOfLba;
679 
680   //
681   // Send data to SMM.
682   //
683   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
684 
685   //
686   // Get data from SMM.
687   //
688   FreePool (SmmCommunicateHeader);
689 
690   return Status;
691 }
692 
693 
694 /**
695   The EraseBlocks() function erases one or more blocks as denoted by the
696   variable argument list. The entire parameter list of blocks must be verified
697   prior to erasing any blocks.  If a block is requested that does not exist
698   within the associated firmware volume (it has a larger index than the last
699   block of the firmware volume), the EraseBlock() function must return
700   EFI_INVALID_PARAMETER without modifying the contents of the firmware volume.
701 
702   @param[in] This           Calling context/
703   @param[in] ...            Starting LBA followed by Number of Lba to erase.
704                             a -1 to terminate the list.
705 /
FvbEraseBlocks(IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL * This,...)706   @retval EFI_SUCCESS       The erase request was successfully completed
707   @retval EFI_ACCESS_DENIED The firmware volume is in the WriteDisabled state/
708   @retval EFI_DEVICE_ERROR  The block device is not functioning correctly and
709                             could not be written. Firmware device may have been
710                             partially erased/
711 
712 **/
713 EFI_STATUS
714 EFIAPI
715 FvbEraseBlocks (
716   IN CONST EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL    *This,
717   ...
718   )
719 {
720   EFI_STATUS                                     Status;
721   VA_LIST                                        Marker;
722   EFI_LBA                                        StartingLba;
723   UINTN                                          NumOfLba;
724 
725   Status = EFI_SUCCESS;
726 
727   //
728   // Check the parameter.
729   //
730   VA_START (Marker, This);
731   do {
732     StartingLba = VA_ARG (Marker, EFI_LBA);
733     if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
734       break;
735     }
736 
737     NumOfLba = VA_ARG (Marker, UINT32);
738     if (NumOfLba == 0) {
739       return EFI_INVALID_PARAMETER;
740     }
741 
742   } while ( 1 );
743   VA_END (Marker);
744 
745   //
746   // Erase the blocks.
747   //
748   VA_START (Marker, This);
749   do {
750     StartingLba = VA_ARG (Marker, EFI_LBA);
751     if (StartingLba == EFI_LBA_LIST_TERMINATOR ) {
752       break;
753     }
754     NumOfLba = VA_ARG (Marker, UINT32);
755     Status = EraseBlock (This, StartingLba, NumOfLba);
756     if (EFI_ERROR (Status)) {
757       break;
758     }
759   } while ( 1 );
760   VA_END (Marker);
761 
762   return Status;
763 }
InstallFvb(IN EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL * SmmFvb)764 
765 
766 /**
767   Install the FVB protocol which based on SMM FVB protocol.
768 
769   @param[in] SmmFvb        The SMM FVB protocol.
770 
771 **/
772 VOID
773 InstallFvb (
774   IN    EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *SmmFvb
775   )
776 {
777   EFI_STATUS                                    Status;
778   EFI_HANDLE                                    FvbHandle;
779   EFI_FVB_DEVICE                                *FvbDevice;
780   EFI_FIRMWARE_VOLUME_HEADER                    *VolumeHeader;
781   EFI_PHYSICAL_ADDRESS                          Address;
782   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL            *OldFvbInterface;
783 
784   FvbDevice = AllocateRuntimeCopyPool (sizeof (EFI_FVB_DEVICE), &mFvbDeviceTemplate);
785   ASSERT (FvbDevice != NULL);
786   FvbDevice->SmmFvbInstance = SmmFvb;
787 
788   Status = gBS->LocateProtocol (
789                   &gEfiSmmCommunicationProtocolGuid,
790                   NULL,
791                   (VOID **) &mSmmCommunication
792                   );
793   ASSERT_EFI_ERROR (Status);
794 
795   Status = GetPhysicalAddress (SmmFvb, &Address);
796   ASSERT_EFI_ERROR (Status);
797 
798   VolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN)Address;
799 
800   //
801   // Set up the devicepath.
802   //
803   if (VolumeHeader->ExtHeaderOffset == 0) {
804     //
805     // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH.
806     //
807     FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate);
808     ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.StartingAddress = (UINTN)Address;
809     ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.EndingAddress   = (UINTN)Address + VolumeHeader->FvLength - 1;
810   } else {
811     FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateRuntimeCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate);
812     CopyGuid (
813       &((FV_PIWG_DEVICE_PATH *)FvbDevice->DevicePath)->FvDevPath.FvName,
814       (GUID *)(UINTN)((UINTN)Address + VolumeHeader->ExtHeaderOffset)
815       );
816   }
817 
818   //
819   // Find a handle with a matching device path that has supports FW Block protocol.
820   //
821   Status = gBS->LocateDevicePath (
822                   &gEfiFirmwareVolumeBlockProtocolGuid,
823                   &FvbDevice->DevicePath,
824                   &FvbHandle
825                   );
826   if (EFI_ERROR (Status) ) {
827     //
828     // LocateDevicePath fails so install a new interface and device path.
829     //
830     FvbHandle = NULL;
831     Status =  gBS->InstallMultipleProtocolInterfaces (
832                      &FvbHandle,
833                      &gEfiFirmwareVolumeBlockProtocolGuid,
834                      &FvbDevice->FvbInstance,
835                      &gEfiDevicePathProtocolGuid,
836                      FvbDevice->DevicePath,
837                      NULL
838                      );
839     ASSERT_EFI_ERROR (Status);
840   } else if (IsDevicePathEnd (FvbDevice->DevicePath)) {
841     //
842     // Device allready exists, so reinstall the FVB protocol.
843     //
844     Status = gBS->HandleProtocol (
845                     FvbHandle,
846                     &gEfiFirmwareVolumeBlockProtocolGuid,
847                     (VOID **) &OldFvbInterface
848                     );
849     ASSERT_EFI_ERROR (Status);
850 
851     Status =  gBS->ReinstallProtocolInterface (
852                      FvbHandle,
853                      &gEfiFirmwareVolumeBlockProtocolGuid,
854                      OldFvbInterface,
855                      &FvbDevice->FvbInstance
856                      );
857     ASSERT_EFI_ERROR (Status);
858   } else {
859     //
860     // There was a FVB protocol on an End Device Path node.
861     //
862     ASSERT (FALSE);
863   }
864 }
865 
866 
867 /**
SmmFvbReady(IN EFI_EVENT Event,IN VOID * Context)868   SMM Firmware Volume Block Protocol notification event handler.
869 
870   Discover NV Variable Store and install Variable Write Arch Protocol.
871 
872   @param[in] Event    Event whose notification function is being invoked.
873   @param[in] Context  Pointer to the notification function's context.
874 **/
875 VOID
876 EFIAPI
877 SmmFvbReady (
878   IN  EFI_EVENT                                 Event,
879   IN  VOID                                      *Context
880   )
881 {
882   EFI_STATUS                                    Status;
883   EFI_HANDLE                                    *HandleBuffer;
884   UINTN                                         HandleCount;
885   UINTN                                         Index;
886   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *SmmFvb;
887 
888   //
889   // Locate all handles of Smm Fvb protocol.
890   //
891   Status = gBS->LocateHandleBuffer (
892                   ByProtocol,
893                   &gEfiSmmFirmwareVolumeBlockProtocolGuid,
894                   NULL,
895                   &HandleCount,
896                   &HandleBuffer
897                   );
898   if (EFI_ERROR (Status)) {
899     return ;
900   }
901 
902   //
903   // Install FVB protocol.
904   //
905   for (Index = 0; Index < HandleCount; Index++) {
906     SmmFvb = NULL;
907     Status = gBS->HandleProtocol (
908                     HandleBuffer[Index],
909                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
910                     (VOID **) &SmmFvb
911                     );
912     if (EFI_ERROR (Status)) {
913       break;
914     }
915 
916     InstallFvb (SmmFvb);
917   }
918 
919   FreePool (HandleBuffer);
920 }
921 
922 
923 /**
924   The driver entry point for Firmware Volume Block Driver.
925 
926   The function does the necessary initialization work
927   Firmware Volume Block Driver.
928 
FvbSmmDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)929   @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
930   @param[in]  SystemTable       A pointer to the EFI system table.
931 
932   @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
933                                 It will ASSERT on errors.
934 
935 **/
936 EFI_STATUS
937 EFIAPI
938 FvbSmmDxeInitialize (
939   IN EFI_HANDLE                                 ImageHandle,
940   IN EFI_SYSTEM_TABLE                           *SystemTable
941   )
942 {
943   VOID                                          *SmmFvbRegistration;
944 
945   //
946   // Smm FVB driver is ready.
947   //
948   EfiCreateProtocolNotifyEvent (
949     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
950     TPL_CALLBACK,
951     SmmFvbReady,
952     NULL,
953     &SmmFvbRegistration
954     );
955 
956   return EFI_SUCCESS;
957 }
958 
959