1 /** @file
2   The realization of EFI_RAM_DISK_PROTOCOL.
3 
4   Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5   (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "RamDiskImpl.h"
17 
18 RAM_DISK_PRIVATE_DATA mRamDiskPrivateDataTemplate = {
19   RAM_DISK_PRIVATE_DATA_SIGNATURE,
20   NULL
21 };
22 
23 MEDIA_RAM_DISK_DEVICE_PATH  mRamDiskDeviceNodeTemplate = {
24   {
25     MEDIA_DEVICE_PATH,
26     MEDIA_RAM_DISK_DP,
27     {
28       (UINT8) (sizeof (MEDIA_RAM_DISK_DEVICE_PATH)),
29       (UINT8) ((sizeof (MEDIA_RAM_DISK_DEVICE_PATH)) >> 8)
30     }
31   }
32 };
33 
34 BOOLEAN  mRamDiskSsdtTableKeyValid = FALSE;
35 UINTN    mRamDiskSsdtTableKey;
36 
37 
38 /**
39   Initialize the RAM disk device node.
40 
41   @param[in]      PrivateData     Points to RAM disk private data.
42   @param[in, out] RamDiskDevNode  Points to the RAM disk device node.
43 
44 **/
45 VOID
RamDiskInitDeviceNode(IN RAM_DISK_PRIVATE_DATA * PrivateData,IN OUT MEDIA_RAM_DISK_DEVICE_PATH * RamDiskDevNode)46 RamDiskInitDeviceNode (
47   IN     RAM_DISK_PRIVATE_DATA         *PrivateData,
48   IN OUT MEDIA_RAM_DISK_DEVICE_PATH    *RamDiskDevNode
49   )
50 {
51   WriteUnaligned64 (
52     (UINT64 *) &(RamDiskDevNode->StartingAddr[0]),
53     (UINT64) PrivateData->StartingAddr
54     );
55   WriteUnaligned64 (
56     (UINT64 *) &(RamDiskDevNode->EndingAddr[0]),
57     (UINT64) PrivateData->StartingAddr + PrivateData->Size - 1
58     );
59   CopyGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid);
60   RamDiskDevNode->Instance = PrivateData->InstanceNumber;
61 }
62 
63 
64 /**
65   Initialize and publish NVDIMM root device SSDT in ACPI table.
66 
67   @retval EFI_SUCCESS        The NVDIMM root device SSDT is published.
68   @retval Others             The NVDIMM root device SSDT is not published.
69 
70 **/
71 EFI_STATUS
RamDiskPublishSsdt(VOID)72 RamDiskPublishSsdt (
73   VOID
74   )
75 {
76   EFI_STATUS                     Status;
77   EFI_ACPI_DESCRIPTION_HEADER    *Table;
78   UINTN                          SectionInstance;
79   UINTN                          TableSize;
80 
81   Status          = EFI_SUCCESS;
82   SectionInstance = 0;
83 
84   //
85   // Scan all the EFI raw section instances in FV to find the NVDIMM root
86   // device SSDT.
87   //
88   while (TRUE) {
89     Status = GetSectionFromFv (
90                &gEfiCallerIdGuid,
91                EFI_SECTION_RAW,
92                SectionInstance,
93                (VOID **) &Table,
94                &TableSize
95                );
96     if (EFI_ERROR (Status)) {
97       break;
98     }
99 
100     if (Table->OemTableId == SIGNATURE_64 ('R', 'a', 'm', 'D', 'i', 's', 'k', ' ')) {
101       Status = mAcpiTableProtocol->InstallAcpiTable (
102                                      mAcpiTableProtocol,
103                                      Table,
104                                      TableSize,
105                                      &mRamDiskSsdtTableKey
106                                      );
107       ASSERT_EFI_ERROR (Status);
108 
109       if (!EFI_ERROR (Status)) {
110         mRamDiskSsdtTableKeyValid = TRUE;
111       }
112 
113       FreePool (Table);
114       return Status;
115     } else {
116       FreePool (Table);
117       SectionInstance++;
118     }
119   }
120 
121   return Status;
122 }
123 
124 
125 /**
126   Publish the RAM disk NVDIMM Firmware Interface Table (NFIT) to the ACPI
127   table.
128 
129   @param[in] PrivateData          Points to RAM disk private data.
130 
131   @retval EFI_SUCCESS             The RAM disk NFIT has been published.
132   @retval others                  The RAM disk NFIT has not been published.
133 
134 **/
135 EFI_STATUS
RamDiskPublishNfit(IN RAM_DISK_PRIVATE_DATA * PrivateData)136 RamDiskPublishNfit (
137   IN RAM_DISK_PRIVATE_DATA        *PrivateData
138   )
139 {
140   EFI_STATUS                                    Status;
141   EFI_MEMORY_DESCRIPTOR                         *MemoryMap;
142   EFI_MEMORY_DESCRIPTOR                         *MemoryMapEntry;
143   EFI_MEMORY_DESCRIPTOR                         *MemoryMapEnd;
144   UINTN                                         TableIndex;
145   VOID                                          *TableHeader;
146   EFI_ACPI_TABLE_VERSION                        TableVersion;
147   UINTN                                         TableKey;
148   EFI_ACPI_DESCRIPTION_HEADER                   *NfitHeader;
149   EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE
150                                                 *SpaRange;
151   VOID                                          *Nfit;
152   UINT32                                        NfitLen;
153   UINTN                                         MemoryMapSize;
154   UINTN                                         MapKey;
155   UINTN                                         DescriptorSize;
156   UINT32                                        DescriptorVersion;
157   UINT64                                        CurrentData;
158   UINT8                                         Checksum;
159   BOOLEAN                                       MemoryFound;
160 
161   //
162   // Get the EFI memory map.
163   //
164   MemoryMapSize = 0;
165   MemoryMap     = NULL;
166   MemoryFound   = FALSE;
167 
168   Status = gBS->GetMemoryMap (
169                   &MemoryMapSize,
170                   MemoryMap,
171                   &MapKey,
172                   &DescriptorSize,
173                   &DescriptorVersion
174                   );
175   ASSERT (Status == EFI_BUFFER_TOO_SMALL);
176   do {
177     MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
178     ASSERT (MemoryMap != NULL);
179     Status = gBS->GetMemoryMap (
180                     &MemoryMapSize,
181                     MemoryMap,
182                     &MapKey,
183                     &DescriptorSize,
184                     &DescriptorVersion
185                     );
186     if (EFI_ERROR (Status)) {
187       FreePool (MemoryMap);
188     }
189   } while (Status == EFI_BUFFER_TOO_SMALL);
190   ASSERT_EFI_ERROR (Status);
191 
192   MemoryMapEntry = MemoryMap;
193   MemoryMapEnd   = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
194   while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
195     if ((MemoryMapEntry->Type == EfiReservedMemoryType) &&
196         (MemoryMapEntry->PhysicalStart <= PrivateData->StartingAddr) &&
197         (MemoryMapEntry->PhysicalStart +
198          MultU64x32 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SIZE)
199          >= PrivateData->StartingAddr + PrivateData->Size)) {
200       MemoryFound = TRUE;
201       DEBUG ((
202         EFI_D_INFO,
203         "RamDiskPublishNfit: RAM disk with reserved meomry type, will publish to NFIT.\n"
204         ));
205       break;
206     }
207     MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
208   }
209   FreePool (MemoryMap);
210 
211   if (!MemoryFound) {
212     return EFI_NOT_FOUND;
213   }
214 
215   //
216   // Determine whether there is a NFIT already in the ACPI table.
217   //
218   Status      = EFI_SUCCESS;
219   TableIndex  = 0;
220   TableKey    = 0;
221   TableHeader = NULL;
222 
223   while (!EFI_ERROR (Status)) {
224     Status = mAcpiSdtProtocol->GetAcpiTable (
225                                  TableIndex,
226                                  (EFI_ACPI_SDT_HEADER **)&TableHeader,
227                                  &TableVersion,
228                                  &TableKey
229                                  );
230     if (!EFI_ERROR (Status)) {
231       TableIndex++;
232 
233       if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature ==
234           EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) {
235         break;
236       }
237     }
238   }
239 
240   if (!EFI_ERROR (Status)) {
241     //
242     // A NFIT is already in the ACPI table.
243     //
244     DEBUG ((
245       EFI_D_INFO,
246       "RamDiskPublishNfit: A NFIT is already exist in the ACPI Table.\n"
247       ));
248 
249     NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)TableHeader;
250     NfitLen    = NfitHeader->Length + sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
251     Nfit       = AllocateZeroPool (NfitLen);
252     if (Nfit == NULL) {
253       return EFI_OUT_OF_RESOURCES;
254     }
255     CopyMem (Nfit, TableHeader, NfitHeader->Length);
256 
257     //
258     // Update the NFIT head pointer.
259     //
260     NfitHeader = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit;
261 
262     //
263     // Uninstall the origin NFIT from the ACPI table.
264     //
265     Status = mAcpiTableProtocol->UninstallAcpiTable (
266                                    mAcpiTableProtocol,
267                                    TableKey
268                                    );
269     ASSERT_EFI_ERROR (Status);
270 
271     if (EFI_ERROR (Status)) {
272       FreePool (Nfit);
273       return Status;
274     }
275 
276     //
277     // Append the System Physical Address (SPA) Range Structure at the end
278     // of the origin NFIT.
279     //
280     SpaRange   = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)
281                  ((UINT8 *)Nfit + NfitHeader->Length);
282 
283     //
284     // Update the length field of the NFIT
285     //
286     NfitHeader->Length   = NfitLen;
287 
288     //
289     // The checksum will be updated after the new contents are appended.
290     //
291     NfitHeader->Checksum = 0;
292   } else {
293     //
294     // Assumption is made that if no NFIT is in the ACPI table, there is no
295     // NVDIMM root device in the \SB scope.
296     // Therefore, a NVDIMM root device will be reported via Secondary System
297     // Description Table (SSDT).
298     //
299     Status = RamDiskPublishSsdt ();
300     if (EFI_ERROR (Status)) {
301       return Status;
302     }
303 
304     //
305     // No NFIT is in the ACPI table, we will create one here.
306     //
307     DEBUG ((
308       EFI_D_INFO,
309       "RamDiskPublishNfit: No NFIT is in the ACPI Table, will create one.\n"
310       ));
311 
312     NfitLen = sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE) +
313               sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
314     Nfit    = AllocateZeroPool (NfitLen);
315     if (Nfit == NULL) {
316       return EFI_OUT_OF_RESOURCES;
317     }
318 
319     SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)
320                ((UINT8 *)Nfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
321 
322     NfitHeader                  = (EFI_ACPI_DESCRIPTION_HEADER *)Nfit;
323     NfitHeader->Signature       = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE;
324     NfitHeader->Length          = NfitLen;
325     NfitHeader->Revision        = EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_REVISION;
326     NfitHeader->Checksum        = 0;
327     NfitHeader->OemRevision     = PcdGet32 (PcdAcpiDefaultOemRevision);
328     NfitHeader->CreatorId       = PcdGet32 (PcdAcpiDefaultCreatorId);
329     NfitHeader->CreatorRevision = PcdGet32 (PcdAcpiDefaultCreatorRevision);
330     CurrentData                 = PcdGet64 (PcdAcpiDefaultOemTableId);
331     CopyMem (NfitHeader->OemId, PcdGetPtr (PcdAcpiDefaultOemId), sizeof (NfitHeader->OemId));
332     CopyMem (&NfitHeader->OemTableId, &CurrentData, sizeof (UINT64));
333   }
334 
335   //
336   // Fill in the content of the SPA Range Structure.
337   //
338   SpaRange->Type   = EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE;
339   SpaRange->Length = sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
340   SpaRange->SystemPhysicalAddressRangeBase   = PrivateData->StartingAddr;
341   SpaRange->SystemPhysicalAddressRangeLength = PrivateData->Size;
342   CopyGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid);
343 
344   Checksum             = CalculateCheckSum8((UINT8 *)Nfit, NfitHeader->Length);
345   NfitHeader->Checksum = Checksum;
346 
347   //
348   // Publish the NFIT to the ACPI table.
349   // Note, since the NFIT might be modified by other driver, therefore, we
350   // do not track the returning TableKey from the InstallAcpiTable().
351   //
352   Status = mAcpiTableProtocol->InstallAcpiTable (
353                                  mAcpiTableProtocol,
354                                  Nfit,
355                                  NfitHeader->Length,
356                                  &TableKey
357                                  );
358   ASSERT_EFI_ERROR (Status);
359 
360   FreePool (Nfit);
361 
362   if (EFI_ERROR (Status)) {
363     return Status;
364   }
365 
366   PrivateData->InNfit = TRUE;
367 
368   return EFI_SUCCESS;
369 }
370 
371 
372 /**
373   Unpublish the RAM disk NVDIMM Firmware Interface Table (NFIT) from the
374   ACPI table.
375 
376   @param[in] PrivateData          Points to RAM disk private data.
377 
378   @retval EFI_SUCCESS             The RAM disk NFIT has been unpublished.
379   @retval others                  The RAM disk NFIT has not been unpublished.
380 
381 **/
382 EFI_STATUS
RamDiskUnpublishNfit(IN RAM_DISK_PRIVATE_DATA * PrivateData)383 RamDiskUnpublishNfit (
384   IN RAM_DISK_PRIVATE_DATA        *PrivateData
385   )
386 {
387   EFI_STATUS                                    Status;
388   UINTN                                         TableIndex;
389   VOID                                          *TableHeader;
390   EFI_ACPI_TABLE_VERSION                        TableVersion;
391   UINTN                                         TableKey;
392   EFI_ACPI_DESCRIPTION_HEADER                   *NewNfitHeader;
393   EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE
394                                                 *SpaRange;
395   VOID                                          *NewNfit;
396   VOID                                          *NewNfitPtr;
397   EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER            *NfitStructHeader;
398   UINT32                                        NewNfitLen;
399   UINT32                                        RemainLen;
400   UINT8                                         Checksum;
401 
402   //
403   // Find the NFIT in the ACPI table.
404   //
405   Status      = EFI_SUCCESS;
406   TableIndex  = 0;
407   TableKey    = 0;
408   TableHeader = NULL;
409 
410   while (!EFI_ERROR (Status)) {
411     Status = mAcpiSdtProtocol->GetAcpiTable (
412                                  TableIndex,
413                                  (EFI_ACPI_SDT_HEADER **)&TableHeader,
414                                  &TableVersion,
415                                  &TableKey
416                                  );
417     if (!EFI_ERROR (Status)) {
418       TableIndex++;
419 
420       if (((EFI_ACPI_SDT_HEADER *)TableHeader)->Signature ==
421           EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE_STRUCTURE_SIGNATURE) {
422         break;
423       }
424     }
425   }
426 
427   if (EFI_ERROR (Status)) {
428     //
429     // No NFIT is found in the ACPI table.
430     //
431     return EFI_NOT_FOUND;
432   }
433 
434   NewNfitLen    = ((EFI_ACPI_DESCRIPTION_HEADER *)TableHeader)->Length -
435                   sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE);
436 
437   //
438   // After removing this RAM disk from the NFIT, if no other structure is in
439   // the NFIT, we just remove the NFIT and the SSDT which is used to report
440   // the NVDIMM root device.
441   //
442   if (NewNfitLen == sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE)) {
443     //
444     // Remove the NFIT.
445     //
446     Status = mAcpiTableProtocol->UninstallAcpiTable (
447                                    mAcpiTableProtocol,
448                                    TableKey
449                                    );
450     ASSERT_EFI_ERROR (Status);
451     if (EFI_ERROR (Status)) {
452       return Status;
453     }
454 
455     //
456     // Remove the SSDT which is used by RamDiskDxe driver to report the NVDIMM
457     // root device.
458     // We do not care the return status since this SSDT might already be
459     // uninstalled by other drivers to update the information of the NVDIMM
460     // root device.
461     //
462     if (mRamDiskSsdtTableKeyValid) {
463       mRamDiskSsdtTableKeyValid = FALSE;
464 
465       mAcpiTableProtocol->UninstallAcpiTable (
466                             mAcpiTableProtocol,
467                             mRamDiskSsdtTableKey
468                             );
469     }
470 
471     return EFI_SUCCESS;
472   }
473 
474   NewNfit = AllocateZeroPool (NewNfitLen);
475   if (NewNfit == NULL) {
476     return EFI_OUT_OF_RESOURCES;
477   }
478 
479   //
480   // Get a copy of the old NFIT header content.
481   //
482   CopyMem (NewNfit, TableHeader, sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
483   NewNfitHeader           = (EFI_ACPI_DESCRIPTION_HEADER *)NewNfit;
484   NewNfitHeader->Length   = NewNfitLen;
485   NewNfitHeader->Checksum = 0;
486 
487   //
488   // Copy the content of required NFIT structures.
489   //
490   NewNfitPtr       = (UINT8 *)NewNfit + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE);
491   RemainLen        = NewNfitLen - sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE);
492   NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
493                      ((UINT8 *)TableHeader + sizeof (EFI_ACPI_6_1_NVDIMM_FIRMWARE_INTERFACE_TABLE));
494   while (RemainLen > 0) {
495     if ((NfitStructHeader->Type == EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE_TYPE) &&
496         (NfitStructHeader->Length == sizeof (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE))) {
497       SpaRange = (EFI_ACPI_6_1_NFIT_SYSTEM_PHYSICAL_ADDRESS_RANGE_STRUCTURE *)NfitStructHeader;
498 
499       if ((SpaRange->SystemPhysicalAddressRangeBase == PrivateData->StartingAddr) &&
500           (SpaRange->SystemPhysicalAddressRangeLength == PrivateData->Size) &&
501           (CompareGuid (&SpaRange->AddressRangeTypeGUID, &PrivateData->TypeGuid))) {
502         //
503         // Skip the SPA Range Structure for the RAM disk to be unpublished
504         // from NFIT.
505         //
506         NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
507                            ((UINT8 *)NfitStructHeader + NfitStructHeader->Length);
508         continue;
509       }
510     }
511 
512     //
513     // Copy the content of origin NFIT.
514     //
515     CopyMem (NewNfitPtr, NfitStructHeader, NfitStructHeader->Length);
516     NewNfitPtr = (UINT8 *)NewNfitPtr + NfitStructHeader->Length;
517 
518     //
519     // Move to the header of next NFIT structure.
520     //
521     RemainLen       -= NfitStructHeader->Length;
522     NfitStructHeader = (EFI_ACPI_6_1_NFIT_STRUCTURE_HEADER *)
523                        ((UINT8 *)NfitStructHeader + NfitStructHeader->Length);
524   }
525 
526   Checksum                = CalculateCheckSum8((UINT8 *)NewNfit, NewNfitHeader->Length);
527   NewNfitHeader->Checksum = Checksum;
528 
529   Status = mAcpiTableProtocol->UninstallAcpiTable (
530                                  mAcpiTableProtocol,
531                                  TableKey
532                                  );
533   ASSERT_EFI_ERROR (Status);
534 
535   if (EFI_ERROR (Status)) {
536     FreePool (NewNfit);
537     return Status;
538   }
539 
540   //
541   // Publish the NFIT to the ACPI table.
542   // Note, since the NFIT might be modified by other driver, therefore, we
543   // do not track the returning TableKey from the InstallAcpiTable().
544   //
545   Status = mAcpiTableProtocol->InstallAcpiTable (
546                                  mAcpiTableProtocol,
547                                  NewNfit,
548                                  NewNfitLen,
549                                  &TableKey
550                                  );
551   ASSERT_EFI_ERROR (Status);
552 
553   FreePool (NewNfit);
554   if (EFI_ERROR (Status)) {
555     return Status;
556   }
557 
558   return EFI_SUCCESS;
559 }
560 
561 
562 /**
563   Register a RAM disk with specified address, size and type.
564 
565   @param[in]  RamDiskBase    The base address of registered RAM disk.
566   @param[in]  RamDiskSize    The size of registered RAM disk.
567   @param[in]  RamDiskType    The type of registered RAM disk. The GUID can be
568                              any of the values defined in section 9.3.6.9, or a
569                              vendor defined GUID.
570   @param[in]  ParentDevicePath
571                              Pointer to the parent device path. If there is no
572                              parent device path then ParentDevicePath is NULL.
573   @param[out] DevicePath     On return, points to a pointer to the device path
574                              of the RAM disk device.
575                              If ParentDevicePath is not NULL, the returned
576                              DevicePath is created by appending a RAM disk node
577                              to the parent device path. If ParentDevicePath is
578                              NULL, the returned DevicePath is a RAM disk device
579                              path without appending. This function is
580                              responsible for allocating the buffer DevicePath
581                              with the boot service AllocatePool().
582 
583   @retval EFI_SUCCESS             The RAM disk is registered successfully.
584   @retval EFI_INVALID_PARAMETER   DevicePath or RamDiskType is NULL.
585                                   RamDiskSize is 0.
586   @retval EFI_ALREADY_STARTED     A Device Path Protocol instance to be created
587                                   is already present in the handle database.
588   @retval EFI_OUT_OF_RESOURCES    The RAM disk register operation fails due to
589                                   resource limitation.
590 
591 **/
592 EFI_STATUS
593 EFIAPI
RamDiskRegister(IN UINT64 RamDiskBase,IN UINT64 RamDiskSize,IN EFI_GUID * RamDiskType,IN EFI_DEVICE_PATH * ParentDevicePath OPTIONAL,OUT EFI_DEVICE_PATH_PROTOCOL ** DevicePath)594 RamDiskRegister (
595   IN UINT64                       RamDiskBase,
596   IN UINT64                       RamDiskSize,
597   IN EFI_GUID                     *RamDiskType,
598   IN EFI_DEVICE_PATH              *ParentDevicePath     OPTIONAL,
599   OUT EFI_DEVICE_PATH_PROTOCOL    **DevicePath
600   )
601 {
602   EFI_STATUS                      Status;
603   RAM_DISK_PRIVATE_DATA           *PrivateData;
604   RAM_DISK_PRIVATE_DATA           *RegisteredPrivateData;
605   MEDIA_RAM_DISK_DEVICE_PATH      *RamDiskDevNode;
606   UINTN                           DevicePathSize;
607   LIST_ENTRY                      *Entry;
608 
609   if ((0 == RamDiskSize) || (NULL == RamDiskType) || (NULL == DevicePath)) {
610     return EFI_INVALID_PARAMETER;
611   }
612 
613   //
614   // Add check to prevent data read across the memory boundary
615   //
616   if (RamDiskBase + RamDiskSize > ((UINTN) -1) - RAM_DISK_BLOCK_SIZE + 1) {
617     return EFI_INVALID_PARAMETER;
618   }
619 
620   RamDiskDevNode = NULL;
621 
622   //
623   // Create a new RAM disk instance and initialize its private data
624   //
625   PrivateData = AllocateCopyPool (
626                   sizeof (RAM_DISK_PRIVATE_DATA),
627                   &mRamDiskPrivateDataTemplate
628                   );
629   if (NULL == PrivateData) {
630     return EFI_OUT_OF_RESOURCES;
631   }
632 
633   PrivateData->StartingAddr = RamDiskBase;
634   PrivateData->Size         = RamDiskSize;
635   CopyGuid (&PrivateData->TypeGuid, RamDiskType);
636   InitializeListHead (&PrivateData->ThisInstance);
637 
638   //
639   // Generate device path information for the registered RAM disk
640   //
641   RamDiskDevNode = AllocateCopyPool (
642                      sizeof (MEDIA_RAM_DISK_DEVICE_PATH),
643                      &mRamDiskDeviceNodeTemplate
644                      );
645   if (NULL == RamDiskDevNode) {
646     Status = EFI_OUT_OF_RESOURCES;
647     goto ErrorExit;
648   }
649 
650   RamDiskInitDeviceNode (PrivateData, RamDiskDevNode);
651 
652   *DevicePath = AppendDevicePathNode (
653                   ParentDevicePath,
654                   (EFI_DEVICE_PATH_PROTOCOL *) RamDiskDevNode
655                   );
656   if (NULL == *DevicePath) {
657     Status = EFI_OUT_OF_RESOURCES;
658     goto ErrorExit;
659   }
660 
661   PrivateData->DevicePath = *DevicePath;
662 
663   //
664   // Check whether the created device path is already present in the handle
665   // database
666   //
667   if (!IsListEmpty(&RegisteredRamDisks)) {
668     DevicePathSize = GetDevicePathSize (PrivateData->DevicePath);
669 
670     EFI_LIST_FOR_EACH (Entry, &RegisteredRamDisks) {
671       RegisteredPrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry);
672       if (DevicePathSize == GetDevicePathSize (RegisteredPrivateData->DevicePath)) {
673         //
674         // Compare device path
675         //
676         if ((CompareMem (
677                PrivateData->DevicePath,
678                RegisteredPrivateData->DevicePath,
679                DevicePathSize)) == 0) {
680           *DevicePath = NULL;
681           Status      = EFI_ALREADY_STARTED;
682           goto ErrorExit;
683         }
684       }
685     }
686   }
687 
688   //
689   // Fill Block IO protocol informations for the RAM disk
690   //
691   RamDiskInitBlockIo (PrivateData);
692 
693   //
694   // Install EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL on a new
695   // handle
696   //
697   Status = gBS->InstallMultipleProtocolInterfaces (
698                   &PrivateData->Handle,
699                   &gEfiBlockIoProtocolGuid,
700                   &PrivateData->BlockIo,
701                   &gEfiBlockIo2ProtocolGuid,
702                   &PrivateData->BlockIo2,
703                   &gEfiDevicePathProtocolGuid,
704                   PrivateData->DevicePath,
705                   NULL
706                   );
707   if (EFI_ERROR (Status)) {
708     goto ErrorExit;
709   }
710 
711   //
712   // Insert the newly created one to the registered RAM disk list
713   //
714   InsertTailList (&RegisteredRamDisks, &PrivateData->ThisInstance);
715 
716   gBS->ConnectController (PrivateData->Handle, NULL, NULL, TRUE);
717 
718   FreePool (RamDiskDevNode);
719 
720   if ((mAcpiTableProtocol != NULL) && (mAcpiSdtProtocol != NULL)) {
721     RamDiskPublishNfit (PrivateData);
722   }
723 
724   return EFI_SUCCESS;
725 
726 ErrorExit:
727   if (RamDiskDevNode != NULL) {
728     FreePool (RamDiskDevNode);
729   }
730 
731   if (PrivateData != NULL) {
732     if (PrivateData->DevicePath) {
733       FreePool (PrivateData->DevicePath);
734     }
735 
736     FreePool (PrivateData);
737   }
738 
739   return Status;
740 }
741 
742 
743 /**
744   Unregister a RAM disk specified by DevicePath.
745 
746   @param[in] DevicePath      A pointer to the device path that describes a RAM
747                              Disk device.
748 
749   @retval EFI_SUCCESS             The RAM disk is unregistered successfully.
750   @retval EFI_INVALID_PARAMETER   DevicePath is NULL.
751   @retval EFI_UNSUPPORTED         The device specified by DevicePath is not a
752                                   valid ramdisk device path and not supported
753                                   by the driver.
754   @retval EFI_NOT_FOUND           The RAM disk pointed by DevicePath doesn't
755                                   exist.
756 
757 **/
758 EFI_STATUS
759 EFIAPI
RamDiskUnregister(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)760 RamDiskUnregister (
761   IN  EFI_DEVICE_PATH_PROTOCOL    *DevicePath
762   )
763 {
764   LIST_ENTRY                      *Entry;
765   LIST_ENTRY                      *NextEntry;
766   BOOLEAN                         Found;
767   UINT64                          StartingAddr;
768   UINT64                          EndingAddr;
769   EFI_DEVICE_PATH_PROTOCOL        *Header;
770   MEDIA_RAM_DISK_DEVICE_PATH      *RamDiskDevNode;
771   RAM_DISK_PRIVATE_DATA           *PrivateData;
772 
773   if (NULL == DevicePath) {
774     return EFI_INVALID_PARAMETER;
775   }
776 
777   //
778   // Locate the RAM disk device node.
779   //
780   RamDiskDevNode = NULL;
781   Header         = DevicePath;
782   do {
783     //
784     // Test if the current device node is a RAM disk.
785     //
786     if ((MEDIA_DEVICE_PATH == Header->Type) &&
787       (MEDIA_RAM_DISK_DP == Header->SubType)) {
788       RamDiskDevNode = (MEDIA_RAM_DISK_DEVICE_PATH *) Header;
789 
790       break;
791     }
792 
793     Header = NextDevicePathNode (Header);
794   } while ((Header->Type != END_DEVICE_PATH_TYPE));
795 
796   if (NULL == RamDiskDevNode) {
797     return EFI_UNSUPPORTED;
798   }
799 
800   Found          = FALSE;
801   StartingAddr   = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->StartingAddr[0]));
802   EndingAddr     = ReadUnaligned64 ((UINT64 *) &(RamDiskDevNode->EndingAddr[0]));
803 
804   if (!IsListEmpty(&RegisteredRamDisks)) {
805     EFI_LIST_FOR_EACH_SAFE (Entry, NextEntry, &RegisteredRamDisks) {
806       PrivateData = RAM_DISK_PRIVATE_FROM_THIS (Entry);
807 
808       //
809       // Unregister the RAM disk given by its starting address, ending address
810       // and type guid.
811       //
812       if ((StartingAddr == PrivateData->StartingAddr) &&
813           (EndingAddr == PrivateData->StartingAddr + PrivateData->Size - 1) &&
814           (CompareGuid (&RamDiskDevNode->TypeGuid, &PrivateData->TypeGuid))) {
815         //
816         // Remove the content for this RAM disk in NFIT.
817         //
818         if (PrivateData->InNfit) {
819           RamDiskUnpublishNfit (PrivateData);
820         }
821 
822         //
823         // Uninstall the EFI_DEVICE_PATH_PROTOCOL & EFI_BLOCK_IO(2)_PROTOCOL
824         //
825         gBS->UninstallMultipleProtocolInterfaces (
826                PrivateData->Handle,
827                &gEfiBlockIoProtocolGuid,
828                &PrivateData->BlockIo,
829                &gEfiBlockIo2ProtocolGuid,
830                &PrivateData->BlockIo2,
831                &gEfiDevicePathProtocolGuid,
832                (EFI_DEVICE_PATH_PROTOCOL *) PrivateData->DevicePath,
833                NULL
834                );
835 
836         RemoveEntryList (&PrivateData->ThisInstance);
837 
838         if (RamDiskCreateHii == PrivateData->CreateMethod) {
839           //
840           // If a RAM disk is created within HII, then the RamDiskDxe driver
841           // driver is responsible for freeing the allocated memory for the
842           // RAM disk.
843           //
844           FreePool ((VOID *)(UINTN) PrivateData->StartingAddr);
845         }
846 
847         FreePool (PrivateData->DevicePath);
848         FreePool (PrivateData);
849         Found = TRUE;
850 
851         break;
852       }
853     }
854   }
855 
856   if (TRUE == Found) {
857     return EFI_SUCCESS;
858   } else {
859     return EFI_NOT_FOUND;
860   }
861 }
862