]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c
MdeModulePkg: Change use of EFI_D_* to DEBUG_*
[mirror_edk2.git] / MdeModulePkg / Universal / SmbiosDxe / SmbiosDxe.c
1 /** @file
2 This code produces the Smbios protocol. It also responsible for constructing
3 SMBIOS table into system table.
4
5 Copyright (c) 2009 - 2021, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "SmbiosDxe.h"
11
12 //
13 // Module Global:
14 // Since this driver will only ever produce one instance of the
15 // protocol you are not required to dynamically allocate the PrivateData.
16 //
17 SMBIOS_INSTANCE mPrivateData;
18
19 UINTN mPreAllocatedPages = 0;
20 UINTN mPre64BitAllocatedPages = 0;
21
22 //
23 // Chassis for SMBIOS entry point structure that is to be installed into EFI system config table.
24 //
25 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure = NULL;
26 SMBIOS_TABLE_ENTRY_POINT EntryPointStructureData = {
27 //
28 // AnchorString
29 //
30 {
31 0x5f,
32 0x53,
33 0x4d,
34 0x5f
35 },
36 //
37 // EntryPointStructureChecksum,TO BE FILLED
38 //
39 0,
40 //
41 // EntryPointStructure Length
42 //
43 0x1f,
44 //
45 // MajorVersion
46 //
47 0,
48 //
49 // MinorVersion
50 //
51 0,
52 //
53 // MaxStructureSize, TO BE FILLED
54 //
55 0,
56 //
57 // EntryPointRevision
58 //
59 0,
60 //
61 // FormattedArea
62 //
63 {
64 0,
65 0,
66 0,
67 0,
68 0
69 },
70 //
71 // IntermediateAnchorString
72 //
73 {
74 0x5f,
75 0x44,
76 0x4d,
77 0x49,
78 0x5f
79 },
80 //
81 // IntermediateChecksum, TO BE FILLED
82 //
83 0,
84 //
85 // TableLength, TO BE FILLED
86 //
87 0,
88 //
89 // TableAddress, TO BE FILLED
90 //
91 0,
92 //
93 // NumberOfSmbiosStructures, TO BE FILLED
94 //
95 0,
96 //
97 // SmbiosBcdRevision
98 //
99 0
100 };
101
102 SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30EntryPointStructure = NULL;
103 SMBIOS_TABLE_3_0_ENTRY_POINT Smbios30EntryPointStructureData = {
104 //
105 // AnchorString _SM3_
106 //
107 {
108 0x5f,
109 0x53,
110 0x4d,
111 0x33,
112 0x5f,
113 },
114 //
115 // EntryPointStructureChecksum,TO BE FILLED
116 //
117 0,
118 //
119 // EntryPointLength
120 //
121 0x18,
122 //
123 // MajorVersion
124 //
125 0,
126 //
127 // MinorVersion
128 //
129 0,
130 //
131 // DocRev
132 //
133 0,
134 //
135 // EntryPointRevision
136 //
137 0x01,
138 //
139 // Reserved
140 //
141 0,
142 //
143 // TableMaximumSize,TO BE FILLED
144 //
145 0,
146 //
147 // TableAddress,TO BE FILLED
148 //
149 0
150 };
151
152 IS_SMBIOS_TABLE_VALID_ENTRY mIsSmbiosTableValid[] = {
153 {&gUniversalPayloadSmbios3TableGuid, IsValidSmbios30Table },
154 {&gUniversalPayloadSmbiosTableGuid, IsValidSmbios20Table }
155 };
156
157 /**
158
159 Get the full size of SMBIOS structure including optional strings that follow the formatted structure.
160
161 @param This The EFI_SMBIOS_PROTOCOL instance.
162 @param Head Pointer to the beginning of SMBIOS structure.
163 @param Size The returned size.
164 @param NumberOfStrings The returned number of optional strings that follow the formatted structure.
165
166 @retval EFI_SUCCESS Size retured in Size.
167 @retval EFI_INVALID_PARAMETER Input SMBIOS structure mal-formed or Size is NULL.
168
169 **/
170 EFI_STATUS
171 EFIAPI
172 GetSmbiosStructureSize (
173 IN CONST EFI_SMBIOS_PROTOCOL *This,
174 IN EFI_SMBIOS_TABLE_HEADER *Head,
175 OUT UINTN *Size,
176 OUT UINTN *NumberOfStrings
177 )
178 {
179 UINTN FullSize;
180 UINTN StrLen;
181 UINTN MaxLen;
182 INT8* CharInStr;
183
184 if (Size == NULL || NumberOfStrings == NULL) {
185 return EFI_INVALID_PARAMETER;
186 }
187
188 FullSize = Head->Length;
189 CharInStr = (INT8*)Head + Head->Length;
190 *Size = FullSize;
191 *NumberOfStrings = 0;
192 StrLen = 0;
193 //
194 // look for the two consecutive zeros, check the string limit by the way.
195 //
196 while (*CharInStr != 0 || *(CharInStr+1) != 0) {
197 if (*CharInStr == 0) {
198 *Size += 1;
199 CharInStr++;
200 }
201
202 if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)){
203 MaxLen = SMBIOS_STRING_MAX_LENGTH;
204 } else if (This->MajorVersion < 3) {
205 //
206 // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
207 // However, the length of the entire structure table (including all strings) must be reported
208 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
209 // which is a WORD field limited to 65,535 bytes.
210 //
211 MaxLen = SMBIOS_TABLE_MAX_LENGTH;
212 } else {
213 //
214 // SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes.
215 // Locate the end of string as long as possible.
216 //
217 MaxLen = SMBIOS_3_0_TABLE_MAX_LENGTH;
218 }
219
220 for (StrLen = 0 ; StrLen < MaxLen; StrLen++) {
221 if (*(CharInStr+StrLen) == 0) {
222 break;
223 }
224 }
225
226 if (StrLen == MaxLen) {
227 return EFI_INVALID_PARAMETER;
228 }
229
230 //
231 // forward the pointer
232 //
233 CharInStr += StrLen;
234 *Size += StrLen;
235 *NumberOfStrings += 1;
236 }
237
238 //
239 // count ending two zeros.
240 //
241 *Size += 2;
242 return EFI_SUCCESS;
243 }
244
245 /**
246
247 Determin whether an SmbiosHandle has already in use.
248
249 @param Head Pointer to the beginning of SMBIOS structure.
250 @param Handle A unique handle will be assigned to the SMBIOS record.
251
252 @retval TRUE Smbios handle already in use.
253 @retval FALSE Smbios handle is NOT used.
254
255 **/
256 BOOLEAN
257 EFIAPI
258 CheckSmbiosHandleExistance (
259 IN LIST_ENTRY *Head,
260 IN EFI_SMBIOS_HANDLE Handle
261 )
262 {
263 LIST_ENTRY *Link;
264 SMBIOS_HANDLE_ENTRY *HandleEntry;
265
266 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
267 HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
268 if (HandleEntry->SmbiosHandle == Handle) {
269 return TRUE;
270 }
271 }
272
273 return FALSE;
274 }
275
276 /**
277
278 Get the max SmbiosHandle that could be use.
279
280 @param This The EFI_SMBIOS_PROTOCOL instance.
281 @param MaxHandle The max handle that could be assigned to the SMBIOS record.
282
283 **/
284 VOID
285 EFIAPI
286 GetMaxSmbiosHandle (
287 IN CONST EFI_SMBIOS_PROTOCOL *This,
288 IN OUT EFI_SMBIOS_HANDLE *MaxHandle
289 )
290 {
291 if (This->MajorVersion == 2 && This->MinorVersion == 0) {
292 *MaxHandle = 0xFFFE;
293 } else {
294 *MaxHandle = 0xFEFF;
295 }
296 }
297
298 /**
299
300 Get an SmbiosHandle that could use.
301
302 @param This The EFI_SMBIOS_PROTOCOL instance.
303 @param SmbiosHandle A unique handle will be assigned to the SMBIOS record.
304
305 @retval EFI_SUCCESS Smbios handle got.
306 @retval EFI_OUT_OF_RESOURCES Smbios handle is NOT available.
307
308 **/
309 EFI_STATUS
310 EFIAPI
311 GetAvailableSmbiosHandle (
312 IN CONST EFI_SMBIOS_PROTOCOL *This,
313 IN OUT EFI_SMBIOS_HANDLE *Handle
314 )
315 {
316 LIST_ENTRY *Head;
317 SMBIOS_INSTANCE *Private;
318 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
319 EFI_SMBIOS_HANDLE AvailableHandle;
320
321 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
322
323 Private = SMBIOS_INSTANCE_FROM_THIS (This);
324 Head = &Private->AllocatedHandleListHead;
325 for (AvailableHandle = 0; AvailableHandle < MaxSmbiosHandle; AvailableHandle++) {
326 if (!CheckSmbiosHandleExistance(Head, AvailableHandle)) {
327 *Handle = AvailableHandle;
328 return EFI_SUCCESS;
329 }
330 }
331
332 return EFI_OUT_OF_RESOURCES;
333 }
334
335
336 /**
337 Add an SMBIOS record.
338
339 @param This The EFI_SMBIOS_PROTOCOL instance.
340 @param ProducerHandle The handle of the controller or driver associated with the SMBIOS information. NULL
341 means no handle.
342 @param SmbiosHandle On entry, the handle of the SMBIOS record to add. If FFFEh, then a unique handle
343 will be assigned to the SMBIOS record. If the SMBIOS handle is already in use,
344 EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated.
345 @param Record The data for the fixed portion of the SMBIOS record. The format of the record is
346 determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined
347 by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or
348 a set of null terminated strings and a null.
349
350 @retval EFI_SUCCESS Record was added.
351 @retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources.
352 @retval EFI_ALREADY_STARTED The SmbiosHandle passed in was already in use.
353
354 **/
355 EFI_STATUS
356 EFIAPI
357 SmbiosAdd (
358 IN CONST EFI_SMBIOS_PROTOCOL *This,
359 IN EFI_HANDLE ProducerHandle, OPTIONAL
360 IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
361 IN EFI_SMBIOS_TABLE_HEADER *Record
362 )
363 {
364 VOID *Raw;
365 UINTN TotalSize;
366 UINTN RecordSize;
367 UINTN StructureSize;
368 UINTN NumberOfStrings;
369 EFI_STATUS Status;
370 LIST_ENTRY *Head;
371 SMBIOS_INSTANCE *Private;
372 EFI_SMBIOS_ENTRY *SmbiosEntry;
373 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
374 SMBIOS_HANDLE_ENTRY *HandleEntry;
375 EFI_SMBIOS_RECORD_HEADER *InternalRecord;
376 BOOLEAN Smbios32BitTable;
377 BOOLEAN Smbios64BitTable;
378
379 if (SmbiosHandle == NULL) {
380 return EFI_INVALID_PARAMETER;
381 }
382
383 Private = SMBIOS_INSTANCE_FROM_THIS (This);
384 //
385 // Check whether SmbiosHandle is already in use
386 //
387 Head = &Private->AllocatedHandleListHead;
388 if (*SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED && CheckSmbiosHandleExistance(Head, *SmbiosHandle)) {
389 return EFI_ALREADY_STARTED;
390 }
391
392 //
393 // when SmbiosHandle is 0xFFFE, an available handle will be assigned
394 //
395 if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
396 Status = GetAvailableSmbiosHandle(This, SmbiosHandle);
397 if (EFI_ERROR(Status)) {
398 return Status;
399 }
400 } else {
401 //
402 // Check this handle validity
403 //
404 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
405 if (*SmbiosHandle > MaxSmbiosHandle) {
406 return EFI_INVALID_PARAMETER;
407 }
408 }
409
410 //
411 // Calculate record size and string number
412 //
413 Status = GetSmbiosStructureSize(This, Record, &StructureSize, &NumberOfStrings);
414 if (EFI_ERROR(Status)) {
415 return Status;
416 }
417
418 Smbios32BitTable = FALSE;
419 Smbios64BitTable = FALSE;
420 if ((This->MajorVersion < 0x3) ||
421 ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) {
422 //
423 // For SMBIOS 32-bit table, the length of the entire structure table (including all strings) must be reported
424 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
425 // which is a WORD field limited to 65,535 bytes. So the max size of 32-bit table should not exceed 65,535 bytes.
426 //
427 if ((EntryPointStructure != NULL) &&
428 (EntryPointStructure->TableLength + StructureSize > SMBIOS_TABLE_MAX_LENGTH)) {
429 DEBUG ((DEBUG_INFO, "SmbiosAdd: Total length exceeds max 32-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize));
430 } else {
431 Smbios32BitTable = TRUE;
432 DEBUG ((DEBUG_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 32-bit table\n", Record->Type, StructureSize));
433 }
434 }
435
436 //
437 // For SMBIOS 3.0, Structure table maximum size in Entry Point structure is DWORD field limited to 0xFFFFFFFF bytes.
438 //
439 if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) {
440 //
441 // For SMBIOS 64-bit table, Structure table maximum size in SMBIOS 3.0 (64-bit) Entry Point
442 // is a DWORD field limited to 0xFFFFFFFF bytes. So the max size of 64-bit table should not exceed 0xFFFFFFFF bytes.
443 //
444 if ((Smbios30EntryPointStructure != NULL) &&
445 (Smbios30EntryPointStructure->TableMaximumSize + StructureSize > SMBIOS_3_0_TABLE_MAX_LENGTH)) {
446 DEBUG ((DEBUG_INFO, "SmbiosAdd: Total length exceeds max 64-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize));
447 } else {
448 DEBUG ((DEBUG_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 64-bit table\n", Record->Type, StructureSize));
449 Smbios64BitTable = TRUE;
450 }
451 }
452
453 if ((!Smbios32BitTable) && (!Smbios64BitTable)) {
454 //
455 // If both 32-bit and 64-bit table are not updated, quit
456 //
457 return EFI_OUT_OF_RESOURCES;
458 }
459
460 //
461 // Enter into critical section
462 //
463 Status = EfiAcquireLockOrFail (&Private->DataLock);
464 if (EFI_ERROR (Status)) {
465 return Status;
466 }
467
468 RecordSize = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize;
469 TotalSize = sizeof (EFI_SMBIOS_ENTRY) + RecordSize;
470
471 //
472 // Allocate internal buffer
473 //
474 SmbiosEntry = AllocateZeroPool (TotalSize);
475 if (SmbiosEntry == NULL) {
476 EfiReleaseLock (&Private->DataLock);
477 return EFI_OUT_OF_RESOURCES;
478 }
479 HandleEntry = AllocateZeroPool (sizeof(SMBIOS_HANDLE_ENTRY));
480 if (HandleEntry == NULL) {
481 EfiReleaseLock (&Private->DataLock);
482 return EFI_OUT_OF_RESOURCES;
483 }
484
485 //
486 // Build Handle Entry and insert into linked list
487 //
488 HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE;
489 HandleEntry->SmbiosHandle = *SmbiosHandle;
490 InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link);
491
492 InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (SmbiosEntry + 1);
493 Raw = (VOID *) (InternalRecord + 1);
494
495 //
496 // Build internal record Header
497 //
498 InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION;
499 InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
500 InternalRecord->RecordSize = RecordSize;
501 InternalRecord->ProducerHandle = ProducerHandle;
502 InternalRecord->NumberOfStrings = NumberOfStrings;
503 //
504 // Insert record into the internal linked list
505 //
506 SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE;
507 SmbiosEntry->RecordHeader = InternalRecord;
508 SmbiosEntry->RecordSize = TotalSize;
509 SmbiosEntry->Smbios32BitTable = Smbios32BitTable;
510 SmbiosEntry->Smbios64BitTable = Smbios64BitTable;
511 InsertTailList (&Private->DataListHead, &SmbiosEntry->Link);
512
513 CopyMem (Raw, Record, StructureSize);
514 ((EFI_SMBIOS_TABLE_HEADER*)Raw)->Handle = *SmbiosHandle;
515
516 //
517 // Some UEFI drivers (such as network) need some information in SMBIOS table.
518 // Here we create SMBIOS table and publish it in
519 // configuration table, so other UEFI drivers can get SMBIOS table from
520 // configuration table without depending on PI SMBIOS protocol.
521 //
522 SmbiosTableConstruction (Smbios32BitTable, Smbios64BitTable);
523
524 //
525 // Leave critical section
526 //
527 EfiReleaseLock (&Private->DataLock);
528 return EFI_SUCCESS;
529 }
530
531 /**
532 Update the string associated with an existing SMBIOS record.
533
534 @param This The EFI_SMBIOS_PROTOCOL instance.
535 @param SmbiosHandle SMBIOS Handle of structure that will have its string updated.
536 @param StringNumber The non-zero string number of the string to update
537 @param String Update the StringNumber string with String.
538
539 @retval EFI_SUCCESS SmbiosHandle had its StringNumber String updated.
540 @retval EFI_INVALID_PARAMETER SmbiosHandle does not exist.
541 @retval EFI_UNSUPPORTED String was not added because it is longer than the SMBIOS Table supports.
542 @retval EFI_NOT_FOUND The StringNumber.is not valid for this SMBIOS record.
543
544 **/
545 EFI_STATUS
546 EFIAPI
547 SmbiosUpdateString (
548 IN CONST EFI_SMBIOS_PROTOCOL *This,
549 IN EFI_SMBIOS_HANDLE *SmbiosHandle,
550 IN UINTN *StringNumber,
551 IN CHAR8 *String
552 )
553 {
554 UINTN InputStrLen;
555 UINTN TargetStrLen;
556 UINTN StrIndex;
557 UINTN TargetStrOffset;
558 UINTN NewEntrySize;
559 CHAR8 *StrStart;
560 VOID *Raw;
561 LIST_ENTRY *Link;
562 LIST_ENTRY *Head;
563 EFI_STATUS Status;
564 SMBIOS_INSTANCE *Private;
565 EFI_SMBIOS_ENTRY *SmbiosEntry;
566 EFI_SMBIOS_ENTRY *ResizedSmbiosEntry;
567 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
568 EFI_SMBIOS_TABLE_HEADER *Record;
569 EFI_SMBIOS_RECORD_HEADER *InternalRecord;
570
571 //
572 // Check args validity
573 //
574 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
575
576 if (*SmbiosHandle > MaxSmbiosHandle) {
577 return EFI_INVALID_PARAMETER;
578 }
579
580 if (String == NULL) {
581 return EFI_ABORTED;
582 }
583
584 if (*StringNumber == 0) {
585 return EFI_NOT_FOUND;
586 }
587
588 InputStrLen = AsciiStrLen(String);
589
590 if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)) {
591 if (InputStrLen > SMBIOS_STRING_MAX_LENGTH) {
592 return EFI_UNSUPPORTED;
593 }
594 } else if (This->MajorVersion < 3) {
595 //
596 // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
597 // However, the length of the entire structure table (including all strings) must be reported
598 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
599 // which is a WORD field limited to 65,535 bytes.
600 //
601 if (InputStrLen > SMBIOS_TABLE_MAX_LENGTH) {
602 return EFI_UNSUPPORTED;
603 }
604 } else {
605 if (InputStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH) {
606 //
607 // SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes.
608 // The input string length should not exceed 0xFFFFFFFF bytes.
609 //
610 return EFI_UNSUPPORTED;
611 }
612 }
613
614 Private = SMBIOS_INSTANCE_FROM_THIS (This);
615 //
616 // Enter into critical section
617 //
618 Status = EfiAcquireLockOrFail (&Private->DataLock);
619 if (EFI_ERROR (Status)) {
620 return Status;
621 }
622
623 Head = &Private->DataListHead;
624 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
625 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
626 Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
627
628 if (Record->Handle == *SmbiosHandle) {
629 //
630 // Find out the specified SMBIOS record
631 //
632 if (*StringNumber > SmbiosEntry->RecordHeader->NumberOfStrings) {
633 EfiReleaseLock (&Private->DataLock);
634 return EFI_NOT_FOUND;
635 }
636 //
637 // Point to unformed string section
638 //
639 StrStart = (CHAR8 *) Record + Record->Length;
640
641 for (StrIndex = 1, TargetStrOffset = 0; StrIndex < *StringNumber; StrStart++, TargetStrOffset++) {
642 //
643 // A string ends in 00h
644 //
645 if (*StrStart == 0) {
646 StrIndex++;
647 }
648
649 //
650 // String section ends in double-null (0000h)
651 //
652 if (*StrStart == 0 && *(StrStart + 1) == 0) {
653 EfiReleaseLock (&Private->DataLock);
654 return EFI_NOT_FOUND;
655 }
656 }
657
658 if (*StrStart == 0) {
659 StrStart++;
660 TargetStrOffset++;
661 }
662
663 //
664 // Now we get the string target
665 //
666 TargetStrLen = AsciiStrLen(StrStart);
667 if (InputStrLen == TargetStrLen) {
668 AsciiStrCpyS(StrStart, TargetStrLen + 1, String);
669 //
670 // Some UEFI drivers (such as network) need some information in SMBIOS table.
671 // Here we create SMBIOS table and publish it in
672 // configuration table, so other UEFI drivers can get SMBIOS table from
673 // configuration table without depending on PI SMBIOS protocol.
674 //
675 SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable);
676 EfiReleaseLock (&Private->DataLock);
677 return EFI_SUCCESS;
678 }
679
680 SmbiosEntry->Smbios32BitTable = FALSE;
681 SmbiosEntry->Smbios64BitTable = FALSE;
682 if ((This->MajorVersion < 0x3) ||
683 ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) {
684 //
685 // 32-bit table is produced, check the valid length.
686 //
687 if ((EntryPointStructure != NULL) &&
688 (EntryPointStructure->TableLength + InputStrLen - TargetStrLen > SMBIOS_TABLE_MAX_LENGTH)) {
689 //
690 // The length of the entire structure table (including all strings) must be reported
691 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
692 // which is a WORD field limited to 65,535 bytes.
693 //
694 DEBUG ((DEBUG_INFO, "SmbiosUpdateString: Total length exceeds max 32-bit table length\n"));
695 } else {
696 DEBUG ((DEBUG_INFO, "SmbiosUpdateString: New smbios record add to 32-bit table\n"));
697 SmbiosEntry->Smbios32BitTable = TRUE;
698 }
699 }
700
701 if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) {
702 //
703 // 64-bit table is produced, check the valid length.
704 //
705 if ((Smbios30EntryPointStructure != NULL) &&
706 (Smbios30EntryPointStructure->TableMaximumSize + InputStrLen - TargetStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH)) {
707 DEBUG ((DEBUG_INFO, "SmbiosUpdateString: Total length exceeds max 64-bit table length\n"));
708 } else {
709 DEBUG ((DEBUG_INFO, "SmbiosUpdateString: New smbios record add to 64-bit table\n"));
710 SmbiosEntry->Smbios64BitTable = TRUE;
711 }
712 }
713
714 if ((!SmbiosEntry->Smbios32BitTable) && (!SmbiosEntry->Smbios64BitTable)) {
715 EfiReleaseLock (&Private->DataLock);
716 return EFI_UNSUPPORTED;
717 }
718
719 //
720 // Original string buffer size is not exactly match input string length.
721 // Re-allocate buffer is needed.
722 //
723 NewEntrySize = SmbiosEntry->RecordSize + InputStrLen - TargetStrLen;
724 ResizedSmbiosEntry = AllocateZeroPool (NewEntrySize);
725
726 if (ResizedSmbiosEntry == NULL) {
727 EfiReleaseLock (&Private->DataLock);
728 return EFI_OUT_OF_RESOURCES;
729 }
730
731 InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (ResizedSmbiosEntry + 1);
732 Raw = (VOID *) (InternalRecord + 1);
733
734 //
735 // Build internal record Header
736 //
737 InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION;
738 InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
739 InternalRecord->RecordSize = SmbiosEntry->RecordHeader->RecordSize + InputStrLen - TargetStrLen;
740 InternalRecord->ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
741 InternalRecord->NumberOfStrings = SmbiosEntry->RecordHeader->NumberOfStrings;
742
743 //
744 // Copy SMBIOS structure and optional strings.
745 //
746 CopyMem (Raw, SmbiosEntry->RecordHeader + 1, Record->Length + TargetStrOffset);
747 CopyMem ((VOID*)((UINTN)Raw + Record->Length + TargetStrOffset), String, InputStrLen + 1);
748 CopyMem ((CHAR8*)((UINTN)Raw + Record->Length + TargetStrOffset + InputStrLen + 1),
749 (CHAR8*)Record + Record->Length + TargetStrOffset + TargetStrLen + 1,
750 SmbiosEntry->RecordHeader->RecordSize - sizeof (EFI_SMBIOS_RECORD_HEADER) - Record->Length - TargetStrOffset - TargetStrLen - 1);
751
752 //
753 // Insert new record
754 //
755 ResizedSmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE;
756 ResizedSmbiosEntry->RecordHeader = InternalRecord;
757 ResizedSmbiosEntry->RecordSize = NewEntrySize;
758 ResizedSmbiosEntry->Smbios32BitTable = SmbiosEntry->Smbios32BitTable;
759 ResizedSmbiosEntry->Smbios64BitTable = SmbiosEntry->Smbios64BitTable;
760 InsertTailList (Link->ForwardLink, &ResizedSmbiosEntry->Link);
761
762 //
763 // Remove old record
764 //
765 RemoveEntryList(Link);
766 FreePool(SmbiosEntry);
767 //
768 // Some UEFI drivers (such as network) need some information in SMBIOS table.
769 // Here we create SMBIOS table and publish it in
770 // configuration table, so other UEFI drivers can get SMBIOS table from
771 // configuration table without depending on PI SMBIOS protocol.
772 //
773 SmbiosTableConstruction (ResizedSmbiosEntry->Smbios32BitTable, ResizedSmbiosEntry->Smbios64BitTable);
774 EfiReleaseLock (&Private->DataLock);
775 return EFI_SUCCESS;
776 }
777 }
778
779 EfiReleaseLock (&Private->DataLock);
780 return EFI_INVALID_PARAMETER;
781 }
782
783 /**
784 Remove an SMBIOS record.
785
786 @param This The EFI_SMBIOS_PROTOCOL instance.
787 @param SmbiosHandle The handle of the SMBIOS record to remove.
788
789 @retval EFI_SUCCESS SMBIOS record was removed.
790 @retval EFI_INVALID_PARAMETER SmbiosHandle does not specify a valid SMBIOS record.
791
792 **/
793 EFI_STATUS
794 EFIAPI
795 SmbiosRemove (
796 IN CONST EFI_SMBIOS_PROTOCOL *This,
797 IN EFI_SMBIOS_HANDLE SmbiosHandle
798 )
799 {
800 LIST_ENTRY *Link;
801 LIST_ENTRY *Head;
802 EFI_STATUS Status;
803 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
804 SMBIOS_INSTANCE *Private;
805 EFI_SMBIOS_ENTRY *SmbiosEntry;
806 SMBIOS_HANDLE_ENTRY *HandleEntry;
807 EFI_SMBIOS_TABLE_HEADER *Record;
808
809 //
810 // Check args validity
811 //
812 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
813
814 if (SmbiosHandle > MaxSmbiosHandle) {
815 return EFI_INVALID_PARAMETER;
816 }
817
818 Private = SMBIOS_INSTANCE_FROM_THIS (This);
819 //
820 // Enter into critical section
821 //
822 Status = EfiAcquireLockOrFail (&Private->DataLock);
823 if (EFI_ERROR (Status)) {
824 return Status;
825 }
826
827 Head = &Private->DataListHead;
828 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
829 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
830 Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
831 if (Record->Handle == SmbiosHandle) {
832 //
833 // Remove specified smobios record from DataList
834 //
835 RemoveEntryList(Link);
836 //
837 // Remove this handle from AllocatedHandleList
838 //
839 Head = &Private->AllocatedHandleListHead;
840 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
841 HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
842 if (HandleEntry->SmbiosHandle == SmbiosHandle) {
843 RemoveEntryList(Link);
844 FreePool(HandleEntry);
845 break;
846 }
847 }
848 //
849 // Some UEFI drivers (such as network) need some information in SMBIOS table.
850 // Here we create SMBIOS table and publish it in
851 // configuration table, so other UEFI drivers can get SMBIOS table from
852 // configuration table without depending on PI SMBIOS protocol.
853 //
854 if (SmbiosEntry->Smbios32BitTable) {
855 DEBUG ((DEBUG_INFO, "SmbiosRemove: remove from 32-bit table\n"));
856 }
857 if (SmbiosEntry->Smbios64BitTable) {
858 DEBUG ((DEBUG_INFO, "SmbiosRemove: remove from 64-bit table\n"));
859 }
860 //
861 // Update the whole SMBIOS table again based on which table the removed SMBIOS record is in.
862 //
863 SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable);
864 FreePool(SmbiosEntry);
865 EfiReleaseLock (&Private->DataLock);
866 return EFI_SUCCESS;
867 }
868 }
869
870 //
871 // Leave critical section
872 //
873 EfiReleaseLock (&Private->DataLock);
874 return EFI_INVALID_PARAMETER;
875
876 }
877
878 /**
879 Allow the caller to discover all or some of the SMBIOS records.
880
881 @param This The EFI_SMBIOS_PROTOCOL instance.
882 @param SmbiosHandle On entry, points to the previous handle of the SMBIOS record. On exit, points to the
883 next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record
884 handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS records.
885 @param Type On entry it means return the next SMBIOS record of type Type. If a NULL is passed in
886 this functionally it ignored. Type is not modified by the GetNext() function.
887 @param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by
888 the unformatted area. The unformatted area optionally contains text strings.
889 @param ProducerHandle On exit, points to the ProducerHandle registered by Add(). If no ProducerHandle was passed into Add() NULL is returned.
890 If a NULL pointer is passed in no data will be returned
891
892 @retval EFI_SUCCESS SMBIOS record information was successfully returned in Record.
893 @retval EFI_NOT_FOUND The SMBIOS record with SmbiosHandle was the last available record.
894
895 **/
896 EFI_STATUS
897 EFIAPI
898 SmbiosGetNext (
899 IN CONST EFI_SMBIOS_PROTOCOL *This,
900 IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
901 IN EFI_SMBIOS_TYPE *Type, OPTIONAL
902 OUT EFI_SMBIOS_TABLE_HEADER **Record,
903 OUT EFI_HANDLE *ProducerHandle OPTIONAL
904 )
905 {
906 BOOLEAN StartPointFound;
907 LIST_ENTRY *Link;
908 LIST_ENTRY *Head;
909 SMBIOS_INSTANCE *Private;
910 EFI_SMBIOS_ENTRY *SmbiosEntry;
911 EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader;
912
913 if (SmbiosHandle == NULL) {
914 return EFI_INVALID_PARAMETER;
915 }
916
917 StartPointFound = FALSE;
918 Private = SMBIOS_INSTANCE_FROM_THIS (This);
919 Head = &Private->DataListHead;
920 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
921 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
922 SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
923
924 //
925 // If SmbiosHandle is 0xFFFE, the first matched SMBIOS record handle will be returned
926 //
927 if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
928 if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
929 continue;
930 }
931
932 *SmbiosHandle = SmbiosTableHeader->Handle;
933 *Record =SmbiosTableHeader;
934 if (ProducerHandle != NULL) {
935 *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
936 }
937 return EFI_SUCCESS;
938 }
939
940 //
941 // Start this round search from the next SMBIOS handle
942 //
943 if (!StartPointFound && (*SmbiosHandle == SmbiosTableHeader->Handle)) {
944 StartPointFound = TRUE;
945 continue;
946 }
947
948 if (StartPointFound) {
949 if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
950 continue;
951 }
952
953 *SmbiosHandle = SmbiosTableHeader->Handle;
954 *Record = SmbiosTableHeader;
955 if (ProducerHandle != NULL) {
956 *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
957 }
958
959 return EFI_SUCCESS;
960 }
961 }
962
963 *SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
964 return EFI_NOT_FOUND;
965
966 }
967
968 /**
969 Allow the caller to discover all of the SMBIOS records.
970
971 @param This The EFI_SMBIOS_PROTOCOL instance.
972 @param CurrentSmbiosEntry On exit, points to the SMBIOS entry on the list which includes the returned SMBIOS record information.
973 If *CurrentSmbiosEntry is NULL on entry, then the first SMBIOS entry on the list will be returned.
974 @param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by
975 the unformatted area. The unformatted area optionally contains text strings.
976
977 @retval EFI_SUCCESS SMBIOS record information was successfully returned in Record.
978 *CurrentSmbiosEntry points to the SMBIOS entry which includes the returned SMBIOS record information.
979 @retval EFI_NOT_FOUND There is no more SMBIOS entry.
980
981 **/
982 EFI_STATUS
983 EFIAPI
984 GetNextSmbiosRecord (
985 IN CONST EFI_SMBIOS_PROTOCOL *This,
986 IN OUT EFI_SMBIOS_ENTRY **CurrentSmbiosEntry,
987 OUT EFI_SMBIOS_TABLE_HEADER **Record
988 )
989 {
990 LIST_ENTRY *Link;
991 LIST_ENTRY *Head;
992 SMBIOS_INSTANCE *Private;
993 EFI_SMBIOS_ENTRY *SmbiosEntry;
994 EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader;
995
996 Private = SMBIOS_INSTANCE_FROM_THIS (This);
997 if (*CurrentSmbiosEntry == NULL) {
998 //
999 // Get the beginning of SMBIOS entry.
1000 //
1001 Head = &Private->DataListHead;
1002 } else {
1003 //
1004 // Get previous SMBIOS entry and make it as start point.
1005 //
1006 Head = &(*CurrentSmbiosEntry)->Link;
1007 }
1008
1009 Link = Head->ForwardLink;
1010
1011 if (Link == &Private->DataListHead) {
1012 //
1013 // If no more SMBIOS entry in the list, return not found.
1014 //
1015 return EFI_NOT_FOUND;
1016 }
1017
1018 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
1019 SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
1020 *Record = SmbiosTableHeader;
1021 *CurrentSmbiosEntry = SmbiosEntry;
1022 return EFI_SUCCESS;
1023 }
1024
1025 /**
1026 Assembles SMBIOS table from the SMBIOS protocol. Produce Table
1027 Entry Point and return the pointer to it.
1028
1029 @param TableEntryPointStructure On exit, points to the SMBIOS entrypoint structure.
1030
1031 @retval EFI_SUCCESS Structure created sucessfully.
1032 @retval EFI_OUT_OF_RESOURCES No enough memory.
1033
1034 **/
1035 EFI_STATUS
1036 EFIAPI
1037 SmbiosCreateTable (
1038 OUT VOID **TableEntryPointStructure
1039 )
1040 {
1041 UINT8 *BufferPointer;
1042 UINTN RecordSize;
1043 UINTN NumOfStr;
1044 EFI_STATUS Status;
1045 EFI_SMBIOS_HANDLE SmbiosHandle;
1046 EFI_SMBIOS_PROTOCOL *SmbiosProtocol;
1047 EFI_PHYSICAL_ADDRESS PhysicalAddress;
1048 EFI_SMBIOS_TABLE_HEADER *SmbiosRecord;
1049 EFI_SMBIOS_TABLE_END_STRUCTURE EndStructure;
1050 EFI_SMBIOS_ENTRY *CurrentSmbiosEntry;
1051
1052 Status = EFI_SUCCESS;
1053 BufferPointer = NULL;
1054
1055 if (EntryPointStructure == NULL) {
1056 //
1057 // Initialize the EntryPointStructure with initial values.
1058 // It should be done only once.
1059 // Allocate memory (below 4GB).
1060 //
1061 DEBUG ((DEBUG_INFO, "SmbiosCreateTable: Initialize 32-bit entry point structure\n"));
1062 EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion;
1063 EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion;
1064 EntryPointStructureData.SmbiosBcdRevision = (UINT8) ((PcdGet16 (PcdSmbiosVersion) >> 4) & 0xf0) | (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x0f);
1065 PhysicalAddress = 0xffffffff;
1066 Status = gBS->AllocatePages (
1067 AllocateMaxAddress,
1068 EfiRuntimeServicesData,
1069 EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1070 &PhysicalAddress
1071 );
1072 if (EFI_ERROR (Status)) {
1073 DEBUG ((DEBUG_ERROR, "SmbiosCreateTable () could not allocate EntryPointStructure < 4GB\n"));
1074 Status = gBS->AllocatePages (
1075 AllocateAnyPages,
1076 EfiRuntimeServicesData,
1077 EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1078 &PhysicalAddress
1079 );
1080 if (EFI_ERROR (Status)) {
1081 return EFI_OUT_OF_RESOURCES;
1082 }
1083 }
1084
1085 EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) (UINTN) PhysicalAddress;
1086
1087 CopyMem (
1088 EntryPointStructure,
1089 &EntryPointStructureData,
1090 sizeof (SMBIOS_TABLE_ENTRY_POINT)
1091 );
1092 }
1093
1094 //
1095 // Get Smbios protocol to traverse SMBIOS records.
1096 //
1097 SmbiosProtocol = &mPrivateData.Smbios;
1098
1099 //
1100 // Make some statistics about all the structures
1101 //
1102 EntryPointStructure->NumberOfSmbiosStructures = 0;
1103 EntryPointStructure->TableLength = 0;
1104 EntryPointStructure->MaxStructureSize = 0;
1105
1106 //
1107 // Calculate EPS Table Length
1108 //
1109 CurrentSmbiosEntry = NULL;
1110 do {
1111 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1112
1113 if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) {
1114 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1115 //
1116 // Record NumberOfSmbiosStructures, TableLength and MaxStructureSize
1117 //
1118 EntryPointStructure->NumberOfSmbiosStructures++;
1119 EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + RecordSize);
1120 if (RecordSize > EntryPointStructure->MaxStructureSize) {
1121 EntryPointStructure->MaxStructureSize = (UINT16) RecordSize;
1122 }
1123 }
1124 } while (!EFI_ERROR(Status));
1125
1126 //
1127 // Create End-Of-Table structure
1128 //
1129 GetMaxSmbiosHandle(SmbiosProtocol, &SmbiosHandle);
1130 EndStructure.Header.Type = SMBIOS_TYPE_END_OF_TABLE;
1131 EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
1132 EndStructure.Header.Handle = SmbiosHandle;
1133 EndStructure.Tailing[0] = 0;
1134 EndStructure.Tailing[1] = 0;
1135 EntryPointStructure->NumberOfSmbiosStructures++;
1136 EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + sizeof (EndStructure));
1137 if (sizeof (EndStructure) > EntryPointStructure->MaxStructureSize) {
1138 EntryPointStructure->MaxStructureSize = (UINT16) sizeof (EndStructure);
1139 }
1140
1141 if (EFI_SIZE_TO_PAGES ((UINT32) EntryPointStructure->TableLength) > mPreAllocatedPages) {
1142 //
1143 // If new SMBIOS table size exceeds the previous allocated page,
1144 // it is time to re-allocate memory (below 4GB).
1145 //
1146 DEBUG ((DEBUG_INFO, "%a() re-allocate SMBIOS 32-bit table\n",
1147 __FUNCTION__));
1148 if (EntryPointStructure->TableAddress != 0) {
1149 //
1150 // Free the previous allocated page
1151 //
1152 FreePages (
1153 (VOID*)(UINTN)EntryPointStructure->TableAddress,
1154 mPreAllocatedPages
1155 );
1156 EntryPointStructure->TableAddress = 0;
1157 mPreAllocatedPages = 0;
1158 }
1159
1160 PhysicalAddress = 0xffffffff;
1161 Status = gBS->AllocatePages (
1162 AllocateMaxAddress,
1163 EfiRuntimeServicesData,
1164 EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength),
1165 &PhysicalAddress
1166 );
1167 if (EFI_ERROR (Status)) {
1168 DEBUG ((DEBUG_ERROR, "SmbiosCreateTable() could not allocate SMBIOS table < 4GB\n"));
1169 EntryPointStructure->TableAddress = 0;
1170 return EFI_OUT_OF_RESOURCES;
1171 } else {
1172 EntryPointStructure->TableAddress = (UINT32) PhysicalAddress;
1173 mPreAllocatedPages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
1174 }
1175 }
1176
1177 //
1178 // Assemble the tables
1179 //
1180 ASSERT (EntryPointStructure->TableAddress != 0);
1181 BufferPointer = (UINT8 *) (UINTN) EntryPointStructure->TableAddress;
1182 CurrentSmbiosEntry = NULL;
1183 do {
1184 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1185
1186 if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) {
1187 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1188 CopyMem (BufferPointer, SmbiosRecord, RecordSize);
1189 BufferPointer = BufferPointer + RecordSize;
1190 }
1191 } while (!EFI_ERROR(Status));
1192
1193 //
1194 // Assemble End-Of-Table structure
1195 //
1196 CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure));
1197
1198 //
1199 // Fixup checksums in the Entry Point Structure
1200 //
1201 EntryPointStructure->IntermediateChecksum = 0;
1202 EntryPointStructure->EntryPointStructureChecksum = 0;
1203
1204 EntryPointStructure->IntermediateChecksum =
1205 CalculateCheckSum8 ((UINT8 *) EntryPointStructure + 0x10, EntryPointStructure->EntryPointLength - 0x10);
1206 EntryPointStructure->EntryPointStructureChecksum =
1207 CalculateCheckSum8 ((UINT8 *) EntryPointStructure, EntryPointStructure->EntryPointLength);
1208
1209 //
1210 // Returns the pointer
1211 //
1212 *TableEntryPointStructure = EntryPointStructure;
1213
1214 return EFI_SUCCESS;
1215 }
1216
1217 /**
1218 Assembles SMBIOS 64-bit table from the SMBIOS protocol. Produce Table
1219 Entry Point and return the pointer to it.
1220
1221 @param TableEntryPointStructure On exit, points to the SMBIOS entrypoint structure.
1222
1223 @retval EFI_SUCCESS Structure created sucessfully.
1224 @retval EFI_OUT_OF_RESOURCES No enough memory.
1225
1226 **/
1227 EFI_STATUS
1228 EFIAPI
1229 SmbiosCreate64BitTable (
1230 OUT VOID **TableEntryPointStructure
1231 )
1232 {
1233 UINT8 *BufferPointer;
1234 UINTN RecordSize;
1235 UINTN NumOfStr;
1236 EFI_STATUS Status;
1237 EFI_SMBIOS_HANDLE SmbiosHandle;
1238 EFI_SMBIOS_PROTOCOL *SmbiosProtocol;
1239 EFI_PHYSICAL_ADDRESS PhysicalAddress;
1240 EFI_SMBIOS_TABLE_HEADER *SmbiosRecord;
1241 EFI_SMBIOS_TABLE_END_STRUCTURE EndStructure;
1242 EFI_SMBIOS_ENTRY *CurrentSmbiosEntry;
1243
1244 Status = EFI_SUCCESS;
1245 BufferPointer = NULL;
1246
1247 if (Smbios30EntryPointStructure == NULL) {
1248 //
1249 // Initialize the Smbios30EntryPointStructure with initial values.
1250 // It should be done only once.
1251 // Allocate memory at any address.
1252 //
1253 DEBUG ((DEBUG_INFO, "SmbiosCreateTable: Initialize 64-bit entry point structure\n"));
1254 Smbios30EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion;
1255 Smbios30EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion;
1256 Smbios30EntryPointStructureData.DocRev = PcdGet8 (PcdSmbiosDocRev);
1257 Status = gBS->AllocatePages (
1258 AllocateAnyPages,
1259 EfiRuntimeServicesData,
1260 EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)),
1261 &PhysicalAddress
1262 );
1263 if (EFI_ERROR (Status)) {
1264 DEBUG ((DEBUG_ERROR, "SmbiosCreate64BitTable() could not allocate Smbios30EntryPointStructure\n"));
1265 return EFI_OUT_OF_RESOURCES;
1266 }
1267
1268 Smbios30EntryPointStructure = (SMBIOS_TABLE_3_0_ENTRY_POINT *) (UINTN) PhysicalAddress;
1269
1270 CopyMem (
1271 Smbios30EntryPointStructure,
1272 &Smbios30EntryPointStructureData,
1273 sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)
1274 );
1275 }
1276
1277 //
1278 // Get Smbios protocol to traverse SMBIOS records.
1279 //
1280 SmbiosProtocol = &mPrivateData.Smbios;
1281 Smbios30EntryPointStructure->TableMaximumSize = 0;
1282
1283 //
1284 // Calculate EPS Table Length
1285 //
1286 CurrentSmbiosEntry = NULL;
1287 do {
1288 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1289
1290 if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) {
1291 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1292 //
1293 // Record TableMaximumSize
1294 //
1295 Smbios30EntryPointStructure->TableMaximumSize = (UINT32) (Smbios30EntryPointStructure->TableMaximumSize + RecordSize);
1296 }
1297 } while (!EFI_ERROR(Status));
1298
1299 //
1300 // Create End-Of-Table structure
1301 //
1302 GetMaxSmbiosHandle(SmbiosProtocol, &SmbiosHandle);
1303 EndStructure.Header.Type = SMBIOS_TYPE_END_OF_TABLE;
1304 EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
1305 EndStructure.Header.Handle = SmbiosHandle;
1306 EndStructure.Tailing[0] = 0;
1307 EndStructure.Tailing[1] = 0;
1308 Smbios30EntryPointStructure->TableMaximumSize = (UINT32) (Smbios30EntryPointStructure->TableMaximumSize + sizeof (EndStructure));
1309
1310 if (EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize) > mPre64BitAllocatedPages) {
1311 //
1312 // If new SMBIOS table size exceeds the previous allocated page,
1313 // it is time to re-allocate memory at anywhere.
1314 //
1315 DEBUG ((DEBUG_INFO, "%a() re-allocate SMBIOS 64-bit table\n",
1316 __FUNCTION__));
1317 if (Smbios30EntryPointStructure->TableAddress != 0) {
1318 //
1319 // Free the previous allocated page
1320 //
1321 FreePages (
1322 (VOID*)(UINTN)Smbios30EntryPointStructure->TableAddress,
1323 mPre64BitAllocatedPages
1324 );
1325 Smbios30EntryPointStructure->TableAddress = 0;
1326 mPre64BitAllocatedPages = 0;
1327 }
1328
1329 Status = gBS->AllocatePages (
1330 AllocateAnyPages,
1331 EfiRuntimeServicesData,
1332 EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize),
1333 &PhysicalAddress
1334 );
1335 if (EFI_ERROR (Status)) {
1336 DEBUG ((DEBUG_ERROR, "SmbiosCreateTable() could not allocate SMBIOS 64-bit table\n"));
1337 Smbios30EntryPointStructure->TableAddress = 0;
1338 return EFI_OUT_OF_RESOURCES;
1339 } else {
1340 Smbios30EntryPointStructure->TableAddress = PhysicalAddress;
1341 mPre64BitAllocatedPages = EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize);
1342 }
1343 }
1344
1345 //
1346 // Assemble the tables
1347 //
1348 ASSERT (Smbios30EntryPointStructure->TableAddress != 0);
1349 BufferPointer = (UINT8 *) (UINTN) Smbios30EntryPointStructure->TableAddress;
1350 CurrentSmbiosEntry = NULL;
1351 do {
1352 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1353
1354 if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) {
1355 //
1356 // This record can be added to 64-bit table
1357 //
1358 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1359 CopyMem (BufferPointer, SmbiosRecord, RecordSize);
1360 BufferPointer = BufferPointer + RecordSize;
1361 }
1362 } while (!EFI_ERROR(Status));
1363
1364 //
1365 // Assemble End-Of-Table structure
1366 //
1367 CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure));
1368
1369 //
1370 // Fixup checksums in the Entry Point Structure
1371 //
1372 Smbios30EntryPointStructure->EntryPointStructureChecksum = 0;
1373 Smbios30EntryPointStructure->EntryPointStructureChecksum =
1374 CalculateCheckSum8 ((UINT8 *) Smbios30EntryPointStructure, Smbios30EntryPointStructure->EntryPointLength);
1375
1376 //
1377 // Returns the pointer
1378 //
1379 *TableEntryPointStructure = Smbios30EntryPointStructure;
1380
1381 return EFI_SUCCESS;
1382 }
1383
1384 /**
1385 Create Smbios Table and installs the Smbios Table to the System Table.
1386
1387 @param Smbios32BitTable The flag to update 32-bit table.
1388 @param Smbios64BitTable The flag to update 64-bit table.
1389
1390 **/
1391 VOID
1392 EFIAPI
1393 SmbiosTableConstruction (
1394 BOOLEAN Smbios32BitTable,
1395 BOOLEAN Smbios64BitTable
1396 )
1397 {
1398 UINT8 *Eps;
1399 UINT8 *Eps64Bit;
1400 EFI_STATUS Status;
1401
1402 if (Smbios32BitTable) {
1403 Status = SmbiosCreateTable ((VOID **) &Eps);
1404 if (!EFI_ERROR (Status)) {
1405 gBS->InstallConfigurationTable (&gEfiSmbiosTableGuid, Eps);
1406 }
1407 }
1408
1409 if (Smbios64BitTable) {
1410 Status = SmbiosCreate64BitTable ((VOID **) &Eps64Bit);
1411 if (!EFI_ERROR (Status)) {
1412 gBS->InstallConfigurationTable (&gEfiSmbios3TableGuid, Eps64Bit);
1413 }
1414 }
1415 }
1416
1417 /**
1418 Validates a SMBIOS 2.0 table entry point.
1419
1420 @param TableEntry The SmBios table entry to validate.
1421 @param TableAddress On exit, point to the smbios table addres.
1422 @param TableMaximumSize On exit, point to the maximum size of the table.
1423
1424 @retval TRUE SMBIOS table entry point is valid.
1425 @retval FALSE SMBIOS table entry point is malformed.
1426
1427 **/
1428 STATIC
1429 BOOLEAN
1430 IsValidSmbios20Table (
1431 IN VOID *TableEntry,
1432 OUT VOID **TableAddress,
1433 OUT UINTN *TableMaximumSize
1434 )
1435 {
1436 UINT8 Checksum;
1437 SMBIOS_TABLE_ENTRY_POINT *SmbiosTable;
1438 SmbiosTable = (SMBIOS_TABLE_ENTRY_POINT *) TableEntry;
1439
1440 if (CompareMem (SmbiosTable->AnchorString, "_SM_", 4) != 0) {
1441 return FALSE;
1442 }
1443
1444 if (CompareMem (SmbiosTable->IntermediateAnchorString, "_DMI_", 5) != 0) {
1445 return FALSE;
1446 }
1447
1448 //
1449 // The actual value of the EntryPointLength should be 1Fh.
1450 // However, it was incorrectly stated in version 2.1 of smbios specification.
1451 // Therefore, 0x1F and 0x1E are both accepted.
1452 //
1453 if (SmbiosTable->EntryPointLength != 0x1E && SmbiosTable->EntryPointLength != sizeof (SMBIOS_TABLE_ENTRY_POINT)) {
1454 return FALSE;
1455 }
1456
1457 //
1458 // MajorVersion should not be less than 2.
1459 //
1460 if (SmbiosTable->MajorVersion < 2) {
1461 return FALSE;
1462 }
1463
1464 //
1465 // The whole struct check sum should be zero
1466 //
1467 Checksum = CalculateSum8 (
1468 (UINT8 *) SmbiosTable,
1469 SmbiosTable->EntryPointLength
1470 );
1471 if (Checksum != 0) {
1472 return FALSE;
1473 }
1474
1475 //
1476 // The Intermediate Entry Point Structure check sum should be zero.
1477 //
1478 Checksum = CalculateSum8 (
1479 (UINT8 *) SmbiosTable + OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString),
1480 SmbiosTable->EntryPointLength - OFFSET_OF (SMBIOS_TABLE_ENTRY_POINT, IntermediateAnchorString)
1481 );
1482 if (Checksum != 0) {
1483 return FALSE;
1484 }
1485
1486 *TableAddress = (VOID *) (UINTN) SmbiosTable->TableAddress;
1487 *TableMaximumSize = SmbiosTable->TableLength;
1488 return TRUE;
1489 }
1490
1491 /**
1492 Validates a SMBIOS 3.0 table entry point.
1493
1494 @param TableEntry The SmBios table entry to validate.
1495 @param TableAddress On exit, point to the smbios table addres.
1496 @param TableMaximumSize On exit, point to the maximum size of the table.
1497
1498 @retval TRUE SMBIOS table entry point is valid.
1499 @retval FALSE SMBIOS table entry point is malformed.
1500
1501 **/
1502 STATIC
1503 BOOLEAN
1504 IsValidSmbios30Table (
1505 IN VOID *TableEntry,
1506 OUT VOID **TableAddress,
1507 OUT UINTN *TableMaximumSize
1508 )
1509 {
1510 UINT8 Checksum;
1511 SMBIOS_TABLE_3_0_ENTRY_POINT *SmbiosTable;
1512 SmbiosTable = (SMBIOS_TABLE_3_0_ENTRY_POINT *) TableEntry;
1513
1514 if (CompareMem (SmbiosTable->AnchorString, "_SM3_", 5) != 0) {
1515 return FALSE;
1516 }
1517 if (SmbiosTable->EntryPointLength < sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)) {
1518 return FALSE;
1519 }
1520 if (SmbiosTable->MajorVersion < 3) {
1521 return FALSE;
1522 }
1523
1524 //
1525 // The whole struct check sum should be zero
1526 //
1527 Checksum = CalculateSum8 (
1528 (UINT8 *) SmbiosTable,
1529 SmbiosTable->EntryPointLength
1530 );
1531 if (Checksum != 0) {
1532 return FALSE;
1533 }
1534
1535 *TableAddress = (VOID *) (UINTN) SmbiosTable->TableAddress;
1536 *TableMaximumSize = SmbiosTable->TableMaximumSize;
1537 return TRUE;
1538 }
1539
1540 /**
1541 Parse an existing SMBIOS table and insert it using SmbiosAdd.
1542
1543 @param ImageHandle The EFI_HANDLE to this driver.
1544 @param Smbios The SMBIOS table to parse.
1545 @param Length The length of the SMBIOS table.
1546
1547 @retval EFI_SUCCESS SMBIOS table was parsed and installed.
1548 @retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources.
1549 @retval EFI_INVALID_PARAMETER Smbios is not a correct smbios table
1550
1551 **/
1552 STATIC
1553 EFI_STATUS
1554 ParseAndAddExistingSmbiosTable (
1555 IN EFI_HANDLE ImageHandle,
1556 IN SMBIOS_STRUCTURE_POINTER Smbios,
1557 IN UINTN Length
1558 )
1559 {
1560 EFI_STATUS Status;
1561 CHAR8 *String;
1562 EFI_SMBIOS_HANDLE SmbiosHandle;
1563 SMBIOS_STRUCTURE_POINTER SmbiosEnd;
1564
1565 SmbiosEnd.Raw = Smbios.Raw + Length;
1566
1567 if (Smbios.Raw >= SmbiosEnd.Raw || Smbios.Raw == NULL) {
1568 return EFI_INVALID_PARAMETER;
1569 }
1570
1571 do {
1572 //
1573 // Make sure not to access memory beyond SmbiosEnd
1574 //
1575 if (Smbios.Raw + sizeof (SMBIOS_STRUCTURE) > SmbiosEnd.Raw ||
1576 Smbios.Raw + sizeof (SMBIOS_STRUCTURE) < Smbios.Raw) {
1577 return EFI_INVALID_PARAMETER;
1578 }
1579 //
1580 // Check for end marker
1581 //
1582 if (Smbios.Hdr->Type == SMBIOS_TYPE_END_OF_TABLE) {
1583 break;
1584 }
1585 //
1586 // Make sure not to access memory beyond SmbiosEnd
1587 // Each structure shall be terminated by a double-null (0000h).
1588 //
1589 if (Smbios.Raw + Smbios.Hdr->Length + 2 * sizeof (UINT8) > SmbiosEnd.Raw ||
1590 Smbios.Raw + Smbios.Hdr->Length + 2 * sizeof (UINT8) < Smbios.Raw) {
1591 return EFI_INVALID_PARAMETER;
1592 }
1593 //
1594 // Install the table
1595 //
1596 SmbiosHandle = Smbios.Hdr->Handle;
1597 Status = SmbiosAdd (
1598 &mPrivateData.Smbios,
1599 ImageHandle,
1600 &SmbiosHandle,
1601 Smbios.Hdr
1602 );
1603
1604 ASSERT_EFI_ERROR (Status);
1605 if (EFI_ERROR (Status)) {
1606 return Status;
1607 }
1608 //
1609 // Go to the next SMBIOS structure. Each SMBIOS structure may include 2 parts:
1610 // 1. Formatted section; 2. Unformatted string section. So, 2 steps are needed
1611 // to skip one SMBIOS structure.
1612 //
1613
1614 //
1615 // Step 1: Skip over formatted section.
1616 //
1617 String = (CHAR8 *) (Smbios.Raw + Smbios.Hdr->Length);
1618
1619 //
1620 // Step 2: Skip over unformatted string section.
1621 //
1622 do {
1623 //
1624 // Each string is terminated with a NULL(00h) BYTE and the sets of strings
1625 // is terminated with an additional NULL(00h) BYTE.
1626 //
1627 for ( ; *String != 0; String++) {
1628 if ((UINTN) String >= (UINTN) SmbiosEnd.Raw - sizeof (UINT8)) {
1629 return EFI_INVALID_PARAMETER;
1630 }
1631 }
1632
1633 if (*(UINT8 *) ++String == 0) {
1634 //
1635 // Pointer to the next SMBIOS structure.
1636 //
1637 Smbios.Raw = (UINT8 *) ++String;
1638 break;
1639 }
1640 } while (TRUE);
1641 } while (Smbios.Raw < SmbiosEnd.Raw);
1642
1643 return EFI_SUCCESS;
1644 }
1645
1646 /**
1647 Retrieve SMBIOS from Hob.
1648 @param ImageHandle Module's image handle
1649
1650 @retval EFI_SUCCESS Smbios from Hob is installed.
1651 @return EFI_NOT_FOUND Not found Smbios from Hob.
1652 @retval Other No Smbios from Hob is installed.
1653
1654 **/
1655 EFI_STATUS
1656 RetrieveSmbiosFromHob (
1657 IN EFI_HANDLE ImageHandle
1658 )
1659 {
1660 EFI_STATUS Status;
1661 UINTN Index;
1662 SMBIOS_STRUCTURE_POINTER Smbios;
1663 EFI_HOB_GUID_TYPE *GuidHob;
1664 UNIVERSAL_PAYLOAD_SMBIOS_TABLE *SmBiosTableAdress;
1665 UNIVERSAL_PAYLOAD_GENERIC_HEADER *GenericHeader;
1666 VOID *TableAddress;
1667 UINTN TableMaximumSize;
1668
1669 Status = EFI_NOT_FOUND;
1670
1671 for (Index = 0; Index < ARRAY_SIZE (mIsSmbiosTableValid); Index++) {
1672 GuidHob = GetFirstGuidHob (mIsSmbiosTableValid[Index].Guid);
1673 if (GuidHob == NULL) {
1674 continue;
1675 }
1676 GenericHeader = (UNIVERSAL_PAYLOAD_GENERIC_HEADER *) GET_GUID_HOB_DATA (GuidHob);
1677 if ((sizeof (UNIVERSAL_PAYLOAD_GENERIC_HEADER) <= GET_GUID_HOB_DATA_SIZE (GuidHob)) && (GenericHeader->Length <= GET_GUID_HOB_DATA_SIZE (GuidHob))) {
1678 if (GenericHeader->Revision == UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION) {
1679 //
1680 // UNIVERSAL_PAYLOAD_SMBIOS_TABLE structure is used when Revision equals to UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION
1681 //
1682 SmBiosTableAdress = (UNIVERSAL_PAYLOAD_SMBIOS_TABLE *) GET_GUID_HOB_DATA (GuidHob);
1683 if (GenericHeader->Length >= UNIVERSAL_PAYLOAD_SIZEOF_THROUGH_FIELD (UNIVERSAL_PAYLOAD_SMBIOS_TABLE, SmBiosEntryPoint)) {
1684 if (mIsSmbiosTableValid[Index].IsValid ((VOID *) (UINTN )SmBiosTableAdress->SmBiosEntryPoint, &TableAddress, &TableMaximumSize)) {
1685 Smbios.Raw = TableAddress;
1686 Status = ParseAndAddExistingSmbiosTable (ImageHandle, Smbios, TableMaximumSize);
1687 if (EFI_ERROR (Status)) {
1688 DEBUG ((DEBUG_ERROR, "RetrieveSmbiosFromHob: Failed to parse preinstalled tables from Guid Hob\n"));
1689 Status = EFI_UNSUPPORTED;
1690 } else {
1691 return EFI_SUCCESS;
1692 }
1693 }
1694 }
1695 }
1696 }
1697 }
1698 return Status;
1699 }
1700
1701 /**
1702
1703 Driver to produce Smbios protocol and pre-allocate 1 page for the final SMBIOS table.
1704
1705 @param ImageHandle Module's image handle
1706 @param SystemTable Pointer of EFI_SYSTEM_TABLE
1707
1708 @retval EFI_SUCCESS Smbios protocol installed
1709 @retval Other No protocol installed, unload driver.
1710
1711 **/
1712 EFI_STATUS
1713 EFIAPI
1714 SmbiosDriverEntryPoint (
1715 IN EFI_HANDLE ImageHandle,
1716 IN EFI_SYSTEM_TABLE *SystemTable
1717 )
1718 {
1719 EFI_STATUS Status;
1720
1721 mPrivateData.Signature = SMBIOS_INSTANCE_SIGNATURE;
1722 mPrivateData.Smbios.Add = SmbiosAdd;
1723 mPrivateData.Smbios.UpdateString = SmbiosUpdateString;
1724 mPrivateData.Smbios.Remove = SmbiosRemove;
1725 mPrivateData.Smbios.GetNext = SmbiosGetNext;
1726 mPrivateData.Smbios.MajorVersion = (UINT8) (PcdGet16 (PcdSmbiosVersion) >> 8);
1727 mPrivateData.Smbios.MinorVersion = (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x00ff);
1728
1729 InitializeListHead (&mPrivateData.DataListHead);
1730 InitializeListHead (&mPrivateData.AllocatedHandleListHead);
1731 EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY);
1732
1733 //
1734 // Make a new handle and install the protocol
1735 //
1736 mPrivateData.Handle = NULL;
1737 Status = gBS->InstallProtocolInterface (
1738 &mPrivateData.Handle,
1739 &gEfiSmbiosProtocolGuid,
1740 EFI_NATIVE_INTERFACE,
1741 &mPrivateData.Smbios
1742 );
1743
1744 RetrieveSmbiosFromHob (ImageHandle);
1745 return Status;
1746 }