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