mirror of
https://github.com/acidanthera/audk.git
synced 2025-07-28 16:14:04 +02:00
StandaloneMmPkg/MmIpl: load MM Core and execute MM Core in MM RAM
StandaloneMmIplPei will search the MM Core driver in all FV and relocate it to MM RAM, and enter MM Core entrypoint to run MM Core. Signed-off-by: Hongbin1 Zhang <hongbin1.zhang@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Star Zeng <star.zeng@intel.com> Cc: Jiaxin Wu <jiaxin.wu@intel.com> Cc: Wei6 Xu <wei6.xu@intel.com> Cc: Sami Mujawar <sami.mujawar@arm.com> Cc: Ard Biesheuvel <ardb+tianocore@kernel.org> Cc: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
This commit is contained in:
parent
d7e6b863a1
commit
8d764088ea
@ -8,6 +8,336 @@
|
|||||||
|
|
||||||
#include "StandaloneMmIplPei.h"
|
#include "StandaloneMmIplPei.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
Search all the available firmware volumes for MM Core driver.
|
||||||
|
|
||||||
|
@param MmFvBase Base address of FV which included MM Core driver.
|
||||||
|
@param MmFvSize Size of FV which included MM Core driver.
|
||||||
|
@param MmCoreFileName GUID of MM Core.
|
||||||
|
@param MmCoreImageAddress MM Core image address.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS The specified FFS section was returned.
|
||||||
|
@retval EFI_NOT_FOUND The specified FFS section could not be found.
|
||||||
|
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
LocateMmCoreFv (
|
||||||
|
OUT EFI_PHYSICAL_ADDRESS *MmFvBase,
|
||||||
|
OUT UINTN *MmFvSize,
|
||||||
|
OUT EFI_GUID *MmCoreFileName,
|
||||||
|
OUT VOID **MmCoreImageAddress
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
UINTN FvIndex;
|
||||||
|
EFI_PEI_FV_HANDLE VolumeHandle;
|
||||||
|
EFI_PEI_FILE_HANDLE FileHandle;
|
||||||
|
EFI_PE32_SECTION *SectionData;
|
||||||
|
EFI_FV_INFO VolumeInfo;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Search all FV
|
||||||
|
//
|
||||||
|
VolumeHandle = NULL;
|
||||||
|
for (FvIndex = 0; ; FvIndex++) {
|
||||||
|
Status = PeiServicesFfsFindNextVolume (FvIndex, &VolumeHandle);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Search MM Core FFS
|
||||||
|
//
|
||||||
|
FileHandle = NULL;
|
||||||
|
Status = PeiServicesFfsFindNextFile (EFI_FV_FILETYPE_MM_CORE_STANDALONE, VolumeHandle, &FileHandle);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT (FileHandle != NULL);
|
||||||
|
if (FileHandle != NULL) {
|
||||||
|
CopyGuid (MmCoreFileName, &((EFI_FFS_FILE_HEADER *)FileHandle)->Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Search Section
|
||||||
|
//
|
||||||
|
Status = PeiServicesFfsFindSectionData (EFI_SECTION_PE32, FileHandle, MmCoreImageAddress);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get MM Core section data.
|
||||||
|
//
|
||||||
|
SectionData = (EFI_PE32_SECTION *)((UINT8 *)*MmCoreImageAddress - sizeof (EFI_PE32_SECTION));
|
||||||
|
ASSERT (SectionData->Type == EFI_SECTION_PE32);
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is the FV that contains MM Core.
|
||||||
|
//
|
||||||
|
Status = PeiServicesFfsGetVolumeInfo (VolumeHandle, &VolumeInfo);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
*MmFvBase = (EFI_PHYSICAL_ADDRESS)(UINTN)VolumeInfo.FvStart;
|
||||||
|
*MmFvSize = VolumeInfo.FvSize;
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
} else {
|
||||||
|
return EFI_NOT_FOUND;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EFI_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Find largest unallocated MMRAM in current MMRAM descriptor block
|
||||||
|
|
||||||
|
@param[in, out] LagestMmramRangeIndex Lagest mmram range index.
|
||||||
|
@param[in] CurrentBlock Current MMRAM descriptor block.
|
||||||
|
|
||||||
|
**/
|
||||||
|
VOID
|
||||||
|
FindLargestMmramRange (
|
||||||
|
IN OUT UINTN *LagestMmramRangeIndex,
|
||||||
|
IN EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *CurrentBlock
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UINTN Index;
|
||||||
|
UINT64 MaxSize;
|
||||||
|
BOOLEAN Found;
|
||||||
|
EFI_MMRAM_DESCRIPTOR *MmramRanges;
|
||||||
|
|
||||||
|
MmramRanges = CurrentBlock->Descriptor;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find largest Mmram range.
|
||||||
|
//
|
||||||
|
Found = FALSE;
|
||||||
|
for (Index = 0, MaxSize = SIZE_256KB - EFI_PAGE_SIZE; Index < CurrentBlock->NumberOfMmReservedRegions; Index++) {
|
||||||
|
//
|
||||||
|
// Skip any MMRAM region that is already allocated, needs testing, or needs ECC initialization
|
||||||
|
//
|
||||||
|
if ((MmramRanges[Index].RegionState & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MmramRanges[Index].CpuStart >= BASE_1MB) {
|
||||||
|
if ((MmramRanges[Index].CpuStart + MmramRanges[Index].PhysicalSize) <= BASE_4GB) {
|
||||||
|
if (MmramRanges[Index].PhysicalSize >= MaxSize) {
|
||||||
|
Found = TRUE;
|
||||||
|
*LagestMmramRangeIndex = Index;
|
||||||
|
MaxSize = MmramRanges[Index].PhysicalSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Found == FALSE) {
|
||||||
|
DEBUG ((DEBUG_ERROR, "Not found largest unlocated MMRAM\n"));
|
||||||
|
ASSERT (FALSE);
|
||||||
|
CpuDeadLoop ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Allocate available MMRAM for MM core image.
|
||||||
|
|
||||||
|
@param[in] Pages Page count of MM core image.
|
||||||
|
@param[out] NewBlock Pointer of new mmram block HOB.
|
||||||
|
|
||||||
|
@return EFI_PHYSICAL_ADDRESS Address for MM core image to be loaded in MMRAM.
|
||||||
|
**/
|
||||||
|
EFI_PHYSICAL_ADDRESS
|
||||||
|
MmIplAllocateMmramPage (
|
||||||
|
IN UINTN Pages,
|
||||||
|
OUT EFI_MMRAM_HOB_DESCRIPTOR_BLOCK **NewBlock
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UINTN LagestMmramRangeIndex;
|
||||||
|
UINT32 FullMmramRangeCount;
|
||||||
|
EFI_HOB_GUID_TYPE *MmramInfoHob;
|
||||||
|
EFI_MMRAM_DESCRIPTOR *Largest;
|
||||||
|
EFI_MMRAM_DESCRIPTOR *Allocated;
|
||||||
|
EFI_MMRAM_DESCRIPTOR *FullMmramRanges;
|
||||||
|
EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *CurrentBlock;
|
||||||
|
EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *NewDescriptorBlock;
|
||||||
|
|
||||||
|
MmramInfoHob = GetFirstGuidHob (&gEfiSmmSmramMemoryGuid);
|
||||||
|
ASSERT (MmramInfoHob != NULL);
|
||||||
|
if (MmramInfoHob == NULL) {
|
||||||
|
DEBUG ((DEBUG_WARN, "SmramMemoryReserve HOB not found\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentBlock = (EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *)(GET_GUID_HOB_DATA (MmramInfoHob));
|
||||||
|
|
||||||
|
//
|
||||||
|
// 1. Find largest unallocated MMRAM region
|
||||||
|
//
|
||||||
|
FindLargestMmramRange (&LagestMmramRangeIndex, CurrentBlock);
|
||||||
|
ASSERT (LagestMmramRangeIndex < CurrentBlock->NumberOfMmReservedRegions);
|
||||||
|
|
||||||
|
//
|
||||||
|
// 2. Split the largest region and mark the allocated region as ALLOCATED
|
||||||
|
//
|
||||||
|
FullMmramRangeCount = CurrentBlock->NumberOfMmReservedRegions + 1;
|
||||||
|
NewDescriptorBlock = (EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *)BuildGuidHob (
|
||||||
|
&gEfiSmmSmramMemoryGuid,
|
||||||
|
sizeof (EFI_MMRAM_HOB_DESCRIPTOR_BLOCK) + ((FullMmramRangeCount - 1) * sizeof (EFI_MMRAM_DESCRIPTOR))
|
||||||
|
);
|
||||||
|
ASSERT (NewDescriptorBlock != NULL);
|
||||||
|
|
||||||
|
NewDescriptorBlock->NumberOfMmReservedRegions = FullMmramRangeCount;
|
||||||
|
FullMmramRanges = NewDescriptorBlock->Descriptor;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get current MMRAM descriptors and fill to the full MMRAM ranges
|
||||||
|
//
|
||||||
|
CopyMem (NewDescriptorBlock->Descriptor, CurrentBlock->Descriptor, CurrentBlock->NumberOfMmReservedRegions * sizeof (EFI_MMRAM_DESCRIPTOR));
|
||||||
|
|
||||||
|
Largest = &FullMmramRanges[LagestMmramRangeIndex];
|
||||||
|
ASSERT ((Largest->PhysicalSize & EFI_PAGE_MASK) == 0);
|
||||||
|
ASSERT (Largest->PhysicalSize > EFI_PAGES_TO_SIZE (Pages));
|
||||||
|
|
||||||
|
Allocated = &NewDescriptorBlock->Descriptor[NewDescriptorBlock->NumberOfMmReservedRegions - 1];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allocate MMRAM
|
||||||
|
//
|
||||||
|
Largest->PhysicalSize -= EFI_PAGES_TO_SIZE (Pages);
|
||||||
|
Allocated->CpuStart = Largest->CpuStart + Largest->PhysicalSize;
|
||||||
|
Allocated->PhysicalStart = Largest->PhysicalStart + Largest->PhysicalSize;
|
||||||
|
Allocated->RegionState = Largest->RegionState | EFI_ALLOCATED;
|
||||||
|
Allocated->PhysicalSize = EFI_PAGES_TO_SIZE (Pages);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Scrub old one
|
||||||
|
//
|
||||||
|
ZeroMem (&MmramInfoHob->Name, sizeof (MmramInfoHob->Name));
|
||||||
|
|
||||||
|
//
|
||||||
|
// New MMRAM descriptor block
|
||||||
|
//
|
||||||
|
*NewBlock = NewDescriptorBlock;
|
||||||
|
|
||||||
|
return Allocated->CpuStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Load the MM Core image into MMRAM and executes the MM Core from MMRAM.
|
||||||
|
|
||||||
|
@param[in] MmCommBuffer MM communicate buffer
|
||||||
|
|
||||||
|
@return EFI_STATUS Execute MM core successfully.
|
||||||
|
Other Execute MM core failed.
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
ExecuteMmCoreFromMmram (
|
||||||
|
IN MM_COMM_BUFFER *MmCommBuffer
|
||||||
|
)
|
||||||
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
|
UINTN PageCount;
|
||||||
|
VOID *MmHobList;
|
||||||
|
EFI_GUID MmCoreFileName;
|
||||||
|
UINTN MmFvSize;
|
||||||
|
EFI_PHYSICAL_ADDRESS MmFvBase;
|
||||||
|
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
|
||||||
|
STANDALONE_MM_FOUNDATION_ENTRY_POINT Entry;
|
||||||
|
EFI_MMRAM_HOB_DESCRIPTOR_BLOCK *Block;
|
||||||
|
|
||||||
|
MmFvBase = 0;
|
||||||
|
MmFvSize = 0;
|
||||||
|
//
|
||||||
|
// Search all Firmware Volumes for a PE/COFF image in a file of type MM_CORE_STANDALONE.
|
||||||
|
//
|
||||||
|
Status = LocateMmCoreFv (&MmFvBase, &MmFvSize, &MmCoreFileName, &ImageContext.Handle);
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Unblock the MM FV range to be accessible from inside MM
|
||||||
|
//
|
||||||
|
if ((MmFvBase != 0) && (MmFvSize != 0)) {
|
||||||
|
Status = MmUnblockMemoryRequest (MmFvBase, EFI_SIZE_TO_PAGES (MmFvSize));
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize ImageContext
|
||||||
|
//
|
||||||
|
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get information about the image being loaded
|
||||||
|
//
|
||||||
|
Status = PeCoffLoaderGetImageInfo (&ImageContext);
|
||||||
|
if (EFI_ERROR (Status)) {
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allocate memory for the image being loaded from unallocated mmram range
|
||||||
|
//
|
||||||
|
ImageContext.ImageAddress = MmIplAllocateMmramPage (PageCount, &Block);
|
||||||
|
if (ImageContext.ImageAddress == 0) {
|
||||||
|
return EFI_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Align buffer on section boundary
|
||||||
|
//
|
||||||
|
ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
|
||||||
|
ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Print debug message showing MM Core load address.
|
||||||
|
//
|
||||||
|
DEBUG ((DEBUG_INFO, "StandaloneMM IPL loading MM Core at MMRAM address %p\n", (VOID *)(UINTN)ImageContext.ImageAddress));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Load the image to our new buffer
|
||||||
|
//
|
||||||
|
Status = PeCoffLoaderLoadImage (&ImageContext);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
//
|
||||||
|
// Relocate the image in our new buffer
|
||||||
|
//
|
||||||
|
Status = PeCoffLoaderRelocateImage (&ImageContext);
|
||||||
|
if (!EFI_ERROR (Status)) {
|
||||||
|
DEBUG ((DEBUG_INFO, "MmCoreImageBase - 0x%016lx\n", ImageContext.ImageAddress));
|
||||||
|
DEBUG ((DEBUG_INFO, "MmCoreImageSize - 0x%016lx\n", ImageContext.ImageSize));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Flush the instruction cache so the image data are written before we execute it
|
||||||
|
//
|
||||||
|
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get HOB list for Standalone MM Core.
|
||||||
|
//
|
||||||
|
MmHobList = GetHobList ();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Print debug message showing Standalone MM Core entry point address.
|
||||||
|
//
|
||||||
|
DEBUG ((DEBUG_INFO, "StandaloneMM IPL calling Standalone MM Core at MMRAM address - 0x%016lx\n", ImageContext.EntryPoint));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Execute image
|
||||||
|
//
|
||||||
|
Entry = (STANDALONE_MM_FOUNDATION_ENTRY_POINT)(UINTN)ImageContext.EntryPoint;
|
||||||
|
Status = Entry (MmHobList);
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Build communication buffer HOB.
|
Build communication buffer HOB.
|
||||||
|
|
||||||
@ -86,6 +416,7 @@ StandaloneMmIplPeiEntry (
|
|||||||
IN CONST EFI_PEI_SERVICES **PeiServices
|
IN CONST EFI_PEI_SERVICES **PeiServices
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
EFI_STATUS Status;
|
||||||
MM_COMM_BUFFER *MmCommBuffer;
|
MM_COMM_BUFFER *MmCommBuffer;
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -94,5 +425,11 @@ StandaloneMmIplPeiEntry (
|
|||||||
MmCommBuffer = MmIplBuildCommBufferHob ();
|
MmCommBuffer = MmIplBuildCommBufferHob ();
|
||||||
ASSERT (MmCommBuffer != NULL);
|
ASSERT (MmCommBuffer != NULL);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Locate and execute Mm Core to dispatch MM drivers.
|
||||||
|
//
|
||||||
|
Status = ExecuteMmCoreFromMmram (MmCommBuffer);
|
||||||
|
ASSERT_EFI_ERROR (Status);
|
||||||
|
|
||||||
return EFI_SUCCESS;
|
return EFI_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,16 @@
|
|||||||
#ifndef STANDALONE_MM_IPL_PEI_H_
|
#ifndef STANDALONE_MM_IPL_PEI_H_
|
||||||
#define STANDALONE_MM_IPL_PEI_H_
|
#define STANDALONE_MM_IPL_PEI_H_
|
||||||
|
|
||||||
|
#include <StandaloneMm.h>
|
||||||
#include <Guid/MmCommBuffer.h>
|
#include <Guid/MmCommBuffer.h>
|
||||||
|
#include <Guid/MmramMemoryReserve.h>
|
||||||
#include <Library/HobLib.h>
|
#include <Library/HobLib.h>
|
||||||
#include <Library/DebugLib.h>
|
#include <Library/DebugLib.h>
|
||||||
#include <Library/MemoryAllocationLib.h>
|
#include <Library/MemoryAllocationLib.h>
|
||||||
|
#include <Library/PeiServicesLib.h>
|
||||||
#include <Library/MmUnblockMemoryLib.h>
|
#include <Library/MmUnblockMemoryLib.h>
|
||||||
|
#include <Library/BaseMemoryLib.h>
|
||||||
|
#include <Library/PeCoffLib.h>
|
||||||
|
#include <Library/CacheMaintenanceLib.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -33,13 +33,18 @@
|
|||||||
|
|
||||||
[LibraryClasses]
|
[LibraryClasses]
|
||||||
PeimEntryPoint
|
PeimEntryPoint
|
||||||
|
PeiServicesLib
|
||||||
DebugLib
|
DebugLib
|
||||||
HobLib
|
HobLib
|
||||||
MemoryAllocationLib
|
MemoryAllocationLib
|
||||||
MmUnblockMemoryLib
|
MmUnblockMemoryLib
|
||||||
|
BaseMemoryLib
|
||||||
|
PeCoffLib
|
||||||
|
CacheMaintenanceLib
|
||||||
|
|
||||||
[Guids]
|
[Guids]
|
||||||
gMmCommBufferHobGuid
|
gMmCommBufferHobGuid
|
||||||
|
gEfiSmmSmramMemoryGuid
|
||||||
|
|
||||||
[Ppis]
|
[Ppis]
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@
|
|||||||
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
|
HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
|
||||||
PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
|
PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
|
||||||
MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
|
MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
|
||||||
|
PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
|
||||||
|
PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
|
||||||
MmUnblockMemoryLib|MdePkg/Library/MmUnblockMemoryLib/MmUnblockMemoryLibNull.inf
|
MmUnblockMemoryLib|MdePkg/Library/MmUnblockMemoryLib/MmUnblockMemoryLibNull.inf
|
||||||
|
|
||||||
[LibraryClasses.AARCH64, LibraryClasses.ARM]
|
[LibraryClasses.AARCH64, LibraryClasses.ARM]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user