]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.c
MdeModulePkg/IntelFrameworkModulePkg: Update PeiCore, SmbiosDxe and IsaSerialDxe...
[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 - 2014, Intel Corporation. All rights reserved.<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 "SmbiosDxe.h"
17
18 //
19 // Module Global:
20 // Since this driver will only ever produce one instance of the
21 // protocol you are not required to dynamically allocate the PrivateData.
22 //
23 SMBIOS_INSTANCE mPrivateData;
24
25 UINTN mPreAllocatedPages = 0;
26
27 //
28 // Chassis for SMBIOS entry point structure that is to be installed into EFI system config table.
29 //
30 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure = NULL;
31 SMBIOS_TABLE_ENTRY_POINT EntryPointStructureData = {
32 //
33 // AnchorString
34 //
35 {
36 0x5f,
37 0x53,
38 0x4d,
39 0x5f
40 },
41 //
42 // EntryPointStructureChecksum,TO BE FILLED
43 //
44 0,
45 //
46 // EntryPointStructure Length
47 //
48 0x1f,
49 //
50 // MajorVersion
51 //
52 0,
53 //
54 // MinorVersion
55 //
56 0,
57 //
58 // MaxStructureSize, TO BE FILLED
59 //
60 0,
61 //
62 // EntryPointRevision
63 //
64 0,
65 //
66 // FormattedArea
67 //
68 {
69 0,
70 0,
71 0,
72 0,
73 0
74 },
75 //
76 // IntermediateAnchorString
77 //
78 {
79 0x5f,
80 0x44,
81 0x4d,
82 0x49,
83 0x5f
84 },
85 //
86 // IntermediateChecksum, TO BE FILLED
87 //
88 0,
89 //
90 // TableLength, TO BE FILLED
91 //
92 0,
93 //
94 // TableAddress, TO BE FILLED
95 //
96 0,
97 //
98 // NumberOfSmbiosStructures, TO BE FILLED
99 //
100 0,
101 //
102 // SmbiosBcdRevision
103 //
104 0
105 };
106
107
108
109 /**
110
111 Get the full size of SMBIOS structure including optional strings that follow the formatted structure.
112
113 @param This The EFI_SMBIOS_PROTOCOL instance.
114 @param Head Pointer to the beginning of SMBIOS structure.
115 @param Size The returned size.
116 @param NumberOfStrings The returned number of optional strings that follow the formatted structure.
117
118 @retval EFI_SUCCESS Size retured in Size.
119 @retval EFI_INVALID_PARAMETER Input SMBIOS structure mal-formed or Size is NULL.
120
121 **/
122 EFI_STATUS
123 EFIAPI
124 GetSmbiosStructureSize (
125 IN CONST EFI_SMBIOS_PROTOCOL *This,
126 IN EFI_SMBIOS_TABLE_HEADER *Head,
127 OUT UINTN *Size,
128 OUT UINTN *NumberOfStrings
129 )
130 {
131 UINTN FullSize;
132 UINTN StrLen;
133 UINTN MaxLen;
134 INT8* CharInStr;
135
136 if (Size == NULL || NumberOfStrings == NULL) {
137 return EFI_INVALID_PARAMETER;
138 }
139
140 FullSize = Head->Length;
141 CharInStr = (INT8*)Head + Head->Length;
142 *Size = FullSize;
143 *NumberOfStrings = 0;
144 StrLen = 0;
145 //
146 // look for the two consecutive zeros, check the string limit by the way.
147 //
148 while (*CharInStr != 0 || *(CharInStr+1) != 0) {
149 if (*CharInStr == 0) {
150 *Size += 1;
151 CharInStr++;
152 }
153
154 if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)){
155 MaxLen = SMBIOS_STRING_MAX_LENGTH;
156 } else {
157 //
158 // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
159 // However, the length of the entire structure table (including all strings) must be reported
160 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
161 // which is a WORD field limited to 65,535 bytes.
162 //
163 MaxLen = SMBIOS_TABLE_MAX_LENGTH;
164 }
165
166 for (StrLen = 0 ; StrLen < MaxLen; StrLen++) {
167 if (*(CharInStr+StrLen) == 0) {
168 break;
169 }
170 }
171
172 if (StrLen == MaxLen) {
173 return EFI_INVALID_PARAMETER;
174 }
175
176 //
177 // forward the pointer
178 //
179 CharInStr += StrLen;
180 *Size += StrLen;
181 *NumberOfStrings += 1;
182 }
183
184 //
185 // count ending two zeros.
186 //
187 *Size += 2;
188 return EFI_SUCCESS;
189 }
190
191 /**
192
193 Determin whether an SmbiosHandle has already in use.
194
195 @param Head Pointer to the beginning of SMBIOS structure.
196 @param Handle A unique handle will be assigned to the SMBIOS record.
197
198 @retval TRUE Smbios handle already in use.
199 @retval FALSE Smbios handle is NOT used.
200
201 **/
202 BOOLEAN
203 EFIAPI
204 CheckSmbiosHandleExistance (
205 IN LIST_ENTRY *Head,
206 IN EFI_SMBIOS_HANDLE Handle
207 )
208 {
209 LIST_ENTRY *Link;
210 SMBIOS_HANDLE_ENTRY *HandleEntry;
211
212 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
213 HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
214 if (HandleEntry->SmbiosHandle == Handle) {
215 return TRUE;
216 }
217 }
218
219 return FALSE;
220 }
221
222 /**
223
224 Get the max SmbiosHandle that could be use.
225
226 @param This The EFI_SMBIOS_PROTOCOL instance.
227 @param MaxHandle The max handle that could be assigned to the SMBIOS record.
228
229 **/
230 VOID
231 EFIAPI
232 GetMaxSmbiosHandle (
233 IN CONST EFI_SMBIOS_PROTOCOL *This,
234 IN OUT EFI_SMBIOS_HANDLE *MaxHandle
235 )
236 {
237 if (This->MajorVersion == 2 && This->MinorVersion == 0) {
238 *MaxHandle = 0xFFFE;
239 } else {
240 *MaxHandle = 0xFEFF;
241 }
242 }
243
244 /**
245
246 Get an SmbiosHandle that could use.
247
248 @param This The EFI_SMBIOS_PROTOCOL instance.
249 @param SmbiosHandle A unique handle will be assigned to the SMBIOS record.
250
251 @retval EFI_SUCCESS Smbios handle got.
252 @retval EFI_OUT_OF_RESOURCES Smbios handle is NOT available.
253
254 **/
255 EFI_STATUS
256 EFIAPI
257 GetAvailableSmbiosHandle (
258 IN CONST EFI_SMBIOS_PROTOCOL *This,
259 IN OUT EFI_SMBIOS_HANDLE *Handle
260 )
261 {
262 LIST_ENTRY *Head;
263 SMBIOS_INSTANCE *Private;
264 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
265 EFI_SMBIOS_HANDLE AvailableHandle;
266
267 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
268
269 Private = SMBIOS_INSTANCE_FROM_THIS (This);
270 Head = &Private->AllocatedHandleListHead;
271 for (AvailableHandle = 0; AvailableHandle < MaxSmbiosHandle; AvailableHandle++) {
272 if (!CheckSmbiosHandleExistance(Head, AvailableHandle)) {
273 *Handle = AvailableHandle;
274 return EFI_SUCCESS;
275 }
276 }
277
278 return EFI_OUT_OF_RESOURCES;
279 }
280
281
282 /**
283 Add an SMBIOS record.
284
285 @param This The EFI_SMBIOS_PROTOCOL instance.
286 @param ProducerHandle The handle of the controller or driver associated with the SMBIOS information. NULL
287 means no handle.
288 @param SmbiosHandle On entry, the handle of the SMBIOS record to add. If FFFEh, then a unique handle
289 will be assigned to the SMBIOS record. If the SMBIOS handle is already in use,
290 EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated.
291 @param Record The data for the fixed portion of the SMBIOS record. The format of the record is
292 determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined
293 by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or
294 a set of null terminated strings and a null.
295
296 @retval EFI_SUCCESS Record was added.
297 @retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources.
298 @retval EFI_ALREADY_STARTED The SmbiosHandle passed in was already in use.
299
300 **/
301 EFI_STATUS
302 EFIAPI
303 SmbiosAdd (
304 IN CONST EFI_SMBIOS_PROTOCOL *This,
305 IN EFI_HANDLE ProducerHandle, OPTIONAL
306 IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
307 IN EFI_SMBIOS_TABLE_HEADER *Record
308 )
309 {
310 VOID *Raw;
311 UINTN TotalSize;
312 UINTN RecordSize;
313 UINTN StructureSize;
314 UINTN NumberOfStrings;
315 EFI_STATUS Status;
316 LIST_ENTRY *Head;
317 SMBIOS_INSTANCE *Private;
318 EFI_SMBIOS_ENTRY *SmbiosEntry;
319 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
320 SMBIOS_HANDLE_ENTRY *HandleEntry;
321 EFI_SMBIOS_RECORD_HEADER *InternalRecord;
322
323 if (SmbiosHandle == NULL) {
324 return EFI_INVALID_PARAMETER;
325 }
326
327 Private = SMBIOS_INSTANCE_FROM_THIS (This);
328 //
329 // Check whether SmbiosHandle is already in use
330 //
331 Head = &Private->AllocatedHandleListHead;
332 if (*SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED && CheckSmbiosHandleExistance(Head, *SmbiosHandle)) {
333 return EFI_ALREADY_STARTED;
334 }
335
336 //
337 // when SmbiosHandle is 0xFFFE, an available handle will be assigned
338 //
339 if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
340 Status = GetAvailableSmbiosHandle(This, SmbiosHandle);
341 if (EFI_ERROR(Status)) {
342 return Status;
343 }
344 } else {
345 //
346 // Check this handle validity
347 //
348 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
349 if (*SmbiosHandle > MaxSmbiosHandle) {
350 return EFI_INVALID_PARAMETER;
351 }
352 }
353
354 //
355 // Calculate record size and string number
356 //
357 Status = GetSmbiosStructureSize(This, Record, &StructureSize, &NumberOfStrings);
358 if (EFI_ERROR(Status)) {
359 return Status;
360 }
361
362 if (EntryPointStructure->TableLength + StructureSize > SMBIOS_TABLE_MAX_LENGTH) {
363 //
364 // The length of the entire structure table (including all strings) must be reported
365 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
366 // which is a WORD field limited to 65,535 bytes.
367 //
368 return EFI_OUT_OF_RESOURCES;
369 }
370
371 //
372 // Enter into critical section
373 //
374 Status = EfiAcquireLockOrFail (&Private->DataLock);
375 if (EFI_ERROR (Status)) {
376 return Status;
377 }
378
379 RecordSize = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize;
380 TotalSize = sizeof (EFI_SMBIOS_ENTRY) + RecordSize;
381
382 //
383 // Allocate internal buffer
384 //
385 SmbiosEntry = AllocateZeroPool (TotalSize);
386 if (SmbiosEntry == NULL) {
387 EfiReleaseLock (&Private->DataLock);
388 return EFI_OUT_OF_RESOURCES;
389 }
390 HandleEntry = AllocateZeroPool (sizeof(SMBIOS_HANDLE_ENTRY));
391 if (HandleEntry == NULL) {
392 EfiReleaseLock (&Private->DataLock);
393 return EFI_OUT_OF_RESOURCES;
394 }
395
396 //
397 // Build Handle Entry and insert into linked list
398 //
399 HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE;
400 HandleEntry->SmbiosHandle = *SmbiosHandle;
401 InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link);
402
403 InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (SmbiosEntry + 1);
404 Raw = (VOID *) (InternalRecord + 1);
405
406 //
407 // Build internal record Header
408 //
409 InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION;
410 InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
411 InternalRecord->RecordSize = RecordSize;
412 InternalRecord->ProducerHandle = ProducerHandle;
413 InternalRecord->NumberOfStrings = NumberOfStrings;
414 //
415 // Insert record into the internal linked list
416 //
417 SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE;
418 SmbiosEntry->RecordHeader = InternalRecord;
419 SmbiosEntry->RecordSize = TotalSize;
420 InsertTailList (&Private->DataListHead, &SmbiosEntry->Link);
421
422 CopyMem (Raw, Record, StructureSize);
423 ((EFI_SMBIOS_TABLE_HEADER*)Raw)->Handle = *SmbiosHandle;
424
425 //
426 // Some UEFI drivers (such as network) need some information in SMBIOS table.
427 // Here we create SMBIOS table and publish it in
428 // configuration table, so other UEFI drivers can get SMBIOS table from
429 // configuration table without depending on PI SMBIOS protocol.
430 //
431 SmbiosTableConstruction ();
432
433 //
434 // Leave critical section
435 //
436 EfiReleaseLock (&Private->DataLock);
437 return EFI_SUCCESS;
438 }
439
440 /**
441 Update the string associated with an existing SMBIOS record.
442
443 @param This The EFI_SMBIOS_PROTOCOL instance.
444 @param SmbiosHandle SMBIOS Handle of structure that will have its string updated.
445 @param StringNumber The non-zero string number of the string to update
446 @param String Update the StringNumber string with String.
447
448 @retval EFI_SUCCESS SmbiosHandle had its StringNumber String updated.
449 @retval EFI_INVALID_PARAMETER SmbiosHandle does not exist.
450 @retval EFI_UNSUPPORTED String was not added because it is longer than the SMBIOS Table supports.
451 @retval EFI_NOT_FOUND The StringNumber.is not valid for this SMBIOS record.
452
453 **/
454 EFI_STATUS
455 EFIAPI
456 SmbiosUpdateString (
457 IN CONST EFI_SMBIOS_PROTOCOL *This,
458 IN EFI_SMBIOS_HANDLE *SmbiosHandle,
459 IN UINTN *StringNumber,
460 IN CHAR8 *String
461 )
462 {
463 UINTN InputStrLen;
464 UINTN TargetStrLen;
465 UINTN StrIndex;
466 UINTN TargetStrOffset;
467 UINTN NewEntrySize;
468 CHAR8 *StrStart;
469 VOID *Raw;
470 LIST_ENTRY *Link;
471 LIST_ENTRY *Head;
472 EFI_STATUS Status;
473 SMBIOS_INSTANCE *Private;
474 EFI_SMBIOS_ENTRY *SmbiosEntry;
475 EFI_SMBIOS_ENTRY *ResizedSmbiosEntry;
476 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
477 EFI_SMBIOS_TABLE_HEADER *Record;
478 EFI_SMBIOS_RECORD_HEADER *InternalRecord;
479
480 //
481 // Check args validity
482 //
483 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
484
485 if (*SmbiosHandle > MaxSmbiosHandle) {
486 return EFI_INVALID_PARAMETER;
487 }
488
489 if (String == NULL) {
490 return EFI_ABORTED;
491 }
492
493 if (*StringNumber == 0) {
494 return EFI_NOT_FOUND;
495 }
496
497 InputStrLen = AsciiStrLen(String);
498
499 if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)) {
500 if (InputStrLen > SMBIOS_STRING_MAX_LENGTH) {
501 return EFI_UNSUPPORTED;
502 }
503 } else {
504 //
505 // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
506 // However, the length of the entire structure table (including all strings) must be reported
507 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
508 // which is a WORD field limited to 65,535 bytes.
509 //
510 if (InputStrLen > SMBIOS_TABLE_MAX_LENGTH) {
511 return EFI_UNSUPPORTED;
512 }
513 }
514
515 Private = SMBIOS_INSTANCE_FROM_THIS (This);
516 //
517 // Enter into critical section
518 //
519 Status = EfiAcquireLockOrFail (&Private->DataLock);
520 if (EFI_ERROR (Status)) {
521 return Status;
522 }
523
524 Head = &Private->DataListHead;
525 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
526 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
527 Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
528
529 if (Record->Handle == *SmbiosHandle) {
530 //
531 // Find out the specified SMBIOS record
532 //
533 if (*StringNumber > SmbiosEntry->RecordHeader->NumberOfStrings) {
534 EfiReleaseLock (&Private->DataLock);
535 return EFI_NOT_FOUND;
536 }
537 //
538 // Point to unformed string section
539 //
540 StrStart = (CHAR8 *) Record + Record->Length;
541
542 for (StrIndex = 1, TargetStrOffset = 0; StrIndex < *StringNumber; StrStart++, TargetStrOffset++) {
543 //
544 // A string ends in 00h
545 //
546 if (*StrStart == 0) {
547 StrIndex++;
548 }
549
550 //
551 // String section ends in double-null (0000h)
552 //
553 if (*StrStart == 0 && *(StrStart + 1) == 0) {
554 EfiReleaseLock (&Private->DataLock);
555 return EFI_NOT_FOUND;
556 }
557 }
558
559 if (*StrStart == 0) {
560 StrStart++;
561 TargetStrOffset++;
562 }
563
564 //
565 // Now we get the string target
566 //
567 TargetStrLen = AsciiStrLen(StrStart);
568 if (InputStrLen == TargetStrLen) {
569 AsciiStrCpy(StrStart, String);
570 //
571 // Some UEFI drivers (such as network) need some information in SMBIOS table.
572 // Here we create SMBIOS table and publish it in
573 // configuration table, so other UEFI drivers can get SMBIOS table from
574 // configuration table without depending on PI SMBIOS protocol.
575 //
576 SmbiosTableConstruction ();
577 EfiReleaseLock (&Private->DataLock);
578 return EFI_SUCCESS;
579 }
580
581 if (EntryPointStructure->TableLength + InputStrLen - TargetStrLen > SMBIOS_TABLE_MAX_LENGTH) {
582 //
583 // The length of the entire structure table (including all strings) must be reported
584 // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
585 // which is a WORD field limited to 65,535 bytes.
586 //
587 return EFI_UNSUPPORTED;
588 }
589
590 //
591 // Original string buffer size is not exactly match input string length.
592 // Re-allocate buffer is needed.
593 //
594 NewEntrySize = SmbiosEntry->RecordSize + InputStrLen - TargetStrLen;
595 ResizedSmbiosEntry = AllocateZeroPool (NewEntrySize);
596
597 if (ResizedSmbiosEntry == NULL) {
598 EfiReleaseLock (&Private->DataLock);
599 return EFI_OUT_OF_RESOURCES;
600 }
601
602 InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (ResizedSmbiosEntry + 1);
603 Raw = (VOID *) (InternalRecord + 1);
604
605 //
606 // Build internal record Header
607 //
608 InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION;
609 InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
610 InternalRecord->RecordSize = SmbiosEntry->RecordHeader->RecordSize + InputStrLen - TargetStrLen;
611 InternalRecord->ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
612 InternalRecord->NumberOfStrings = SmbiosEntry->RecordHeader->NumberOfStrings;
613
614 //
615 // Copy SMBIOS structure and optional strings.
616 //
617 CopyMem (Raw, SmbiosEntry->RecordHeader + 1, Record->Length + TargetStrOffset);
618 CopyMem ((VOID*)((UINTN)Raw + Record->Length + TargetStrOffset), String, InputStrLen + 1);
619 CopyMem ((CHAR8*)((UINTN)Raw + Record->Length + TargetStrOffset + InputStrLen + 1),
620 (CHAR8*)Record + Record->Length + TargetStrOffset + TargetStrLen + 1,
621 SmbiosEntry->RecordHeader->RecordSize - sizeof (EFI_SMBIOS_RECORD_HEADER) - Record->Length - TargetStrOffset - TargetStrLen - 1);
622
623 //
624 // Insert new record
625 //
626 ResizedSmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE;
627 ResizedSmbiosEntry->RecordHeader = InternalRecord;
628 ResizedSmbiosEntry->RecordSize = NewEntrySize;
629 InsertTailList (Link->ForwardLink, &ResizedSmbiosEntry->Link);
630
631 //
632 // Remove old record
633 //
634 RemoveEntryList(Link);
635 FreePool(SmbiosEntry);
636 //
637 // Some UEFI drivers (such as network) need some information in SMBIOS table.
638 // Here we create SMBIOS table and publish it in
639 // configuration table, so other UEFI drivers can get SMBIOS table from
640 // configuration table without depending on PI SMBIOS protocol.
641 //
642 SmbiosTableConstruction ();
643 EfiReleaseLock (&Private->DataLock);
644 return EFI_SUCCESS;
645 }
646 }
647
648 EfiReleaseLock (&Private->DataLock);
649 return EFI_INVALID_PARAMETER;
650 }
651
652 /**
653 Remove an SMBIOS record.
654
655 @param This The EFI_SMBIOS_PROTOCOL instance.
656 @param SmbiosHandle The handle of the SMBIOS record to remove.
657
658 @retval EFI_SUCCESS SMBIOS record was removed.
659 @retval EFI_INVALID_PARAMETER SmbiosHandle does not specify a valid SMBIOS record.
660
661 **/
662 EFI_STATUS
663 EFIAPI
664 SmbiosRemove (
665 IN CONST EFI_SMBIOS_PROTOCOL *This,
666 IN EFI_SMBIOS_HANDLE SmbiosHandle
667 )
668 {
669 LIST_ENTRY *Link;
670 LIST_ENTRY *Head;
671 EFI_STATUS Status;
672 EFI_SMBIOS_HANDLE MaxSmbiosHandle;
673 SMBIOS_INSTANCE *Private;
674 EFI_SMBIOS_ENTRY *SmbiosEntry;
675 SMBIOS_HANDLE_ENTRY *HandleEntry;
676 EFI_SMBIOS_TABLE_HEADER *Record;
677
678 //
679 // Check args validity
680 //
681 GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
682
683 if (SmbiosHandle > MaxSmbiosHandle) {
684 return EFI_INVALID_PARAMETER;
685 }
686
687 Private = SMBIOS_INSTANCE_FROM_THIS (This);
688 //
689 // Enter into critical section
690 //
691 Status = EfiAcquireLockOrFail (&Private->DataLock);
692 if (EFI_ERROR (Status)) {
693 return Status;
694 }
695
696 Head = &Private->DataListHead;
697 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
698 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
699 Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
700 if (Record->Handle == SmbiosHandle) {
701 //
702 // Remove specified smobios record from DataList
703 //
704 RemoveEntryList(Link);
705 FreePool(SmbiosEntry);
706 //
707 // Remove this handle from AllocatedHandleList
708 //
709 Head = &Private->AllocatedHandleListHead;
710 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
711 HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
712 if (HandleEntry->SmbiosHandle == SmbiosHandle) {
713 RemoveEntryList(Link);
714 FreePool(HandleEntry);
715 break;
716 }
717 }
718 //
719 // Some UEFI drivers (such as network) need some information in SMBIOS table.
720 // Here we create SMBIOS table and publish it in
721 // configuration table, so other UEFI drivers can get SMBIOS table from
722 // configuration table without depending on PI SMBIOS protocol.
723 //
724 SmbiosTableConstruction ();
725 EfiReleaseLock (&Private->DataLock);
726 return EFI_SUCCESS;
727 }
728 }
729
730 //
731 // Leave critical section
732 //
733 EfiReleaseLock (&Private->DataLock);
734 return EFI_INVALID_PARAMETER;
735
736 }
737
738 /**
739 Allow the caller to discover all or some of the SMBIOS records.
740
741 @param This The EFI_SMBIOS_PROTOCOL instance.
742 @param SmbiosHandle On entry, points to the previous handle of the SMBIOS record. On exit, points to the
743 next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record
744 handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS records.
745 @param Type On entry it means return the next SMBIOS record of type Type. If a NULL is passed in
746 this functionally it ignored. Type is not modified by the GetNext() function.
747 @param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by
748 the unformatted area. The unformatted area optionally contains text strings.
749 @param ProducerHandle On exit, points to the ProducerHandle registered by Add(). If no ProducerHandle was passed into Add() NULL is returned.
750 If a NULL pointer is passed in no data will be returned
751
752 @retval EFI_SUCCESS SMBIOS record information was successfully returned in Record.
753 @retval EFI_NOT_FOUND The SMBIOS record with SmbiosHandle was the last available record.
754
755 **/
756 EFI_STATUS
757 EFIAPI
758 SmbiosGetNext (
759 IN CONST EFI_SMBIOS_PROTOCOL *This,
760 IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle,
761 IN EFI_SMBIOS_TYPE *Type, OPTIONAL
762 OUT EFI_SMBIOS_TABLE_HEADER **Record,
763 OUT EFI_HANDLE *ProducerHandle OPTIONAL
764 )
765 {
766 BOOLEAN StartPointFound;
767 LIST_ENTRY *Link;
768 LIST_ENTRY *Head;
769 SMBIOS_INSTANCE *Private;
770 EFI_SMBIOS_ENTRY *SmbiosEntry;
771 EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader;
772
773 if (SmbiosHandle == NULL) {
774 return EFI_INVALID_PARAMETER;
775 }
776
777 StartPointFound = FALSE;
778 Private = SMBIOS_INSTANCE_FROM_THIS (This);
779 Head = &Private->DataListHead;
780 for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
781 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
782 SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
783
784 //
785 // If SmbiosHandle is 0xFFFE, the first matched SMBIOS record handle will be returned
786 //
787 if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
788 if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
789 continue;
790 }
791
792 *SmbiosHandle = SmbiosTableHeader->Handle;
793 *Record =SmbiosTableHeader;
794 if (ProducerHandle != NULL) {
795 *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
796 }
797 return EFI_SUCCESS;
798 }
799
800 //
801 // Start this round search from the next SMBIOS handle
802 //
803 if (!StartPointFound && (*SmbiosHandle == SmbiosTableHeader->Handle)) {
804 StartPointFound = TRUE;
805 continue;
806 }
807
808 if (StartPointFound) {
809 if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
810 continue;
811 }
812
813 *SmbiosHandle = SmbiosTableHeader->Handle;
814 *Record = SmbiosTableHeader;
815 if (ProducerHandle != NULL) {
816 *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
817 }
818
819 return EFI_SUCCESS;
820 }
821 }
822
823 *SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
824 return EFI_NOT_FOUND;
825
826 }
827
828 /**
829 Allow the caller to discover all of the SMBIOS records.
830
831 @param This The EFI_SMBIOS_PROTOCOL instance.
832 @param CurrentSmbiosEntry On exit, points to the SMBIOS entry on the list which includes the returned SMBIOS record information.
833 If *CurrentSmbiosEntry is NULL on entry, then the first SMBIOS entry on the list will be returned.
834 @param Record On exit, points to the SMBIOS Record consisting of the formatted area followed by
835 the unformatted area. The unformatted area optionally contains text strings.
836
837 @retval EFI_SUCCESS SMBIOS record information was successfully returned in Record.
838 *CurrentSmbiosEntry points to the SMBIOS entry which includes the returned SMBIOS record information.
839 @retval EFI_NOT_FOUND There is no more SMBIOS entry.
840
841 **/
842 EFI_STATUS
843 EFIAPI
844 GetNextSmbiosRecord (
845 IN CONST EFI_SMBIOS_PROTOCOL *This,
846 IN OUT EFI_SMBIOS_ENTRY **CurrentSmbiosEntry,
847 OUT EFI_SMBIOS_TABLE_HEADER **Record
848 )
849 {
850 LIST_ENTRY *Link;
851 LIST_ENTRY *Head;
852 SMBIOS_INSTANCE *Private;
853 EFI_SMBIOS_ENTRY *SmbiosEntry;
854 EFI_SMBIOS_TABLE_HEADER *SmbiosTableHeader;
855
856 Private = SMBIOS_INSTANCE_FROM_THIS (This);
857 if (*CurrentSmbiosEntry == NULL) {
858 //
859 // Get the beginning of SMBIOS entry.
860 //
861 Head = &Private->DataListHead;
862 } else {
863 //
864 // Get previous SMBIOS entry and make it as start point.
865 //
866 Head = &(*CurrentSmbiosEntry)->Link;
867 }
868
869 Link = Head->ForwardLink;
870
871 if (Link == &Private->DataListHead) {
872 //
873 // If no more SMBIOS entry in the list, return not found.
874 //
875 return EFI_NOT_FOUND;
876 }
877
878 SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
879 SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
880 *Record = SmbiosTableHeader;
881 *CurrentSmbiosEntry = SmbiosEntry;
882 return EFI_SUCCESS;
883 }
884
885 /**
886 Assembles SMBIOS table from the SMBIOS protocol. Produce Table
887 Entry Point and return the pointer to it.
888
889 @param TableEntryPointStructure On exit, points to the SMBIOS entrypoint structure.
890
891 @retval EFI_SUCCESS Structure created sucessfully.
892 @retval EFI_NOT_READY Some of The SMBIOS records was not available yet.
893 @retval EFI_OUT_OF_RESOURCES No enough memory.
894
895 **/
896 EFI_STATUS
897 EFIAPI
898 SmbiosCreateTable (
899 OUT VOID **TableEntryPointStructure
900 )
901 {
902 UINT8 *BufferPointer;
903 UINTN RecordSize;
904 UINTN NumOfStr;
905 EFI_STATUS Status;
906 EFI_SMBIOS_HANDLE SmbiosHandle;
907 EFI_SMBIOS_PROTOCOL *SmbiosProtocol;
908 EFI_PHYSICAL_ADDRESS PhysicalAddress;
909 EFI_SMBIOS_TABLE_HEADER *SmbiosRecord;
910 EFI_SMBIOS_TABLE_END_STRUCTURE EndStructure;
911 EFI_SMBIOS_ENTRY *CurrentSmbiosEntry;
912
913 Status = EFI_SUCCESS;
914 BufferPointer = NULL;
915
916 //
917 // Get Smbios protocol to traverse SMBIOS records.
918 //
919 SmbiosProtocol = &mPrivateData.Smbios;
920
921 //
922 // Make some statistics about all the structures
923 //
924 EntryPointStructure->NumberOfSmbiosStructures = 0;
925 EntryPointStructure->TableLength = 0;
926 EntryPointStructure->MaxStructureSize = 0;
927
928 //
929 // Calculate EPS Table Length
930 //
931 CurrentSmbiosEntry = NULL;
932 do {
933 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
934
935 if (Status == EFI_SUCCESS) {
936 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
937 //
938 // Record NumberOfSmbiosStructures, TableLength and MaxStructureSize
939 //
940 EntryPointStructure->NumberOfSmbiosStructures++;
941 EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + RecordSize);
942 if (RecordSize > EntryPointStructure->MaxStructureSize) {
943 EntryPointStructure->MaxStructureSize = (UINT16) RecordSize;
944 }
945 }
946 } while (!EFI_ERROR(Status));
947
948 //
949 // Create End-Of-Table structure
950 //
951 GetMaxSmbiosHandle(SmbiosProtocol, &SmbiosHandle);
952 EndStructure.Header.Type = EFI_SMBIOS_TYPE_END_OF_TABLE;
953 EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
954 EndStructure.Header.Handle = SmbiosHandle;
955 EndStructure.Tailing[0] = 0;
956 EndStructure.Tailing[1] = 0;
957 EntryPointStructure->NumberOfSmbiosStructures++;
958 EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + sizeof (EndStructure));
959 if (sizeof (EndStructure) > EntryPointStructure->MaxStructureSize) {
960 EntryPointStructure->MaxStructureSize = (UINT16) sizeof (EndStructure);
961 }
962
963 if ((UINTN) EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength) > mPreAllocatedPages) {
964 //
965 // If new SMBIOS talbe size exceeds the original pre-allocated page,
966 // it is time to re-allocate memory (below 4GB).
967 //
968 if (EntryPointStructure->TableAddress != 0) {
969 //
970 // Free the original pre-allocated page
971 //
972 FreePages (
973 (VOID*)(UINTN)EntryPointStructure->TableAddress,
974 mPreAllocatedPages
975 );
976 EntryPointStructure->TableAddress = 0;
977 mPreAllocatedPages = 0;
978 }
979
980 PhysicalAddress = 0xffffffff;
981 Status = gBS->AllocatePages (
982 AllocateMaxAddress,
983 EfiRuntimeServicesData,
984 EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength),
985 &PhysicalAddress
986 );
987 if (EFI_ERROR (Status)) {
988 DEBUG ((EFI_D_ERROR, "SmbiosCreateTable() could not allocate SMBIOS table < 4GB\n"));
989 EntryPointStructure->TableAddress = 0;
990 return EFI_OUT_OF_RESOURCES;
991 } else {
992 EntryPointStructure->TableAddress = (UINT32) PhysicalAddress;
993 mPreAllocatedPages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
994 }
995 }
996
997 //
998 // Assemble the tables
999 //
1000 ASSERT (EntryPointStructure->TableAddress != 0);
1001 BufferPointer = (UINT8 *) (UINTN) EntryPointStructure->TableAddress;
1002 CurrentSmbiosEntry = NULL;
1003 do {
1004 Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1005
1006 if (Status == EFI_SUCCESS) {
1007 GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1008 CopyMem (BufferPointer, SmbiosRecord, RecordSize);
1009 BufferPointer = BufferPointer + RecordSize;
1010 }
1011 } while (!EFI_ERROR(Status));
1012
1013 //
1014 // Assemble End-Of-Table structure
1015 //
1016 CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure));
1017
1018 //
1019 // Fixup checksums in the Entry Point Structure
1020 //
1021 EntryPointStructure->IntermediateChecksum = 0;
1022 EntryPointStructure->EntryPointStructureChecksum = 0;
1023
1024 EntryPointStructure->IntermediateChecksum =
1025 CalculateCheckSum8 ((UINT8 *) EntryPointStructure + 0x10, EntryPointStructure->EntryPointLength - 0x10);
1026 EntryPointStructure->EntryPointStructureChecksum =
1027 CalculateCheckSum8 ((UINT8 *) EntryPointStructure, EntryPointStructure->EntryPointLength);
1028
1029 //
1030 // Returns the pointer
1031 //
1032 *TableEntryPointStructure = EntryPointStructure;
1033
1034 return EFI_SUCCESS;
1035 }
1036
1037 /**
1038 Create SMBIOS Table and install it to the System Table.
1039 **/
1040 VOID
1041 EFIAPI
1042 SmbiosTableConstruction (
1043 VOID
1044 )
1045 {
1046 UINT8 *Eps;
1047 EFI_STATUS Status;
1048
1049 Status = SmbiosCreateTable ((VOID **) &Eps);
1050 if (!EFI_ERROR (Status)) {
1051 gBS->InstallConfigurationTable (&gEfiSmbiosTableGuid, Eps);
1052 }
1053 }
1054
1055 /**
1056
1057 Driver to produce Smbios protocol and pre-allocate 1 page for the final SMBIOS table.
1058
1059 @param ImageHandle Module's image handle
1060 @param SystemTable Pointer of EFI_SYSTEM_TABLE
1061
1062 @retval EFI_SUCCESS Smbios protocol installed
1063 @retval Other No protocol installed, unload driver.
1064
1065 **/
1066 EFI_STATUS
1067 EFIAPI
1068 SmbiosDriverEntryPoint (
1069 IN EFI_HANDLE ImageHandle,
1070 IN EFI_SYSTEM_TABLE *SystemTable
1071 )
1072 {
1073 EFI_STATUS Status;
1074 EFI_PHYSICAL_ADDRESS PhysicalAddress;
1075
1076 mPrivateData.Signature = SMBIOS_INSTANCE_SIGNATURE;
1077 mPrivateData.Smbios.Add = SmbiosAdd;
1078 mPrivateData.Smbios.UpdateString = SmbiosUpdateString;
1079 mPrivateData.Smbios.Remove = SmbiosRemove;
1080 mPrivateData.Smbios.GetNext = SmbiosGetNext;
1081 mPrivateData.Smbios.MajorVersion = (UINT8) (PcdGet16 (PcdSmbiosVersion) >> 8);
1082 mPrivateData.Smbios.MinorVersion = (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x00ff);
1083 EntryPointStructureData.MajorVersion = mPrivateData.Smbios.MajorVersion;
1084 EntryPointStructureData.MinorVersion = mPrivateData.Smbios.MinorVersion;
1085 EntryPointStructureData.SmbiosBcdRevision = (UINT8) ((PcdGet16 (PcdSmbiosVersion) >> 4) & 0xf0) | (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x0f);
1086
1087 InitializeListHead (&mPrivateData.DataListHead);
1088 InitializeListHead (&mPrivateData.AllocatedHandleListHead);
1089 EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY);
1090
1091 //
1092 // Initialize the EntryPointStructure with initial values.
1093 // Allocate memory (below 4GB).
1094 //
1095 PhysicalAddress = 0xffffffff;
1096 Status = gBS->AllocatePages (
1097 AllocateMaxAddress,
1098 EfiRuntimeServicesData,
1099 EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1100 &PhysicalAddress
1101 );
1102 if (EFI_ERROR (Status)) {
1103 DEBUG ((EFI_D_ERROR, "SmbiosDriverEntryPoint() could not allocate EntryPointStructure < 4GB\n"));
1104 Status = gBS->AllocatePages (
1105 AllocateAnyPages,
1106 EfiRuntimeServicesData,
1107 EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1108 &PhysicalAddress
1109 );
1110 if (EFI_ERROR (Status)) {
1111 return EFI_OUT_OF_RESOURCES;
1112 }
1113 }
1114
1115 EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) (UINTN) PhysicalAddress;
1116
1117 CopyMem (
1118 EntryPointStructure,
1119 &EntryPointStructureData,
1120 sizeof (SMBIOS_TABLE_ENTRY_POINT)
1121 );
1122
1123 //
1124 // Pre-allocate 1 page for SMBIOS table below 4GB.
1125 // SMBIOS table will be updated when new SMBIOS type is added or
1126 // existing SMBIOS type is updated. If the total size of SMBIOS table exceeds 1 page,
1127 // we will re-allocate new memory when creating whole SMBIOS table.
1128 //
1129 PhysicalAddress = 0xffffffff;
1130 Status = gBS->AllocatePages (
1131 AllocateMaxAddress,
1132 EfiRuntimeServicesData,
1133 1,
1134 &PhysicalAddress
1135 );
1136 if (EFI_ERROR (Status)) {
1137 DEBUG ((EFI_D_ERROR, "SmbiosDriverEntryPoint() could not allocate SMBIOS table < 4GB\n"));
1138 EntryPointStructure->TableAddress = 0;
1139 } else {
1140 EntryPointStructure->TableAddress = (UINT32) PhysicalAddress;
1141 mPreAllocatedPages = 1;
1142 }
1143
1144 //
1145 // Init TableLength to the length of End-Of-Table structure for SmbiosAdd() called at the first time
1146 // to check the TableLength limitation.
1147 //
1148 EntryPointStructure->TableLength = sizeof (EFI_SMBIOS_TABLE_END_STRUCTURE);
1149
1150 //
1151 // Make a new handle and install the protocol
1152 //
1153 mPrivateData.Handle = NULL;
1154 Status = gBS->InstallProtocolInterface (
1155 &mPrivateData.Handle,
1156 &gEfiSmbiosProtocolGuid,
1157 EFI_NATIVE_INTERFACE,
1158 &mPrivateData.Smbios
1159 );
1160
1161 return Status;
1162 }