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