/** @file This library will parse the coreboot table in memory and extract those required information. Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include "Coreboot.h" /** Convert a packed value from cbuint64 to a UINT64 value. @param val The pointer to packed data. @return the UNIT64 value after conversion. **/ UINT64 cb_unpack64 ( IN struct cbuint64 val ) { return LShiftU64 (val.hi, 32) | val.lo; } /** Returns the sum of all elements in a buffer of 16-bit values. During calculation, the carry bits are also been added. @param Buffer The pointer to the buffer to carry out the sum operation. @param Length The size, in bytes, of Buffer. @return Sum The sum of Buffer with carry bits included during additions. **/ UINT16 CbCheckSum16 ( IN UINT16 *Buffer, IN UINTN Length ) { UINT32 Sum, TmpValue; UINTN Idx; UINT8 *TmpPtr; Sum = 0; TmpPtr = (UINT8 *)Buffer; for(Idx = 0; Idx < Length; Idx++) { TmpValue = TmpPtr[Idx]; if (Idx % 2 == 1) { TmpValue <<= 8; } Sum += TmpValue; // Wrap if (Sum >= 0x10000) { Sum = (Sum + (Sum >> 16)) & 0xFFFF; } } return (UINT16)((~Sum) & 0xFFFF); } /** Find coreboot record with given Tag from the memory Start in 4096 bytes range. @param Start The start memory to be searched in @param Tag The tag id to be found @retval NULL The Tag is not found. @retval Others The pointer to the record found. **/ VOID * EFIAPI FindCbTag ( IN VOID *Start, IN UINT32 Tag ) { struct cb_header *Header; struct cb_record *Record; UINT8 *TmpPtr; UINT8 *TagPtr; UINTN Idx; UINT16 CheckSum; Header = NULL; TmpPtr = (UINT8 *)Start; for (Idx = 0; Idx < 4096; Idx += 16, TmpPtr += 16) { Header = (struct cb_header *)TmpPtr; if (Header->signature == CB_HEADER_SIGNATURE) { break; } } if (Idx >= 4096) { return NULL; } if ((Header == NULL) || (Header->table_bytes == 0)) { return NULL; } // // Check the checksum of the coreboot table header // CheckSum = CbCheckSum16 ((UINT16 *)Header, sizeof (*Header)); if (CheckSum != 0) { DEBUG ((EFI_D_ERROR, "Invalid coreboot table header checksum\n")); return NULL; } CheckSum = CbCheckSum16 ((UINT16 *)(TmpPtr + sizeof (*Header)), Header->table_bytes); if (CheckSum != Header->table_checksum) { DEBUG ((EFI_D_ERROR, "Incorrect checksum of all the coreboot table entries\n")); return NULL; } TagPtr = NULL; TmpPtr += Header->header_bytes; for (Idx = 0; Idx < Header->table_entries; Idx++) { Record = (struct cb_record *)TmpPtr; if (Record->tag == CB_TAG_FORWARD) { TmpPtr = (VOID *)(UINTN)((struct cb_forward *)(UINTN)Record)->forward; if (Tag == CB_TAG_FORWARD) { return TmpPtr; } else { return FindCbTag (TmpPtr, Tag); } } if (Record->tag == Tag) { TagPtr = TmpPtr; break; } TmpPtr += Record->size; } return TagPtr; } /** Find the given table with TableId from the given coreboot memory Root. @param Root The coreboot memory table to be searched in @param TableId Table id to be found @param pMemTable To save the base address of the memory table found @param pMemTableSize To save the size of memory table found @retval RETURN_SUCCESS Successfully find out the memory table. @retval RETURN_INVALID_PARAMETER Invalid input parameters. @retval RETURN_NOT_FOUND Failed to find the memory table. **/ RETURN_STATUS EFIAPI FindCbMemTable ( IN struct cbmem_root *Root, IN UINT32 TableId, OUT VOID **pMemTable, OUT UINT32 *pMemTableSize ) { UINTN Idx; BOOLEAN IsImdEntry; struct cbmem_entry *Entries; if ((Root == NULL) || (pMemTable == NULL)) { return RETURN_INVALID_PARAMETER; } // // Check if the entry is CBMEM or IMD // and handle them separately // Entries = Root->entries; if (Entries[0].magic == CBMEM_ENTRY_MAGIC) { IsImdEntry = FALSE; } else { Entries = (struct cbmem_entry *)((struct imd_root *)Root)->entries; if (Entries[0].magic == IMD_ENTRY_MAGIC) { IsImdEntry = TRUE; } else { return RETURN_NOT_FOUND; } } for (Idx = 0; Idx < Root->num_entries; Idx++) { if (Entries[Idx].id == TableId) { if (IsImdEntry) { *pMemTable = (VOID *) ((UINTN)Entries[Idx].start + (UINTN)Root); } else { *pMemTable = (VOID *) (UINTN)Entries[Idx].start; } if (pMemTableSize != NULL) { *pMemTableSize = Entries[Idx].size; } DEBUG ((EFI_D_INFO, "Find CbMemTable Id 0x%x, base %p, size 0x%x\n", TableId, *pMemTable, Entries[Idx].size)); return RETURN_SUCCESS; } } return RETURN_NOT_FOUND; } /** Acquire the memory information from the coreboot table in memory. @param MemInfoCallback The callback routine @param pParam Pointer to the callback routine parameter @retval RETURN_SUCCESS Successfully find out the memory information. @retval RETURN_NOT_FOUND Failed to find the memory information. **/ RETURN_STATUS EFIAPI CbParseMemoryInfo ( IN CB_MEM_INFO_CALLBACK MemInfoCallback, IN VOID *pParam ) { struct cb_memory *rec; struct cb_memory_range *Range; UINT64 Start; UINT64 Size; UINTN Index; // // Get the coreboot memory table // rec = (struct cb_memory *)FindCbTag (0, CB_TAG_MEMORY); if (rec == NULL) { rec = (struct cb_memory *)FindCbTag ((VOID *)(UINTN)PcdGet32 (PcdCbHeaderPointer), CB_TAG_MEMORY); } if (rec == NULL) { return RETURN_NOT_FOUND; } for (Index = 0; Index < MEM_RANGE_COUNT(rec); Index++) { Range = MEM_RANGE_PTR(rec, Index); Start = cb_unpack64(Range->start); Size = cb_unpack64(Range->size); DEBUG ((EFI_D_INFO, "%d. %016lx - %016lx [%02x]\n", Index, Start, Start + Size - 1, Range->type)); MemInfoCallback (Start, Size, Range->type, pParam); } return RETURN_SUCCESS; } /** Acquire the coreboot memory table with the given table id @param TableId Table id to be searched @param pMemTable Pointer to the base address of the memory table @param pMemTableSize Pointer to the size of the memory table @retval RETURN_SUCCESS Successfully find out the memory table. @retval RETURN_INVALID_PARAMETER Invalid input parameters. @retval RETURN_NOT_FOUND Failed to find the memory table. **/ RETURN_STATUS EFIAPI CbParseCbMemTable ( IN UINT32 TableId, OUT VOID **pMemTable, OUT UINT32 *pMemTableSize ) { struct cb_memory *rec; struct cb_memory_range *Range; UINT64 Start; UINT64 Size; UINTN Index; if (pMemTable == NULL) { return RETURN_INVALID_PARAMETER; } *pMemTable = NULL; // // Get the coreboot memory table // rec = (struct cb_memory *)FindCbTag (0, CB_TAG_MEMORY); if (rec == NULL) { rec = (struct cb_memory *)FindCbTag ((VOID *)(UINTN)PcdGet32 (PcdCbHeaderPointer), CB_TAG_MEMORY); } if (rec == NULL) { return RETURN_NOT_FOUND; } for (Index = 0; Index < MEM_RANGE_COUNT(rec); Index++) { Range = MEM_RANGE_PTR(rec, Index); Start = cb_unpack64(Range->start); Size = cb_unpack64(Range->size); if ((Range->type == CB_MEM_TABLE) && (Start > 0x1000)) { if (FindCbMemTable ((struct cbmem_root *)(UINTN)(Start + Size - DYN_CBMEM_ALIGN_SIZE), TableId, pMemTable, pMemTableSize) == RETURN_SUCCESS) return RETURN_SUCCESS; } } return RETURN_NOT_FOUND; } /** Acquire the acpi table from coreboot @param pMemTable Pointer to the base address of the memory table @param pMemTableSize Pointer to the size of the memory table @retval RETURN_SUCCESS Successfully find out the memory table. @retval RETURN_INVALID_PARAMETER Invalid input parameters. @retval RETURN_NOT_FOUND Failed to find the memory table. **/ RETURN_STATUS EFIAPI CbParseAcpiTable ( OUT VOID **pMemTable, OUT UINT32 *pMemTableSize ) { return CbParseCbMemTable (SIGNATURE_32 ('I', 'P', 'C', 'A'), pMemTable, pMemTableSize); } /** Acquire the smbios table from coreboot @param pMemTable Pointer to the base address of the memory table @param pMemTableSize Pointer to the size of the memory table @retval RETURN_SUCCESS Successfully find out the memory table. @retval RETURN_INVALID_PARAMETER Invalid input parameters. @retval RETURN_NOT_FOUND Failed to find the memory table. **/ RETURN_STATUS EFIAPI CbParseSmbiosTable ( OUT VOID **pMemTable, OUT UINT32 *pMemTableSize ) { return CbParseCbMemTable (SIGNATURE_32 ('T', 'B', 'M', 'S'), pMemTable, pMemTableSize); } /** Find the required fadt information @param pPmCtrlReg Pointer to the address of power management control register @param pPmTimerReg Pointer to the address of power management timer register @param pResetReg Pointer to the address of system reset register @param pResetValue Pointer to the value to be written to the system reset register @param pPmEvtReg Pointer to the address of power management event register @param pPmGpeEnReg Pointer to the address of power management GPE enable register @retval RETURN_SUCCESS Successfully find out all the required fadt information. @retval RETURN_NOT_FOUND Failed to find the fadt table. **/ RETURN_STATUS EFIAPI CbParseFadtInfo ( OUT UINTN *pPmCtrlReg, OUT UINTN *pPmTimerReg, OUT UINTN *pResetReg, OUT UINTN *pResetValue, OUT UINTN *pPmEvtReg, OUT UINTN *pPmGpeEnReg ) { EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp; EFI_ACPI_DESCRIPTION_HEADER *Rsdt; UINT32 *Entry32; UINTN Entry32Num; EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt; EFI_ACPI_DESCRIPTION_HEADER *Xsdt; UINT64 *Entry64; UINTN Entry64Num; UINTN Idx; RETURN_STATUS Status; Rsdp = NULL; Status = RETURN_SUCCESS; Status = CbParseAcpiTable ((VOID **)&Rsdp, NULL); if (RETURN_ERROR(Status)) { return Status; } if (Rsdp == NULL) { return RETURN_NOT_FOUND; } DEBUG ((EFI_D_INFO, "Find Rsdp at %p\n", Rsdp)); DEBUG ((EFI_D_INFO, "Find Rsdt 0x%x, Xsdt 0x%lx\n", Rsdp->RsdtAddress, Rsdp->XsdtAddress)); // // Search Rsdt First // Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)(Rsdp->RsdtAddress); if (Rsdt != NULL) { Entry32 = (UINT32 *)(Rsdt + 1); Entry32Num = (Rsdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) >> 2; for (Idx = 0; Idx < Entry32Num; Idx++) { if (*(UINT32 *)(UINTN)(Entry32[Idx]) == EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) { Fadt = (EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN)(Entry32[Idx]); if (pPmCtrlReg != NULL) { *pPmCtrlReg = Fadt->Pm1aCntBlk; } DEBUG ((EFI_D_INFO, "PmCtrl Reg 0x%x\n", Fadt->Pm1aCntBlk)); if (pPmTimerReg != NULL) { *pPmTimerReg = Fadt->PmTmrBlk; } DEBUG ((EFI_D_INFO, "PmTimer Reg 0x%x\n", Fadt->PmTmrBlk)); if (pResetReg != NULL) { *pResetReg = (UINTN)Fadt->ResetReg.Address; } DEBUG ((EFI_D_INFO, "Reset Reg 0x%lx\n", Fadt->ResetReg.Address)); if (pResetValue != NULL) { *pResetValue = Fadt->ResetValue; } DEBUG ((EFI_D_INFO, "Reset Value 0x%x\n", Fadt->ResetValue)); if (pPmEvtReg != NULL) { *pPmEvtReg = Fadt->Pm1aEvtBlk; DEBUG ((EFI_D_INFO, "PmEvt Reg 0x%x\n", Fadt->Pm1aEvtBlk)); } if (pPmGpeEnReg != NULL) { *pPmGpeEnReg = Fadt->Gpe0Blk + Fadt->Gpe0BlkLen / 2; DEBUG ((EFI_D_INFO, "PmGpeEn Reg 0x%x\n", *pPmGpeEnReg)); } // // Verify values for proper operation // ASSERT(Fadt->Pm1aCntBlk != 0); ASSERT(Fadt->PmTmrBlk != 0); ASSERT(Fadt->ResetReg.Address != 0); ASSERT(Fadt->Pm1aEvtBlk != 0); ASSERT(Fadt->Gpe0Blk != 0); DEBUG_CODE_BEGIN (); BOOLEAN SciEnabled; // // Check the consistency of SCI enabling // // // Get SCI_EN value // if (Fadt->Pm1CntLen == 4) { SciEnabled = (IoRead32 (Fadt->Pm1aCntBlk) & BIT0)? TRUE : FALSE; } else { // // if (Pm1CntLen == 2), use 16 bit IO read; // if (Pm1CntLen != 2 && Pm1CntLen != 4), use 16 bit IO read as a fallback // SciEnabled = (IoRead16 (Fadt->Pm1aCntBlk) & BIT0)? TRUE : FALSE; } if (!(Fadt->Flags & EFI_ACPI_5_0_HW_REDUCED_ACPI) && (Fadt->SmiCmd == 0) && !SciEnabled) { // // The ACPI enabling status is inconsistent: SCI is not enabled but ACPI // table does not provide a means to enable it through FADT->SmiCmd // DEBUG ((DEBUG_ERROR, "ERROR: The ACPI enabling status is inconsistent: SCI is not" " enabled but the ACPI table does not provide a means to enable it through FADT->SmiCmd." " This may cause issues in OS.\n")); ASSERT (FALSE); } DEBUG_CODE_END (); return RETURN_SUCCESS; } } } // // Search Xsdt Second // Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)(Rsdp->XsdtAddress); if (Xsdt != NULL) { Entry64 = (UINT64 *)(Xsdt + 1); Entry64Num = (Xsdt->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) >> 3; for (Idx = 0; Idx < Entry64Num; Idx++) { if (*(UINT32 *)(UINTN)(Entry64[Idx]) == EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) { Fadt = (EFI_ACPI_3_0_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN)(Entry64[Idx]); if (pPmCtrlReg) *pPmCtrlReg = Fadt->Pm1aCntBlk; DEBUG ((EFI_D_ERROR, "PmCtrl Reg 0x%x\n", Fadt->Pm1aCntBlk)); if (pPmTimerReg) *pPmTimerReg = Fadt->PmTmrBlk; DEBUG ((EFI_D_ERROR, "PmTimer Reg 0x%x\n", Fadt->PmTmrBlk)); if (pResetReg) *pResetReg = (UINTN)Fadt->ResetReg.Address; DEBUG ((EFI_D_ERROR, "Reset Reg 0x%lx\n", Fadt->ResetReg.Address)); if (pResetValue) *pResetValue = Fadt->ResetValue; DEBUG ((EFI_D_ERROR, "Reset Value 0x%x\n", Fadt->ResetValue)); if (pPmEvtReg != NULL) { *pPmEvtReg = Fadt->Pm1aEvtBlk; DEBUG ((EFI_D_INFO, "PmEvt Reg 0x%x\n", Fadt->Pm1aEvtBlk)); } if (pPmGpeEnReg != NULL) { *pPmGpeEnReg = Fadt->Gpe0Blk + Fadt->Gpe0BlkLen / 2; DEBUG ((EFI_D_INFO, "PmGpeEn Reg 0x%x\n", *pPmGpeEnReg)); } return RETURN_SUCCESS; } } } return RETURN_NOT_FOUND; } /** Find the serial port information @param pRegBase Pointer to the base address of serial port registers @param pRegAccessType Pointer to the access type of serial port registers @param pRegWidth Pointer to the register width in bytes @param pBaudrate Pointer to the serial port baudrate @param pInputHertz Pointer to the input clock frequency @param pUartPciAddr Pointer to the UART PCI bus, dev and func address @retval RETURN_SUCCESS Successfully find the serial port information. @retval RETURN_NOT_FOUND Failed to find the serial port information . **/ RETURN_STATUS EFIAPI CbParseSerialInfo ( OUT UINT32 *pRegBase, OUT UINT32 *pRegAccessType, OUT UINT32 *pRegWidth, OUT UINT32 *pBaudrate, OUT UINT32 *pInputHertz, OUT UINT32 *pUartPciAddr ) { struct cb_serial *CbSerial; CbSerial = FindCbTag (0, CB_TAG_SERIAL); if (CbSerial == NULL) { CbSerial = FindCbTag ((VOID *)(UINTN)PcdGet32 (PcdCbHeaderPointer), CB_TAG_SERIAL); } if (CbSerial == NULL) { return RETURN_NOT_FOUND; } if (pRegBase != NULL) { *pRegBase = CbSerial->baseaddr; } if (pRegWidth != NULL) { *pRegWidth = CbSerial->regwidth; } if (pRegAccessType != NULL) { *pRegAccessType = CbSerial->type; } if (pBaudrate != NULL) { *pBaudrate = CbSerial->baud; } if (pInputHertz != NULL) { *pInputHertz = CbSerial->input_hertz; } if (pUartPciAddr != NULL) { *pUartPciAddr = CbSerial->uart_pci_addr; } return RETURN_SUCCESS; } /** Search for the coreboot table header @param Level Level of the search depth @param HeaderPtr Pointer to the pointer of coreboot table header @retval RETURN_SUCCESS Successfully find the coreboot table header . @retval RETURN_NOT_FOUND Failed to find the coreboot table header . **/ RETURN_STATUS EFIAPI CbParseGetCbHeader ( IN UINTN Level, OUT VOID **HeaderPtr ) { UINTN Index; VOID *TempPtr; if (HeaderPtr == NULL) { return RETURN_NOT_FOUND; } TempPtr = NULL; for (Index = 0; Index < Level; Index++) { TempPtr = FindCbTag (TempPtr, CB_TAG_FORWARD); if (TempPtr == NULL) { break; } } if ((Index >= Level) && (TempPtr != NULL)) { *HeaderPtr = TempPtr; return RETURN_SUCCESS; } return RETURN_NOT_FOUND; } /** Find the video frame buffer information @param pFbInfo Pointer to the FRAME_BUFFER_INFO structure @retval RETURN_SUCCESS Successfully find the video frame buffer information. @retval RETURN_NOT_FOUND Failed to find the video frame buffer information . **/ RETURN_STATUS EFIAPI CbParseFbInfo ( OUT FRAME_BUFFER_INFO *pFbInfo ) { struct cb_framebuffer *CbFbRec; if (pFbInfo == NULL) { return RETURN_INVALID_PARAMETER; } CbFbRec = FindCbTag (0, CB_TAG_FRAMEBUFFER); if (CbFbRec == NULL) { CbFbRec = FindCbTag ((VOID *)(UINTN)PcdGet32 (PcdCbHeaderPointer), CB_TAG_FRAMEBUFFER); } if (CbFbRec == NULL) { return RETURN_NOT_FOUND; } DEBUG ((EFI_D_INFO, "Found coreboot video frame buffer information\n")); DEBUG ((EFI_D_INFO, "physical_address: 0x%lx\n", CbFbRec->physical_address)); DEBUG ((EFI_D_INFO, "x_resolution: 0x%x\n", CbFbRec->x_resolution)); DEBUG ((EFI_D_INFO, "y_resolution: 0x%x\n", CbFbRec->y_resolution)); DEBUG ((EFI_D_INFO, "bits_per_pixel: 0x%x\n", CbFbRec->bits_per_pixel)); DEBUG ((EFI_D_INFO, "bytes_per_line: 0x%x\n", CbFbRec->bytes_per_line)); DEBUG ((EFI_D_INFO, "red_mask_size: 0x%x\n", CbFbRec->red_mask_size)); DEBUG ((EFI_D_INFO, "red_mask_pos: 0x%x\n", CbFbRec->red_mask_pos)); DEBUG ((EFI_D_INFO, "green_mask_size: 0x%x\n", CbFbRec->green_mask_size)); DEBUG ((EFI_D_INFO, "green_mask_pos: 0x%x\n", CbFbRec->green_mask_pos)); DEBUG ((EFI_D_INFO, "blue_mask_size: 0x%x\n", CbFbRec->blue_mask_size)); DEBUG ((EFI_D_INFO, "blue_mask_pos: 0x%x\n", CbFbRec->blue_mask_pos)); DEBUG ((EFI_D_INFO, "reserved_mask_size: 0x%x\n", CbFbRec->reserved_mask_size)); DEBUG ((EFI_D_INFO, "reserved_mask_pos: 0x%x\n", CbFbRec->reserved_mask_pos)); pFbInfo->LinearFrameBuffer = CbFbRec->physical_address; pFbInfo->HorizontalResolution = CbFbRec->x_resolution; pFbInfo->VerticalResolution = CbFbRec->y_resolution; pFbInfo->BitsPerPixel = CbFbRec->bits_per_pixel; pFbInfo->BytesPerScanLine = (UINT16)CbFbRec->bytes_per_line; pFbInfo->Red.Mask = (1 << CbFbRec->red_mask_size) - 1; pFbInfo->Red.Position = CbFbRec->red_mask_pos; pFbInfo->Green.Mask = (1 << CbFbRec->green_mask_size) - 1; pFbInfo->Green.Position = CbFbRec->green_mask_pos; pFbInfo->Blue.Mask = (1 << CbFbRec->blue_mask_size) - 1; pFbInfo->Blue.Position = CbFbRec->blue_mask_pos; pFbInfo->Reserved.Mask = (1 << CbFbRec->reserved_mask_size) - 1; pFbInfo->Reserved.Position = CbFbRec->reserved_mask_pos; return RETURN_SUCCESS; }