/** Update the structure buffer of a structure node in SMBIOS database. The function lead the structure pointer for SMBIOS record changed. @param StructureNode The structure node whose structure buffer is to be enlarged. @param NewRecord The new SMBIOS record. **/ VOID SmbiosUpdateStructureBuffer ( IN OUT SMBIOS_STRUCTURE_NODE *StructureNode, IN EFI_SMBIOS_TABLE_HEADER *NewRecord ) { EFI_SMBIOS_PROTOCOL *Smbios; EFI_STATUS Status; UINT8 CountOfString; Smbios = GetSmbiosProtocol(); ASSERT (Smbios != NULL); Status = Smbios->Remove (Smbios, StructureNode->SmbiosHandle); ASSERT_EFI_ERROR (Status); // // try to use original handle to enlarge the buffer. // Status = Smbios->Add (Smbios, NULL, &StructureNode->SmbiosHandle, NewRecord); ASSERT_EFI_ERROR (Status); StructureNode->Structure = GetSmbiosBufferFromHandle ( StructureNode->SmbiosHandle, StructureNode->SmbiosType, NULL ); GetSmbiosStructureSize ( StructureNode->Structure, &StructureNode->StructureSize, &CountOfString ); return ; }
/** Enlarge the structure buffer of a structure node in SMBIOS database. The function maybe lead the structure pointer for SMBIOS record changed. @param StructureNode The structure node whose structure buffer is to be enlarged. @param NewLength The new length of SMBIOS record which does not include unformat area. @param OldBufferSize The old size of SMBIOS record buffer. @param NewBufferSize The new size is targeted for enlarged. @retval EFI_OUT_OF_RESOURCES No more memory to allocate new record @retval EFI_SUCCESS Success to enlarge the record buffer size. **/ EFI_STATUS SmbiosEnlargeStructureBuffer ( IN OUT SMBIOS_STRUCTURE_NODE *StructureNode, UINT8 NewLength, UINTN OldBufferSize, UINTN NewBufferSize ) { EFI_SMBIOS_TABLE_HEADER *NewRecord; EFI_SMBIOS_PROTOCOL *Smbios; EFI_STATUS Status; UINT8 CountOfString; NewRecord = NULL; Smbios = GetSmbiosProtocol(); ASSERT (Smbios != NULL); NewRecord = (EFI_SMBIOS_TABLE_HEADER*) AllocateZeroPool (NewBufferSize); if (NewRecord == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (NewRecord, StructureNode->Structure, OldBufferSize); Status = Smbios->Remove (Smbios, StructureNode->SmbiosHandle); ASSERT_EFI_ERROR (Status); // // try to use original handle to enlarge the buffer. // NewRecord->Length = NewLength; Status = Smbios->Add (Smbios, NULL, &StructureNode->SmbiosHandle, NewRecord); ASSERT_EFI_ERROR (Status); FreePool (NewRecord); StructureNode->Structure = GetSmbiosBufferFromHandle ( StructureNode->SmbiosHandle, StructureNode->SmbiosType, NULL ); GetSmbiosStructureSize ( StructureNode->Structure, &StructureNode->StructureSize, &CountOfString ); return EFI_SUCCESS; }
/** Add an SMBIOS record. @param This The EFI_SMBIOS_PROTOCOL instance. @param ProducerHandle The handle of the controller or driver associated with the SMBIOS information. NULL means no handle. @param SmbiosHandle On entry, if non-zero, the handle of the SMBIOS record. If zero, then a unique handle will be assigned to the SMBIOS record. If the SMBIOS handle is already in use EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated. @param Record The data for the fixed portion of the SMBIOS record. The format of the record is determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or a set of null terminated strings and a null. @retval EFI_SUCCESS Record was added. @retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources. @retval EFI_ALREADY_STARTED The SmbiosHandle passed in was already in use. **/ EFI_STATUS EFIAPI SmbiosAdd ( IN CONST EFI_SMBIOS_PROTOCOL *This, IN EFI_HANDLE ProducerHandle, OPTIONAL IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, IN EFI_SMBIOS_TABLE_HEADER *Record ) { VOID *Raw; UINTN TotalSize; UINTN RecordSize; UINTN StructureSize; UINTN NumberOfStrings; EFI_STATUS Status; LIST_ENTRY *Head; SMBIOS_INSTANCE *Private; EFI_SMBIOS_ENTRY *SmbiosEntry; EFI_SMBIOS_HANDLE MaxSmbiosHandle; SMBIOS_HANDLE_ENTRY *HandleEntry; EFI_SMBIOS_RECORD_HEADER *InternalRecord; if (SmbiosHandle == NULL) { return EFI_INVALID_PARAMETER; } Private = SMBIOS_INSTANCE_FROM_THIS (This); // // Check whether SmbiosHandle is already in use // Head = &Private->AllocatedHandleListHead; if (*SmbiosHandle != 0 && CheckSmbiosHandleExistance(Head, *SmbiosHandle)) { return EFI_ALREADY_STARTED; } // // when SmbiosHandle is zero, an available handle will be assigned // if (*SmbiosHandle == 0) { Status = GetAvailableSmbiosHandle(This, SmbiosHandle); if (EFI_ERROR(Status)) { return Status; } } else { // // Check this handle validity // GetMaxSmbiosHandle(This, &MaxSmbiosHandle); if (*SmbiosHandle > MaxSmbiosHandle) { return EFI_INVALID_PARAMETER; } } // // Calculate record size and string number // Status = GetSmbiosStructureSize(This, Record, &StructureSize, &NumberOfStrings); if (EFI_ERROR(Status)) { return Status; } // // Enter into critical section // Status = EfiAcquireLockOrFail (&Private->DataLock); if (EFI_ERROR (Status)) { return Status; } RecordSize = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize; TotalSize = sizeof (EFI_SMBIOS_ENTRY) + RecordSize; // // Allocate internal buffer // SmbiosEntry = AllocateZeroPool (TotalSize); if (SmbiosEntry == NULL) { EfiReleaseLock (&Private->DataLock); return EFI_OUT_OF_RESOURCES; } HandleEntry = AllocateZeroPool (sizeof(SMBIOS_HANDLE_ENTRY)); if (HandleEntry == NULL) { EfiReleaseLock (&Private->DataLock); return EFI_OUT_OF_RESOURCES; } // // Build Handle Entry and insert into linked list // HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE; HandleEntry->SmbiosHandle = *SmbiosHandle; InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link); InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (SmbiosEntry + 1); Raw = (VOID *) (InternalRecord + 1); // // Build internal record Header // InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION; InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER); InternalRecord->RecordSize = RecordSize; InternalRecord->ProducerHandle = ProducerHandle; InternalRecord->NumberOfStrings = NumberOfStrings; // // Insert record into the internal linked list // SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE; SmbiosEntry->RecordHeader = InternalRecord; SmbiosEntry->RecordSize = TotalSize; InsertTailList (&Private->DataListHead, &SmbiosEntry->Link); CopyMem (Raw, Record, StructureSize); ((EFI_SMBIOS_TABLE_HEADER*)Raw)->Handle = *SmbiosHandle; // // Leave critical section // EfiReleaseLock (&Private->DataLock); return EFI_SUCCESS; }
/** Fill a standard Smbios string field. This function will convert the unicode string to single byte chars, and only English language is supported. This function changes the Structure pointer value of the structure node, which should be noted by Caller. @param StructureNode Pointer to SMBIOS_STRUCTURE_NODE which is current processed. @param Offset Offset of SMBIOS record which RecordData will be filled. @param RecordData RecordData buffer will be filled. @param RecordDataSize The size of RecordData buffer. @retval EFI_INVALID_PARAMETER RecordDataSize is too larger @retval EFI_OUT_OF_RESOURCES No memory to allocate new buffer for string @retval EFI_SUCCESS Sucess append string for a SMBIOS record. **/ EFI_STATUS SmbiosFldString ( IN OUT SMBIOS_STRUCTURE_NODE *StructureNode, IN UINT32 Offset, IN VOID *RecordData, IN UINT32 RecordDataSize ) { EFI_STATUS Status; UINT16 *Data; CHAR8 AsciiData[SMBIOS_STRING_MAX_LENGTH]; UINT8 CountOfStrings; UINTN OrigStringNumber; EFI_SMBIOS_PROTOCOL *Smbios; EFI_SMBIOS_HANDLE SmbiosHandle; UINT32 OrigStructureSize; UINTN NewStructureSize; EFI_SMBIOS_TABLE_HEADER *NewRecord; UINT32 StringLength; Status = EFI_SUCCESS; OrigStringNumber = 0; OrigStructureSize = 0; // // if we have a NULL token, // if (0 == *((STRING_REF *) RecordData)) { *(UINT8 *) ((UINT8 *) (StructureNode->Structure) + Offset) = 0; return EFI_SUCCESS; } // // Get the String from the Hii Database // Data = HiiGetPackageString ( &(StructureNode->ProducerName), *((EFI_STRING_ID *) RecordData), NULL ); if (Data == NULL) { return EFI_INVALID_PARAMETER; } StringLength = (UINT32)StrLen (Data); // // Count the string size including the terminating 0. // if (StringLength == 0) { *(UINT8 *) ((UINT8 *) (StructureNode->Structure) + Offset) = 0; FreePool (Data); return EFI_SUCCESS; } if (StringLength > SMBIOS_STRING_MAX_LENGTH) { // // Too long a string // FreePool (Data); return EFI_INVALID_PARAMETER; } Smbios = GetSmbiosProtocol(); ASSERT (Smbios != NULL); // // Convert Unicode string to Ascii string which only supported by SMBIOS. // ZeroMem (AsciiData, SMBIOS_STRING_MAX_LENGTH); UnicodeStrToAsciiStr (Data, AsciiData); // // if the field at offset is already filled with some value, // find out the string it points to // OrigStringNumber = *(UINT8 *) ((UINT8 *) (StructureNode->Structure) + Offset); if (OrigStringNumber != 0) { DEBUG ((EFI_D_ERROR, "[SMBIOSThunk] Update %dth string for type[%d],offset[0x%x],handle[0x%x] to [%s]\n", OrigStringNumber, StructureNode->SmbiosType, Offset, StructureNode->SmbiosHandle, AsciiData)); // // If original string number is not zero, just update string // Status = Smbios->UpdateString (Smbios, &StructureNode->SmbiosHandle, &OrigStringNumber, AsciiData); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "[SMBIOSThunk] Fail to update %dth string in offset 0x%x for handle:0x%x type:0x%x\n", OrigStringNumber, Offset, StructureNode->SmbiosHandle, StructureNode->SmbiosType)); ASSERT_EFI_ERROR (Status); return Status; } } else { // // If the string has not been filled in SMBIOS database, remove it and add again // with string appended. // Status = GetSmbiosStructureSize (StructureNode->Structure, &OrigStructureSize, &CountOfStrings); ASSERT_EFI_ERROR (Status); if (CountOfStrings == 0) { NewStructureSize = OrigStructureSize + StringLength; } else { NewStructureSize = OrigStructureSize + StringLength + 1; } NewRecord = AllocateZeroPool (NewStructureSize); if (NewRecord == NULL) { return EFI_OUT_OF_RESOURCES; } CopyMem (NewRecord, StructureNode->Structure, OrigStructureSize); // // Copy new string into tail of original SMBIOS record buffer. // if (CountOfStrings == 0) { AsciiStrCpy ((CHAR8 *)NewRecord + OrigStructureSize - 2, AsciiData); } else { AsciiStrCpy ((CHAR8 *)NewRecord + OrigStructureSize - 1, AsciiData); } DEBUG ((EFI_D_ERROR, "[SMBIOSThunk] Type(%d) offset(0x%x) StringNumber:%d\n", StructureNode->SmbiosType, Offset, CountOfStrings + 1)); // // Update string reference number // *(UINT8 *) ((UINT8 *) NewRecord + Offset) = (UINT8) (CountOfStrings + 1); SmbiosHandle = StructureNode->SmbiosHandle; // // Remove original SMBIOS record and add new one // Status = Smbios->Remove (Smbios, StructureNode->SmbiosHandle); ASSERT_EFI_ERROR (Status); // // Add new SMBIOS record // Status = Smbios->Add (Smbios, NULL, &SmbiosHandle, NewRecord); ASSERT_EFI_ERROR (Status); StructureNode->SmbiosHandle = SmbiosHandle; FreePool (NewRecord); } // // The SMBIOS record buffer maybe re-allocated in SMBIOS database, // so update cached buffer pointer in DataHub structure list. // StructureNode->Structure = GetSmbiosBufferFromHandle ( StructureNode->SmbiosHandle, StructureNode->SmbiosType, NULL ); ASSERT (StructureNode->Structure != NULL); // // The string update action maybe lead the record is re-allocated in SMBIOS database // so update cached record pointer // Status = GetSmbiosStructureSize (StructureNode->Structure, &StructureNode->StructureSize, &CountOfStrings); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }
/** Add an SMBIOS record. @param This The EFI_SMBIOS_PROTOCOL instance. @param ProducerHandle The handle of the controller or driver associated with the SMBIOS information. NULL means no handle. @param SmbiosHandle On entry, the handle of the SMBIOS record to add. If FFFEh, then a unique handle will be assigned to the SMBIOS record. If the SMBIOS handle is already in use, EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated. @param Record The data for the fixed portion of the SMBIOS record. The format of the record is determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or a set of null terminated strings and a null. @retval EFI_SUCCESS Record was added. @retval EFI_OUT_OF_RESOURCES Record was not added due to lack of system resources. @retval EFI_ALREADY_STARTED The SmbiosHandle passed in was already in use. **/ EFI_STATUS EFIAPI SmbiosAdd ( IN CONST EFI_SMBIOS_PROTOCOL *This, IN EFI_HANDLE ProducerHandle, OPTIONAL IN OUT EFI_SMBIOS_HANDLE *SmbiosHandle, IN EFI_SMBIOS_TABLE_HEADER *Record ) { VOID *Raw; UINTN TotalSize; UINTN RecordSize; UINTN StructureSize; UINTN NumberOfStrings; EFI_STATUS Status; LIST_ENTRY *Head; SMBIOS_INSTANCE *Private; EFI_SMBIOS_ENTRY *SmbiosEntry; EFI_SMBIOS_HANDLE MaxSmbiosHandle; SMBIOS_HANDLE_ENTRY *HandleEntry; EFI_SMBIOS_RECORD_HEADER *InternalRecord; BOOLEAN Smbios32BitTable; BOOLEAN Smbios64BitTable; if (SmbiosHandle == NULL) { return EFI_INVALID_PARAMETER; } Private = SMBIOS_INSTANCE_FROM_THIS (This); // // Check whether SmbiosHandle is already in use // Head = &Private->AllocatedHandleListHead; if (*SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED && CheckSmbiosHandleExistance(Head, *SmbiosHandle)) { return EFI_ALREADY_STARTED; } // // when SmbiosHandle is 0xFFFE, an available handle will be assigned // if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) { Status = GetAvailableSmbiosHandle(This, SmbiosHandle); if (EFI_ERROR(Status)) { return Status; } } else { // // Check this handle validity // GetMaxSmbiosHandle(This, &MaxSmbiosHandle); if (*SmbiosHandle > MaxSmbiosHandle) { return EFI_INVALID_PARAMETER; } } // // Calculate record size and string number // Status = GetSmbiosStructureSize(This, Record, &StructureSize, &NumberOfStrings); if (EFI_ERROR(Status)) { return Status; } Smbios32BitTable = FALSE; Smbios64BitTable = FALSE; if ((This->MajorVersion < 0x3) || ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) { // // For SMBIOS 32-bit table, the length of the entire structure table (including all strings) must be reported // in the Structure Table Length field of the SMBIOS Structure Table Entry Point, // which is a WORD field limited to 65,535 bytes. So the max size of 32-bit table should not exceed 65,535 bytes. // if ((EntryPointStructure != NULL) && (EntryPointStructure->TableLength + StructureSize > SMBIOS_TABLE_MAX_LENGTH)) { DEBUG ((EFI_D_INFO, "SmbiosAdd: Total length exceeds max 32-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize)); } else { Smbios32BitTable = TRUE; DEBUG ((EFI_D_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 32-bit table\n", Record->Type, StructureSize)); } } // // For SMBIOS 3.0, Structure table maximum size in Entry Point structure is DWORD field limited to 0xFFFFFFFF bytes. // if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) { // // For SMBIOS 64-bit table, Structure table maximum size in SMBIOS 3.0 (64-bit) Entry Point // is a DWORD field limited to 0xFFFFFFFF bytes. So the max size of 64-bit table should not exceed 0xFFFFFFFF bytes. // if ((Smbios30EntryPointStructure != NULL) && (Smbios30EntryPointStructure->TableMaximumSize + StructureSize > SMBIOS_3_0_TABLE_MAX_LENGTH)) { DEBUG ((EFI_D_INFO, "SmbiosAdd: Total length exceeds max 64-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize)); } else { DEBUG ((EFI_D_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 64-bit table\n", Record->Type, StructureSize)); Smbios64BitTable = TRUE; } } if ((!Smbios32BitTable) && (!Smbios64BitTable)) { // // If both 32-bit and 64-bit table are not updated, quit // return EFI_OUT_OF_RESOURCES; } // // Enter into critical section // Status = EfiAcquireLockOrFail (&Private->DataLock); if (EFI_ERROR (Status)) { return Status; } RecordSize = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize; TotalSize = sizeof (EFI_SMBIOS_ENTRY) + RecordSize; // // Allocate internal buffer // SmbiosEntry = AllocateZeroPool (TotalSize); if (SmbiosEntry == NULL) { EfiReleaseLock (&Private->DataLock); return EFI_OUT_OF_RESOURCES; } HandleEntry = AllocateZeroPool (sizeof(SMBIOS_HANDLE_ENTRY)); if (HandleEntry == NULL) { EfiReleaseLock (&Private->DataLock); return EFI_OUT_OF_RESOURCES; } // // Build Handle Entry and insert into linked list // HandleEntry->Signature = SMBIOS_HANDLE_ENTRY_SIGNATURE; HandleEntry->SmbiosHandle = *SmbiosHandle; InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link); InternalRecord = (EFI_SMBIOS_RECORD_HEADER *) (SmbiosEntry + 1); Raw = (VOID *) (InternalRecord + 1); // // Build internal record Header // InternalRecord->Version = EFI_SMBIOS_RECORD_HEADER_VERSION; InternalRecord->HeaderSize = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER); InternalRecord->RecordSize = RecordSize; InternalRecord->ProducerHandle = ProducerHandle; InternalRecord->NumberOfStrings = NumberOfStrings; // // Insert record into the internal linked list // SmbiosEntry->Signature = EFI_SMBIOS_ENTRY_SIGNATURE; SmbiosEntry->RecordHeader = InternalRecord; SmbiosEntry->RecordSize = TotalSize; SmbiosEntry->Smbios32BitTable = Smbios32BitTable; SmbiosEntry->Smbios64BitTable = Smbios64BitTable; InsertTailList (&Private->DataListHead, &SmbiosEntry->Link); CopyMem (Raw, Record, StructureSize); ((EFI_SMBIOS_TABLE_HEADER*)Raw)->Handle = *SmbiosHandle; // // Some UEFI drivers (such as network) need some information in SMBIOS table. // Here we create SMBIOS table and publish it in // configuration table, so other UEFI drivers can get SMBIOS table from // configuration table without depending on PI SMBIOS protocol. // SmbiosTableConstruction (Smbios32BitTable, Smbios64BitTable); // // Leave critical section // EfiReleaseLock (&Private->DataLock); return EFI_SUCCESS; }