/** @file Code implementing Shared Mailbox table generation in System memory. @copyright INTEL CONFIDENTIAL Copyright (c) 2021 Intel Corporation. All rights reserved This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by the license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. This file contains a 'Sample Driver' and is licensed as such under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to the additional terms of the license agreement. @par Specification Reference: **/ #include /** Min: Returns minimum of 2 numbers @param[in] XmlCliCommon @retval UINT32 Minimum value between two input numbers **/ UINT32 Min ( IN UINT32 X, IN UINT32 Y ) { if (X < Y) { return X; } else { return Y; } } /** Reads/Writes HIF Mailbox @param[in] Offset Mailbox offset @param[in] Size Size in bytes @param[out] Buffer Output buffer @param[in] MailboxAddress Device Private Data @param[in] OpRdOrWr Operation type @retval EFI_SUCCESS Operation Successfull @retval !EFI_SUCCESS Failure **/ EFI_STATUS ReadWriteHIF ( IN UINT32 Offset, IN UINT32 Size, OUT VOID *Buffer, IN UINT32 MailboxAddress, IN UINT8 OpRdOrWr ) { UINT32 i; UINT32 tempOffset; UINT32 noOfAlignedIo; UINT32 tempData; volatile UINT32 *mailboxAddr; // Our HIF card supports only dword RW and dword aligned offsets // Determine aligned offset and number of IOs to perform tempOffset = (Offset/4) * 4; // Get aligned offset // Determine start address mailboxAddr = (UINT32*)(UINTN)(MailboxAddress + tempOffset); // Perform as many IOs as required if (Offset & 0x3) { // Mis Aligned Offset? UINT32 lenOfValidData = Min(Size, 4-(Offset-tempOffset)); UINT32 offsetWithinUnalignedDword = (Offset-tempOffset); // If mis-aligned offset, do first IO and extract only specific bytes if (OpRdOrWr == READ_MB) { tempData = *mailboxAddr++; CopyMem(Buffer, (VOID*) (((UINT8*)&tempData) + offsetWithinUnalignedDword), lenOfValidData); } else if (OpRdOrWr == WRITE_MB) { tempData = *mailboxAddr; CopyMem((VOID*) (((UINT8*)&tempData) + offsetWithinUnalignedDword), Buffer, lenOfValidData); *mailboxAddr++ = tempData; } Buffer = ((UINT8*)Buffer) + lenOfValidData; Size -= lenOfValidData; } noOfAlignedIo = Size/4; // No. of Dword aligned IOs for (i=0; (noOfAlignedIo!=0) && (i < noOfAlignedIo); i++) { // Do Dword Aligned IOs if (OpRdOrWr == READ_MB) { *((UINT32*)Buffer) = * mailboxAddr++; } else if (OpRdOrWr == WRITE_MB) { * mailboxAddr++ = *((UINT32*)Buffer); } Buffer = ((UINT32*)Buffer)+1; } if (Size & 0x3) { // Mis aligned size? // Mis-aligned size, Do Last IO and extract only specific bytes tempData = *mailboxAddr; if (OpRdOrWr == READ_MB) { CopyMem(Buffer, (VOID*) &tempData, Size-noOfAlignedIo*4); } else if (OpRdOrWr == WRITE_MB) { CopyMem((VOID*) &tempData, Buffer, Size-noOfAlignedIo*4); *mailboxAddr = tempData; } } return EFI_SUCCESS; } /** Reads/Writes Mailbox @param[in] Offset Mailbox offset @param[in] Size Size in bytes @param[out] Buffer Output buffer @param[in] MailboxAddress Device Private Data and should not exceed to PCIe Address @param[in] PcieAddr PCIe Address @param[in] OpRdOrWr Operation type @retval EFI_SUCCESS Operation Successfull @retval !EFI_SUCCESS Failure **/ VOID ReadWriteMB ( IN UINT32 Offset, IN UINT32 Size, OUT VOID *Buffer, IN UINT32 MailboxAddress, IN UINT32 PcieAddr, IN UINT8 OpRdOrWr ) { if (MailboxAddress == 0) { // either in SOFT SDV flow or MB address is 0 // DEBUG ((DEBUG_INFO, "XML_CLI: SoftSDV Flow or MB address is 0, hence returning. \n")); return; } Offset = Offset + LEG_MAILBOX_OFFSET; if (MailboxAddress < PcieAddr) // if mailbox is below MM CFG base then its sitting in DRAM { // Write to the DRAM Mailbox if (OpRdOrWr == READ_MB) { CopyMem (Buffer, (VOID*)(UINTN)(MailboxAddress + Offset), Size); } else if (OpRdOrWr == WRITE_MB) { CopyMem ((VOID*)(UINTN)(MailboxAddress + Offset), Buffer, Size); } return; } ReadWriteHIF(Offset, Size, Buffer, MailboxAddress, OpRdOrWr); } /** Get Offset and Size of the Entry from Shared memory table by Signature @param[in] SharedMemTable EFI Shared Memory Table pointer to lookup entry @param[in] Signature Signature to look for offset and size @param[out] Offset Entry's offset is returned if Signature found @param[out] Size Entry's size is returned if Signature found @retval EFI_SUCCESS Operation Successful @retval EFI_INVALID_PARAMETER Parameter Offset or Size is not valid @retval !EFI_SUCCESS Failure **/ EFI_STATUS SharedMemGetEntryBySignature ( IN SHARED_MEMORY_TABLE *SharedMemTable, IN UINT32 Signature, OUT UINT32 *Offset, OUT UINT32 *Size ) { UINT32 entry; if (Offset == NULL || Size == NULL) { return EFI_INVALID_PARAMETER; } for (entry = 0; entry < SharedMemTable->Header._u1.EntriesInfo.EntriesNumber; entry++) { if (SharedMemTable->Entry[entry].Signature == Signature) { *Offset = SharedMemTable->Entry[entry].Offset; *Size = SharedMemTable->Entry[entry].Size; return EFI_SUCCESS; } } return EFI_NOT_FOUND; } /** Calculate and store checksum of the Shared Memory Table @param[in,out] SharedMemTable EFI Shared Memory Table, update header checksum @retval VOID **/ VOID SharedMemCalculateChecksum ( IN OUT SHARED_MEMORY_TABLE *SharedMemTable ) { UINT8 i; UINT32 checkSum; checkSum = 0; for (i = 0; i < sizeof (SHARED_MEMORY_TABLE) / 4; i++) { // alignment to DWORD is must! checkSum += *((UINT32 *) (SharedMemTable) + i); } SharedMemTable->Header.CheckSum = checkSum; } /** Add Entry to SHared Memory Table for last entry with last entry signature @param[in,out] SharedMemTable EFI Shared Memory Table @retval VOID **/ VOID SharedMemSetLastEntry ( IN OUT SHARED_MEMORY_TABLE *SharedMemTable ) { UINT32 entry; // update the last entry entry = SharedMemTable->Header._u1.EntriesInfo.EntriesNumber; SharedMemTable->Entry[entry].Signature = SHARED_MB_LAST_SIG; SharedMemTable->Entry[entry].Offset = SHARED_MB_LAST_SIG; SharedMemTable->Entry[entry].Size = SHARED_MB_LAST_SIG; SharedMemTable->Entry[entry]._u2.RawFlags = SHARED_MB_LAST_SIG; } /** Add Entry to Shared Memory Table @param[in,out] SharedMemTable EFI Shared Memory Table @param[in] Signature Signature of the Entry @param[in] Offset Offset of the Entry @param[in] Size Size of the Entry @retval EFI_SUCCESS Operation Successfull @retval EFI_OUT_OF_RESOURCES Resources are not available to add new entry @retval !EFI_SUCCESS Failure **/ EFI_STATUS SharedMemAddEntry ( IN OUT SHARED_MEMORY_TABLE *SharedMemTable, IN UINT32 Signature, IN UINT32 Offset, IN UINT32 Size, IN UINT32 AccessType ) { UINT32 entryNum; if (SharedMemTable->Header._u1.EntriesInfo.EntriesNumber >= SHARED_MEM_MAX_ENTRY_NUMBER) { return EFI_OUT_OF_RESOURCES; } entryNum = SharedMemTable->Header._u1.EntriesInfo.EntriesNumber; SharedMemTable->Header._u1.EntriesInfo.ValidHeader = 0; SharedMemTable->Entry[entryNum].Signature = Signature; SharedMemTable->Entry[entryNum].Offset = Offset; SharedMemTable->Entry[entryNum].Size = Size; SharedMemTable->Entry[entryNum]._u2.Flags.AccessType = AccessType; SharedMemTable->Entry[entryNum]._u2.Flags.DeviceId = 0; SharedMemTable->Entry[entryNum]._u2.Flags.Bar = 0; SharedMemTable->Entry[entryNum]._u2.Flags.ReservedType = 0; SharedMemTable->Entry[entryNum]._u2.Flags.CapabilityBit = 0; if (AccessType == SHARED_MEMORY_FLAG_MMIO_TYPE) { SharedMemTable->Entry[entryNum]._u2.Flags.DeviceId = SharedMemTable->Header.CommonFlags.DeviceId; SharedMemTable->Entry[entryNum]._u2.Flags.Bar = SharedMemTable->Header.CommonFlags.Bar; } SharedMemTable->Header._u1.EntriesInfo.EntriesNumber++; SharedMemSetLastEntry (SharedMemTable); SharedMemTable->Header._u1.EntriesInfo.ValidHeader = 1; SharedMemCalculateChecksum (SharedMemTable); return EFI_SUCCESS; } /** Initialize Shared Memory Table @param[in,out] SharedMemTable Pointer address at which Shared Memory Table to be initialized @param[in] DeviceId Device Id @param[in] BarNumber Base Address Register Number @retval VOID **/ VOID SharedMemInitTable ( IN OUT SHARED_MEMORY_TABLE *SharedMemTable, IN UINT16 DeviceId, IN UINT8 BarNumber ) { ZeroMem ((VOID *) SharedMemTable, sizeof (SHARED_MEMORY_TABLE)); SharedMemTable->Header.BiosSignature = SHARED_MB_BIOS_SIG; SharedMemTable->Header.BiosSignature2 = SHARED_MB_BIOS_SIG; SharedMemTable->Header._u1.EntriesInfo.EntrySize = sizeof (SHARED_MEMORY_ENTRY); SharedMemTable->Header._u1.EntriesInfo.FirstEntryOffset = VARIABLE_OFFSET (SHARED_MEMORY_TABLE, Entry); SharedMemTable->Header.CommonFlags.DeviceId = DeviceId; SharedMemTable->Header.CommonFlags.Bar = BarNumber; SharedMemTable->Header.CliSpecVersion.MinorVersion = 0; SharedMemTable->Header.CliSpecVersion.MajorVersion = 6; SharedMemTable->Header.CliSpecVersion.ReleaseVersion = 0; SharedMemTable->Header._u1.EntriesInfo.EntriesNumber = 0; SharedMemSetLastEntry (SharedMemTable); // set the header as valid SharedMemTable->Header._u1.EntriesInfo.ValidHeader = 1; SharedMemCalculateChecksum (SharedMemTable); } /** Add Table to Shared Memory Table @param[in,out] SharedMemTable Shared Memory table where entry is to be added @param[in] XmlCliCommon XmlCli Common Structure @param[in] SharedMailBox Pointer to shared mailbox @param[in] MailboxAddress Shared mailbox address @retval EFI_SUCCESS Entry added successfully @retval !EFI_SUCCESS Failure**/ EFI_STATUS SharedMemAddTable ( IN OUT SHARED_MEMORY_TABLE* SharedMemTable, IN VOID *XmlCliCommon, IN SHARED_MAILBOX_TYPE* SharedMailBox, IN UINT32 MailboxAddress ) { UINT32 Signature; UINT32 Offset; UINT32 Size; UINTN index; EFI_STATUS Status; index = 0; while (SharedMailBox[ index ].Signature != SHARED_MB_LAST_SIG) { Signature = SharedMailBox[ index ].Signature; Offset = SharedMailBox[ index ].Offset; Size = SharedMailBox[ index ].Size; if (MailboxAddress < ((XML_CLI_COMMON*)XmlCliCommon)->PcieAddr) { // this is for Dram Shared Mailbox. Offset = ((XML_CLI_COMMON*)XmlCliCommon)->DramSharedMBAddr + Offset; } else { if ((Signature == CLI_REQ_SIG) || (Signature == CLI_RES_SIG)) { Offset = ((XML_CLI_COMMON*)XmlCliCommon)->DramSharedMBAddr + Offset; } } Status = SharedMemAddEntry(SharedMemTable, Signature, Offset, Size, SharedMailBox[ index ].AccessType); if (EFI_ERROR(Status)) { return (Status); } index++; } return EFI_SUCCESS; } /** Construct and Publish Dram Shared Mail Box @param[in] XmlCliCom XmlCli Common Structure @param[in] MailboxTable Pointer to where Shared mailbox table to construct @param[in] BarNumber Base Address Register Number @param[in] VenDevId Device Vendor Id @param[in] MailboxAddress Shared mailbox address @retval EFI_SUCCESS Entry added successfully @retval !EFI_SUCCESS Failure **/ VOID SharedMemConstructSharedMB ( IN VOID *XmlCliCom, IN SHARED_MAILBOX_TYPE *MailboxTable, IN UINT8 BarNumber, IN UINT32 VenDevId, IN UINT32 MailboxAddress ) { UINT8 PublishShareMB; UINT32 Data32; EFI_STATUS Status; SHARED_MEMORY_TABLE SharedMemTable; SHARED_MEMORY_TABLE *SharedMemTablePtr; XML_CLI_COMMON *XmlCliCommon; SharedMemTablePtr = (SHARED_MEMORY_TABLE*)(UINTN)MailboxAddress; XmlCliCommon = (XML_CLI_COMMON*)XmlCliCom; PublishShareMB = 0; if ((SharedMemTablePtr->Header.BiosSignature != SHARED_MB_BIOS_SIG) || (SharedMemTablePtr->Header.BiosSignature2 != SHARED_MB_BIOS_SIG)) { PublishShareMB = 1; } READ_MAILBOX(SETUP_KNOB_ADDRESS_OFF, 4, &Data32, MailboxAddress, XmlCliCommon->PcieAddr); if ((PublishShareMB == 0) && (Data32 != 0) && (Data32 != 0xAFAFAFAF)) { DEBUG ((DEBUG_INFO, "XML_CLI: DRAM Shared Mailbox Already Published, overwriting.\n")); //return; // already published. } DEBUG ((DEBUG_INFO, "XML_CLI: Constructing DRAM Shared Mailbox .\n")); ZeroMem((void*)(UINTN)MailboxAddress, 0x100); // Zero out Shared MB header SharedMemInitTable(&SharedMemTable, (UINT16)(VenDevId >> 16) , BarNumber); if (XmlCliCommon->CliGen2Enable) { SharedMemTable.Header.CliSpecVersion.MinorVersion = 0; SharedMemTable.Header.CliSpecVersion.MajorVersion = 8; SharedMemTable.Header.CliSpecVersion.ReleaseVersion = 0; } Status = SharedMemAddTable(&SharedMemTable, (VOID*)XmlCliCommon, MailboxTable, MailboxAddress); ASSERT_EFI_ERROR(Status); ReadWriteHIF(0, sizeof(SHARED_MEMORY_TABLE), &SharedMemTable, MailboxAddress, WRITE_MB); // copy shared mailbox table. if (XmlCliCommon->CliGen2Enable) { /// /// Indicate that XML CLI is enabled by setting BIT1 in target Status Flags offset /// Data32 = 1 << 1; WRITE_MAILBOX(OFF_GEN2_XML_CLI_ENABLED, 4, &Data32, MailboxAddress, XmlCliCommon->PcieAddr); Data32 = XMLCLI_GEN2_DONE_POSTCODE; } else { Data32 = XMLCLI_DONE_POSTCODE; } if ((SharedMemTable.Header.CliSpecVersion.MajorVersion >= 7) || (SharedMemTable.Header.CliSpecVersion.ReleaseVersion > 0)) { WRITE_MAILBOX(OFF_GEN2_GBT_ADDRESS, 4, &XmlCliCommon->GbtXmlAddress, MailboxAddress, XmlCliCommon->PcieAddr); } else { WRITE_MAILBOX(OFF_GBT_ADDRESS, 4, &XmlCliCommon->GbtXmlAddress, MailboxAddress, XmlCliCommon->PcieAddr); } WRITE_MAILBOX(DRAM_SHARED_MB_OFF, 4, &XmlCliCommon->DramSharedMBAddr, MailboxAddress, XmlCliCommon->PcieAddr); WRITE_MAILBOX(OFF_BIOS_KNOBS_DATA_BIN_ADDR, 4, &XmlCliCommon->BiosKnobsDataAddr, MailboxAddress, XmlCliCommon->PcieAddr); WRITE_MAILBOX(OFF_BIOS_KNOBS_DATA_BIN_SIZE, 4, &XmlCliCommon->BiosKnobsDataSize, MailboxAddress, XmlCliCommon->PcieAddr); if (XmlCliCommon->SetupKnobsAddress != 0) { WRITE_MAILBOX(OFF_POST_DONE, 1, &Data32, MailboxAddress, XmlCliCommon->PcieAddr); Data32 = 0x5E9D90B5; // [TODO] what does this value mean? WRITE_MAILBOX(OFF_SEND_KNOBS_SIG, 4, &Data32, MailboxAddress, XmlCliCommon->PcieAddr); WRITE_MAILBOX(SETUP_KNOB_ADDRESS_OFF, 4, &XmlCliCommon->SetupKnobsAddress, MailboxAddress, XmlCliCommon->PcieAddr); WRITE_MAILBOX(SETUP_KNOB_SIZE_OFF, 4, &XmlCliCommon->SetupKnobsSize, MailboxAddress, XmlCliCommon->PcieAddr); WRITE_MAILBOX(KNOB_XML_ENTRY_OFF, 4, &XmlCliCommon->KnobXmlEntryAddr, MailboxAddress, XmlCliCommon->PcieAddr); } }