blob: 0e2690c73687a87cc213a1e06d3a541d06eb5253 [file] [log] [blame]
/** @file
Memory Detection for Virtual Machines.
Copyright (c) 2021, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
Module Name:
MemDetect.c
**/
//
// The package level header files this module uses
//
#include <PiPei.h>
//
// The Library classes this module consumes
//
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/IoLib.h>
#include <Library/PcdLib.h>
#include <Library/PeimEntryPoint.h>
#include <Library/ResourcePublicationLib.h>
#include <Library/BaseRiscVSbiLib.h>
#include <Register/RiscV64/RiscVEncoding.h>
#include <Library/PrePiLib.h>
#include <libfdt.h>
#include <Guid/FdtHob.h>
VOID
BuildMemoryTypeInformationHob (
VOID
);
/**
Create memory range resource HOB using the memory base
address and size.
@param MemoryBase Memory range base address.
@param MemorySize Memory range size.
**/
STATIC
VOID
AddMemoryBaseSizeHob (
IN EFI_PHYSICAL_ADDRESS MemoryBase,
IN UINT64 MemorySize
)
{
BuildResourceDescriptorHob (
EFI_RESOURCE_SYSTEM_MEMORY,
EFI_RESOURCE_ATTRIBUTE_PRESENT |
EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |
EFI_RESOURCE_ATTRIBUTE_TESTED,
MemoryBase,
MemorySize
);
}
/**
Create memory range resource HOB using memory base
address and top address of the memory range.
@param MemoryBase Memory range base address.
@param MemoryLimit Memory range size.
**/
STATIC
VOID
AddMemoryRangeHob (
IN EFI_PHYSICAL_ADDRESS MemoryBase,
IN EFI_PHYSICAL_ADDRESS MemoryLimit
)
{
AddMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));
}
/**
Configure MMU
**/
STATIC
VOID
InitMmu (
)
{
//
// Set supervisor translation mode to Bare mode
//
RiscVSetSupervisorAddressTranslationRegister ((UINT64)SATP_MODE_OFF << 60);
DEBUG ((DEBUG_INFO, "%a: Set Supervisor address mode to bare-metal mode.\n", __func__));
}
/**
Publish system RAM and reserve memory regions.
**/
STATIC
VOID
InitializeRamRegions (
IN EFI_PHYSICAL_ADDRESS SystemMemoryBase,
IN UINT64 SystemMemorySize
)
{
AddMemoryRangeHob (
SystemMemoryBase,
SystemMemoryBase + SystemMemorySize
);
}
/** Get the number of cells for a given property
@param[in] Fdt Pointer to Device Tree (DTB)
@param[in] Node Node
@param[in] Name Name of the property
@return Number of cells.
**/
STATIC
INT32
GetNumCells (
IN VOID *Fdt,
IN INT32 Node,
IN CONST CHAR8 *Name
)
{
CONST INT32 *Prop;
INT32 Len;
UINT32 Val;
Prop = fdt_getprop (Fdt, Node, Name, &Len);
if (Prop == NULL) {
return Len;
}
if (Len != sizeof (*Prop)) {
return -FDT_ERR_BADNCELLS;
}
Val = fdt32_to_cpu (*Prop);
if (Val > FDT_MAX_NCELLS) {
return -FDT_ERR_BADNCELLS;
}
return (INT32)Val;
}
/** Mark reserved memory ranges in the EFI memory map
The M-mode firmware ranges should not be used by the
EDK2/OS. These ranges are passed via device tree using reserved
memory nodes. Parse the DT and mark those ranges as of
type EfiReservedMemoryType.
NOTE: Device Tree spec section 3.5.4 says reserved memory regions
without no-map property should be installed as EfiBootServicesData.
As per UEFI spec, memory of type EfiBootServicesData can be used
by the OS after ExitBootServices().
This is not an issue for DT since OS can parse the DT also along
with EFI memory map and avoid using these ranges. But with ACPI,
there is no such mechanisms possible.
Since EDK2 needs to support both DT and ACPI, we are deviating
from the DT spec and marking all reserved memory ranges as
EfiReservedMemoryType itself irrespective of no-map.
@param FdtPointer Pointer to FDT
**/
STATIC
VOID
AddReservedMemoryMap (
IN VOID *FdtPointer
)
{
CONST INT32 *RegProp;
INT32 Node;
INT32 SubNode;
INT32 Len;
EFI_PHYSICAL_ADDRESS Addr;
UINT64 Size;
INTN NumRsv, i;
INT32 NumAddrCells, NumSizeCells;
NumRsv = fdt_num_mem_rsv (FdtPointer);
/* Look for an existing entry and add it to the efi mem map. */
for (i = 0; i < NumRsv; i++) {
if (fdt_get_mem_rsv (FdtPointer, i, &Addr, &Size) != 0) {
continue;
}
BuildMemoryAllocationHob (
Addr,
Size,
EfiReservedMemoryType
);
}
/* process reserved-memory */
Node = fdt_subnode_offset (FdtPointer, 0, "reserved-memory");
if (Node >= 0) {
NumAddrCells = GetNumCells (FdtPointer, Node, "#address-cells");
if (NumAddrCells <= 0) {
return;
}
NumSizeCells = GetNumCells (FdtPointer, Node, "#size-cells");
if (NumSizeCells <= 0) {
return;
}
fdt_for_each_subnode (SubNode, FdtPointer, Node) {
RegProp = fdt_getprop (FdtPointer, SubNode, "reg", &Len);
if ((RegProp != 0) && (Len == ((NumAddrCells + NumSizeCells) * sizeof (INT32)))) {
Addr = fdt32_to_cpu (RegProp[0]);
if (NumAddrCells > 1) {
Addr = (Addr << 32) | fdt32_to_cpu (RegProp[1]);
}
RegProp += NumAddrCells;
Size = fdt32_to_cpu (RegProp[0]);
if (NumSizeCells > 1) {
Size = (Size << 32) | fdt32_to_cpu (RegProp[1]);
}
DEBUG ((
DEBUG_INFO,
"%a: Adding Reserved Memory Addr = 0x%llx, Size = 0x%llx\n",
__func__,
Addr,
Size
));
BuildMemoryAllocationHob (
Addr,
Size,
EfiReservedMemoryType
);
}
}
}
}
/**
Initialize memory hob based on the DTB information.
@return EFI_SUCCESS The memory hob added successfully.
**/
EFI_STATUS
MemoryPeimInitialization (
VOID
)
{
EFI_RISCV_FIRMWARE_CONTEXT *FirmwareContext;
CONST UINT64 *RegProp;
CONST CHAR8 *Type;
UINT64 CurBase, CurSize;
INT32 Node, Prev;
INT32 Len;
VOID *FdtPointer;
FirmwareContext = NULL;
GetFirmwareContextPointer (&FirmwareContext);
if (FirmwareContext == NULL) {
DEBUG ((DEBUG_ERROR, "%a: Firmware Context is NULL\n", __func__));
return EFI_UNSUPPORTED;
}
FdtPointer = (VOID *)FirmwareContext->FlattenedDeviceTree;
if (FdtPointer == NULL) {
DEBUG ((DEBUG_ERROR, "%a: Invalid FDT pointer\n", __func__));
return EFI_UNSUPPORTED;
}
// Look for the lowest memory node
for (Prev = 0; ; Prev = Node) {
Node = fdt_next_node (FdtPointer, Prev, NULL);
if (Node < 0) {
break;
}
// Check for memory node
Type = fdt_getprop (FdtPointer, Node, "device_type", &Len);
if (Type && (AsciiStrnCmp (Type, "memory", Len) == 0)) {
// Get the 'reg' property of this node. For now, we will assume
// two 8 byte quantities for base and size, respectively.
RegProp = fdt_getprop (FdtPointer, Node, "reg", &Len);
if ((RegProp != 0) && (Len == (2 * sizeof (UINT64)))) {
CurBase = fdt64_to_cpu (ReadUnaligned64 (RegProp));
CurSize = fdt64_to_cpu (ReadUnaligned64 (RegProp + 1));
DEBUG ((
DEBUG_INFO,
"%a: System RAM @ 0x%lx - 0x%lx\n",
__func__,
CurBase,
CurBase + CurSize - 1
));
InitializeRamRegions (
CurBase,
CurSize
);
} else {
DEBUG ((
DEBUG_ERROR,
"%a: Failed to parse FDT memory node\n",
__func__
));
}
}
}
AddReservedMemoryMap (FdtPointer);
InitMmu ();
BuildMemoryTypeInformationHob ();
return EFI_SUCCESS;
}