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